I'm trying out AngularJS in combination with NET Core. Currently trying to parse a "string username", "string password" and "bool rememberMe", but both strings are null no matter what I do. I've even tried parsing a constant value as seen below, the parameter "test", but even that doesn't work. I've confirmed in Fiddler that the script is in fact parsing the value, so the controller basically just can't "fetch it".
Below is my AngularJS loginController code.
var login = angular.module('login', []);
login.controller('LoginController',
[
'$scope', '$http', '$window', function ($scope, $http, $window) {
$scope.submit = function () {
var data = { test: ':D' };
$http({
method: 'POST',
url: '/admin',
data: data
}).then(function success(response) {
alert(response.data.test);
});
}
}
]);
Below is the code for my ActionResult meant to fetch the value being posted to it.
[HttpPost]
public IActionResult Index(string test)
{
return Json(new
{
test,
bla = "BLA"
});
//if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
//{
// if (UserSystem.Login(username, password, rememberMe, HttpContext))
// {
// HttpContext.Response.StatusCode = (int)HttpStatusCode.Accepted;
// return Json(new
// {
// returnUrl = "/admin/dashboard"
// });
// }
//}
//HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
//return Json(new
//{
// error = "Username and/or password do not match"
//});
}
I've also tried specifying the Content-Type, but also without luck. Either way, in Fiddler the request always seems to per default be application/json.
I hope someone can help me. Thanks!
The solution was to request an object in the controller, rather than "string username", "string password" and "bool rememberMe".
The controller now looks like this:
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
public bool RememberMe { get; set; }
}
[HttpPost]
public IActionResult Index([FromBody]LoginModel model)
{
if (!string.IsNullOrEmpty(model.Username) && !string.IsNullOrEmpty(model.Password))
{
if (UserSystem.Login(model.Username, model.Password, model.RememberMe, HttpContext))
{
HttpContext.Response.StatusCode = (int)HttpStatusCode.Accepted;
return Json(new
{
returnUrl = "/admin/dashboard"
});
}
}
HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return Json(new
{
error = "Username and/or password do not match"
});
}
And the AngularJS controller now looks like this:
var login = angular.module('login', []);
login.controller('LoginController',
[
'$scope', '$http', '$window', function ($scope, $http, $window) {
$scope.submit = function () {
var data = {
username: $scope.username,
password: $scope.password,
rememberMe: $scope.rememberMe
};
$http({
method: 'POST',
url: '/admin',
data: data
}).then(function success(response) {
$window.location.href = response.data.returnUrl;
}, function fail(response) {
swal('Error!', response.data.error, 'error');
});
}
}
]);
Related
const [payload, setPayload] = useState({
Email: null,
Password: null
});
const handleSubmit = async (e) => {
e.preventDefault();
var json = JSON.stringify(payload);
try {
const { data } = await axios.post(
"https://localhost:5001/api/User/Login",
{
json
}
);
console.log(data);
ctxDispatch({ type: "USER_LOGIN", payload: data });
localStorage.setItem("userInfo", JSON.stringify(data));
toast.success("Login Successful");
navigate("/");
} catch (err) {
toast.error(getError(err));
}
};
[AllowAnonymous]
[HttpPost("Login")]
public async Task<ActionResult<UserDTO>> Login([FromBody] LoginDTO loginDTO)
{
//LoginDTO loginDTO = Newtonsoft.Json.JsonConvert.DeserializeObject<LoginDTO>(input);
var pwd = Encrypt.ConvertToEncrypt(loginDTO.Password);
User currentUser = await _context.user.FirstOrDefaultAsync(user =>
user.Email.ToLower() == loginDTO.Email.ToLower()
&&`enter code here`
user.Password == pwd);
}
public class LoginDTO
{
public string Email { get; set; }
public string Password { get; set; }
}
When I post this string to my backend it attempts to parse my string but fails at the first char {. When i send this string wrapped in quotes through swagger it passes with no issues. When i send this string through postman with no quotes wrapped around the string I get the same exact error as i get when posting through my frontend.
fix the action input parameters, you don't need to deserialize it , it will be deserialized by MVC
public async Task< ....([FromBody] LoginDTO loginDto)
and since you are sending a json string, add content type to axios
axios.post('..your url', json, {
headers: {
// Overwrite Axios's automatically set Content-Type
'Content-Type': 'application/json'
}
});
I have the follow Ajax call that points to a controller function:
<script type="text/javascript">
$(document).ready(function () {
$('#AddNote').click(function () {
$('#AddNote').addClass("disabled");
var txtNote = document.getElementById('note');
var result = document.getElementById('result');
result.innerText = "Adding note...";
$.ajax({
url: "#Url.Action("AddNoteAsync", "Leads")",
type: "POST",
data: { leadId: #Model.Id, note: txtNote.value },
async: true,
success: function (data) {
// removed
},
});
});
});
</script>
When I click the AddNote button I see the "Adding note..." message display and then nothing else happens. When I check the console in chrome, it reads:
:44309/Leads/AddNoteAsync:1 - Failed to load resource: the server responded with a status of 400 ()
So I know 400 means bad request but I'm not sure why it's happening. I've tried:
Added quotes to the "leadId" and "note" field in data - made no difference.
Added alert boxes to show the value of #Model.Id and txtNote.value before the AJAX call to verify they are correct - they are.
Put a breakpoint in the AddNoteAsync function in my controller - it's never hit
Hard coded the url field to /Leads/AddNoteAsync - made no difference
Since the controller function is never being hit I'm assuming something is wrong with the &.ajax part but I cannot figure out what.
Edit: The controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddNoteAsync(int? leadId, string note)
{
ViewData["AddedNote"] = false;
if (leadId == null)
{
return RedirectToAction("Index", new { initials = User.Identity.Name });
}
var lead = await _context.Leads.FirstOrDefaultAsync(m => m.Id == leadId);
if (lead == null)
{
return RedirectToAction("Index", new { initials = User.Identity.Name });
}
var ownsLead = await LeadBelongsToCurrentUser(leadId.Value, User.Identity.Name);
if (!ownsLead)
{
return RedirectToAction("Index", new { initials = User.Identity.Name });
}
_context.LeadNotes.Add(new LeadNoteModel()
{
LeadId = leadId.Value,
Note = note,
NoteDate = DateTime.Now
});
await _context.SaveChangesAsync();
ViewData["AddedNote"] = true;
return Content("Success");
}
You should accept parameters as Model while making POST request(Recommended). Proposed Model will be -
public class NoteModel
{
public int? leadId { get; set; }
public string note { get; set; }
}
and Action can be -
public async Task<IActionResult> AddNoteAsync(NoteModel model)
{
}
Also Jquery can be -
<script type="text/javascript">
$(document).ready(function () {
$('#AddNote').click(function () {
$('#AddNote').addClass("disabled");
var txtNote = document.getElementById('note');
var result = document.getElementById('result');
var postData = { leadId: #Model.Id, note: txtNote.value };
result.innerText = "Adding note...";
$.ajax({
url: "#Url.Action("AddNoteAsync", "Leads")",
type: "POST",
data: JSON.stringify(postData),
async: true,
success: function (data) {
// removed
},
});
});
});
Fixed this. I was missing this from my AJAX request:
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="f"]').val());
},
And this from my Startup.cs:
services.AddAntiforgery(options =>
{
options.FormFieldName = "f";
options.HeaderName = "XSRF-TOKEN";
});
This script is supposed to send a ProductId to the home controller's Delete-method, and the controller should make the appropriate Remove-operation:
$('[name="DeleteItem"]').click(function (e) {
$.ajax({
type: "DELETE",
url: "#Url.Action('Delete','Home')",
data: { id: $('DeleteItem#data-id').val() },
success: function () {
alert("success!");
window.location.replace("#Url.Action('Index', 'Home')");
},
error: function (data) {
alert("Error: " + data.id);
}
});
});
This is the form:
<form asp-action="Update">
#foreach (var item in Model.ShoppingCartItems)
{
#item.ProductTitle
<input asp-for="#item.Quantity" />
<button name="DeleteItem" data-id="#item.ProductId">DELETE</button>
}
<button type="submit">Update quantity</button>
</form>
This is the controller's Delete-method (I don't have the ShoppingCartId, so I'm getting it based on SessionId, which is stored in the ShoppingCarts-table):
[HttpDelete]
//[ValidateAntiForgeryToken] // <-- Do I need this in this case?
public async Task<IActionResult> Delete(
[Bind("ShoppingCartItemProductId")]
ViewModelAddToCart model)
{
// Initialize session to enable SessionId
HttpContext.Session.SetString("_Name", "MyStore");
string SessionId = HttpContext.Session.Id;
var ShoppingCart = new ShoppingCart()
{
SessionId = SessionId
};
var ShoppingCartItem = new ShoppingCartItem()
{
ProductId = model.ShoppingCartItemProductId,
};
if (ModelState.IsValid)
{
// Find ShoppingCart containing current SessionId.
var cartInfo =
(from Cart in _context.ShoppingCarts
where Cart.SessionId == SessionId
select new { TempId = Cart.Id })
.SingleOrDefault();
if (cartInfo != null)
{
ShoppingCartItem.ShoppingCartId = cartInfo.TempId;
}
// Find ShoppingCartItem containing current ProductId:
var cartItemInfo =
(from CartItem in _context.ShoppingCartItems
where (CartItem.ShoppingCartId == ShoppingCartItem.ShoppingCartId &&
CartItem.ProductId == model.ShoppingCartItemProductId)
select new { TempId = CartItem.Id })
.FirstOrDefault();
if (cartItemInfo != null)
{
// Delete ShoppingCartItem
ShoppingCartItem.Id = cartItemInfo.TempId;
_context.ShoppingCartItems.Remove(ShoppingCartItem);
}
await _context.SaveChangesAsync();
return RedirectToAction("Index", "Home");
}
else
{
return View("Index", "Home");
}
}
Edit I have made some changes to my code, and now I receive "Error: undefined" in an alert. That is because the error: in the ajax is triggered, and the data-object is not defined. Why is that? And a second question is what is the controller supposed to return? As I understand, not a RedirectToAction.
what is "deleteitem"
you should have some id or class for the button in your case class should be easy
<button name="DeleteItem" class = "deleteitemevent" data-id="#item.ProductId">DELETE</button>
$(".deleteitemevent").click(function (e) {
}
[HttpPost]
[ValidateAntiForgeryToken]
//^^yes you should for any post... but since you insist on
//doing ajax calls...
//you will have to research how to build this up... from JS and inject with the ajax call..
public async Task<IActionResult> Delete(
[Bind("ShoppingCartItemProductId")]
ViewModelAddToCart model)
{
//...
}
$('[name="DeleteItem"]').click(function (e) {
var dataid = $(this).attr('data-id'); // because name was used for control not id
$.ajax({
type: "POST",
url: "#Url.Action('Delete','Home')",
data: { id: dataid },
success: function () {
alert("success!");
window.location.replace("#Url.Action('Index', 'Home')");
},
error: function (data) {
alert("Error: " + data.id);
}
});
});
I think you have a long way to go... There are easier ways of doing this without needing ajax calls...
I have this C# method:
C#
[HttpPost]
[AllowAnonymous]
public JsonResult PostOnCRM(string textBoxFirstName, string textBoxCountry, string textBoxLastName, string textBoxEmail, string textBoxTitle, string textBoxTelephone, string textBoxCompany, string textBoxWebsite, string textAreaNote, string checkBoxUpdates)
{
bool isValidEmail = Regex.IsMatch(textBoxEmail,
#"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))" +
#"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
if (!isValidEmail)
throw new Exception("E-mail is not a valid one");
LeadInformation lead = new LeadInformation()
{
Subject = "Web site",
FirstName = textBoxFirstName,
LastName = textBoxLastName,
JobTitle = textBoxTitle,
Address1_Country = textBoxCountry,
EmailAddress1 = textBoxEmail,
MobilePhone = textBoxTelephone,
WebsiteUrl = textBoxWebsite,
Description = textAreaNote,
DoNotEmail = checkBoxUpdates.Contains("Yes") ? true : false
};
//Some method that works well go here
return Json(new { success = true, responseText = "Your message successfuly sent!" }, JsonRequestBehavior.AllowGet);
}
And in my view, this Ajax call
Ajax
$("#formContact").submit(function (evt) {
evt.preventDefault();
var formdata = $('form').serialize();
$.ajax({
type: 'POST',
dataType: "json",
cache: false,
url: 'http://localhost:59289/Lead/PostOnCRM',
data: formdata,
success: function (response) {
alert('success!');
},
error: function (response) {
alert('Error! try again later');
}
});
});
The method performs perfectly. Does the insert in the database but when it returns to the ajax method it always lands on the 'error' and does not return the response I've sent. What can it be?
Sorry for taking the parameters in this way (especially bool value thing) and for not use a Bind or anything, but this is not relevant to the question
I asked it at Portuguese StackOverflow but without a good answer.
Here, a print of my browser
https://postimg.cc/image/e86q3hcol/
First of all, you should never use Regular Expression to validate email address. Look at this.
Second, you want to use Model in ASP.Net MVC, instead of declaring multiple parameters in action method.
Please do it property. If you still have question, please come back and ask again. Here is a working example -
View
#model UserModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
#using (Html.BeginForm())
{
#Html.TextBoxFor(model => model.Email)
<button id="btnSubmit" type="button">Submit</button>
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(function () {
$("#btnSubmit").click(function () {
var data = {
Email: $("#Email").val()
};
$.ajax({
type: 'POST',
dataType: "json",
contentType: "application/json; charset=utf-8",
url: "#Url.Action("PostOnCRM", "Lead")",
data: JSON.stringify(data),
success: function (response) {
alert('success!');
},
error: function (response) {
alert('Error! try again later');
}
});
});
});
</script>
</body>
</html>
View
public class UserModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Country { get; set; }
public string Email { get; set; }
}
Controller
public class LeadController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
[AllowAnonymous]
public JsonResult PostOnCRM(UserModel model)
{
bool isValidEmail = IsValidEmail(model.Email);
if (!isValidEmail)
throw new Exception("E-mail is not a valid one");
// ...
return Json(new { success = true, responseText = "Your message successfuly sent!" });
}
public static bool IsValidEmail(string email)
{
if (String.IsNullOrWhiteSpace(email))
return false;
try
{
email = new MailAddress(email).Address;
return true;
}
catch (FormatException)
{
return false;
}
}
}
My boss resolved my problem in 5 minutes (i tried by 2 days) with
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
On web.config :|
I don't know why. Some one can explain me?
I Forgot to say but it's a cross origin request :|
Grettings friends. So i have been researching this site for a looong time and i have not gotten a satisfactory answer to my question.
This is my Controller:
public ActionResult EliminarLibro(string bookId)
{
bookModel ModeloLibro = new bookModel();
ModeloLibro.EliminarLibro(bookId);
TempData["message"] = "Se ha eliminado el libro correctamente.";
return RedirectToAction("GestionBiblioteca");
}
And this is my Ajax in a view:
var myBookId = $('.parrafo-codigo').text();
$.ajax({
type: "GET",
url: "/Home/VerificarEliminarLibro",
data: { bookId: myBookId },
datatype: "json",
success: function (data) {
// $('#result').html(data);
if (data.esteLibroEstaPrestado == true) {
$('#delModal').modal('hide'); // Quito modal de pregunta si se elimina
$('#errModal').modal('show'); // Muestra modal de error
} else {
window.location.href = '/Home/EliminarLibro/' + myBookId;
}
}
});
The question is: how to make ActionResult EliminarLibro inaccessible via URL (for example XXXX/Home/EliminarLibro/0000532307) but is need to be called from ajax?
Done!
Thanks to Amr ElGarhy:
public class AjaxOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new HttpNotFoundResult();
}
}
}
And the Controller:
[AjaxOnly]
public JsonResult VerificarEliminarLibro(string bookId)
{
bookModel ModeloLibro = new bookModel();
bool HayLibrosPrestados = ModeloLibro.VerificarLibroPrestado(bookId);
if (HayLibrosPrestados == true)
{
return Json(new { esteLibroEstaPrestado = true }, JsonRequestBehavior.AllowGet);
}
else
{
return Json(new { esteLibroEstaPrestado = false }, JsonRequestBehavior.AllowGet);
}
}
if (Request.AjaxRequest())
{
// The Code
}
else
throw new HttpException(404, "");