Errors when passing data via jquery ajax - asp.net-mvc

I have a pretty simple ajax request that I'm sending over to server in order to get some data and fill up my edit modal. But for some reason it keeps returning with error and I can't figure out why. I've debugged the server side, parameter comes in correctly and all data is properly found and returned, still an error though.
Here's my code so someone might see what am I missing here.
Request:
function EditNorm(id) {
$.ajax({
type: "POST",
url: "#Url.Action("GetNormViewModel")",
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({id : id}),
cache: false,
success: function(data) {
FillFormForEditing(data.nvm);
},
error: function() {
alert("Error On EditNorm function");
}
});
}
Server side:
public JsonResult GetNormViewModel(int id)
{
var nvm = new NormViewModel {Norm = db.Norms.Find(id), Materials = db.Materials.ToList()};
return Json(new {nvm = nvm}, JsonRequestBehavior.AllowGet);
}

Firstly: You are using a POST method on your javascript while your controller accepts a Get, add this to your action:
[HttpPost]
public JsonResult GetNormViewModel(int id)
{
return Json(new { ... });
}
Secondly: What is db is it LinqToSQL / Entity Framework context? If so, make sure no call to your data context is performed after the data is returned. (i.e. changed your action and simply return return Json(new { nvm = "test" }); and console.log/alert to make sure you've got the result back. This will tells you that its your model that failed when it's returned due to some late binding.

Related

How to use a custom "__RequestVerificationToken"?

I've made a partial view like this (location: MyController/_Form.cshtml):
<form asp-antiforgery="true">
<input type="button" value="submit" />
</form>
Some actions in Controller:
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Test()
{
return Ok(new { succeeded = true });
}
[HttpPost]
public IActionResult GetTemplate()
{
string template = _viewRender<string>("MyController/_Form", null);
return Ok({ template = template });
}
The _viewRender is a service to convert from partial view to a string.
I've tested with these steps:
Using jquery to make a request from client to server to get the template and append to some div.
let onSuccess = function (data) {
$(data.template).appendTo('.myDiv');
};
$.ajax({
url: '/MyController/GetTemplate',
method: 'POST'
}).done(onSuccess).fail(onError);
And the event to detect submiting form looks like:
$(document).on('click', 'input[type=text]', function () {
let _this = $(this);
let token = _this.parent().find('[name=__RequestVerificationToken]').val();
let onSuccess = function (data) {
console.log(data); // should be: Object:{succeeded:true}
};
$.ajax({
url: '/MyController/Test',
method: 'POST',
data: { __RequestVerificationToken: token },
processData: false,
contentType: false
}).done(onSuccess).fail(onError);
});
When I made the request, I always got error code 404 - not found on Console tab.
I'm sure the path is correct. So, I've tried to remove ValidateAntiForgeryToken attribute from Test action and tried again. It's working fine (request status code 200).
So, I guess the problem (which gave 404 error) came from the token. I've used developer tool to check again and I'm sure that I have a token. But I don't know how to check the token is valid or not.
The token was generated from server. I just made a request to get it and appended to body. Then, re-sent it to server. But server didn't accept it.
Why?
This is how it's done in ASP.NET Core...
In Startup.cs you'll need to setup the anti-forgery header name.
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
You need to do this because by default anti-forgery will only consider form data and we want it to work with ajax too.
In your .cshtml file you'll need to add #Html.AntiForgeryToken() which will render a hidden input with the validation token.
Finally in your ajax code you need to setup the request header before sending.
beforeSend: function(xhr) {
xhr.setRequestHeader("X-XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
}
So in your case the ajax code will look like this.
$.ajax({
url: '/MyController/Test',
method: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
processData: false,
contentType: false
}).done(onSuccess).fail(onError);
Two things.
First, I use a custom filter instead of ValidateAntiForgeryToken. I don't remember why. Probably ValidateAntiForgeryToken doesn't work with AJAX requests.
Here's the code for the custom filter I use.
[AttributeUsage(AttributeTargets.Class)]
public sealed class ValidateAntiForgeryTokenOnAllPostsAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException(nameof(filterContext));
}
var request = filterContext.HttpContext.Request;
// Only validate POSTs
if (request.HttpMethod == WebRequestMethods.Http.Post)
{
// Ajax POSTs and normal form posts have to be treated differently when it comes
// to validating the AntiForgeryToken
if (request.IsAjaxRequest())
{
var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie?.Value;
AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
}
else
{
new ValidateAntiForgeryTokenAttribute().OnAuthorization(filterContext);
}
}
}
}
Second, the token goes in the request header not the data part. I add it to the header using ajaxSetup in the layout file. That way I don't have to worry about remembering to add it to every AJAX request.
$.ajaxSetup({
cache: false,
headers: { "__RequestVerificationToken": token }
});

Ajax Request issue with ASP.NET MVC 4 Area

Today i discovered something weird, i have regular asp.net mvc 4 project with no such ajax (just post, get). so today i need ajax request, i did an ajax action with jquery in controller and it didn't work out. Here is my code
Areas/Admin/Controllers/BannersController
public JsonResult SaveOrder(string model)
{
bool result = false;
if (!string.IsNullOrEmpty(model))
{
var list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<int>>(model);
result = repository.SaveOrder(list);
}
return Json(result, JsonRequestBehavior.AllowGet);
}
View side (Its in area too)
$(document).ready(function () {
$("#saveOrder").click(function () {
var data = JSON.stringify($("#list_banners").nestable('serialize'));
$.ajax({
url: '#Url.Action("SaveOrder", "Banners", new { area = "Admin" })',
data: { model: data },
success: function (result) {
if (result) {
toastr.success('Kaydedildi.');
}
else {
toastr.error('kaydedilemedi.');
}
},
error: function (e) {
console.log(e);
}
});
});
});
i've already tried everything i know, which is $.post, $.get, ajax options, trying request from out of area etc.. just request can't reach action
and here is the errors ,
http://prntscr.com/297nye
error object
http://prntscr.com/297o3x
Try by specifying the data format (json) you wand to post to server like and Also change the way you pass data object in JSON like this :
var data = $("#list_banners").nestable('serialize');
$.ajax({
url: '#Url.Action("SaveOrder", "Banners", new { area = "Admin" })',
data: JSON.stringify({ model: data }),
dataType: 'json',
contentType: "application/json",
...
I had same issue, but after spending too much time, got solution for that. If your request is going to your specified controller, then check your response. There must be some problem in your response. In my case, response was not properly converted to JSON, then i tried with passing some values of response object from controller using select function, and got what i needed.

Ajax call returns parsererror

I have a Controller with the Action Like This
public ActionResult UpdateCompanyInfo(Parameter parameter)
{
bool result = false;
if(ModelState.IsValid)
{
bla bla bla
}
return Json(result, JsonRequestBehavior.AllowGet);
}
I also have a javascript function:
function blabla(){
$.ajax({
type: 'POST',
url: "What Ever",
async: false,
data: JSON.stringify(jsonObject),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function (succeed) {
if (succeed) {
// Some Code
}
},
error: function () {
//Some Other Code
}
});
}
the javascript function calls properly and it hits Action method with the parameter (No problem yet!).
but when the Action method returns bool object using Json function, it hits the javascript error function response with the following parameters:
readyState 4
responseText "true"
status 200
statusText "parsererror"
jQuery15101815967692459196_1384707824272 was not called
I also tried
return Json(new { result }, JsonRequestBehavior.AllowGet); and
return Json(new {succeed = result }, JsonRequestBehavior.AllowGet); and
return Json(new {succeed = result.ToString() }, JsonRequestBehavior.AllowGet);
but they did not work too.
in addition i have an exactly the same function in another controller which calls another same javascript function but it works properly. I don't know what's wrong with it. am i missing something?
----------- EDIT --------------
I don't know why! but when i remove the dataType: 'json', from the Ajax call, and put return Json(result, JsonRequestBehavior.AllowGet); in action method, it works properly. could someone explain it to my "why?"
Actually it seems there is a bug in jquery 1.5.1 and 1.5.0 (which does not exist in previous versions).
In fact when you use dataType: 'json' it tries to parse json as a script and you should use dataType: 'text/json' instead. see this link.
The data returned from the server might not be of json type. So you can use below two method to overcome.
Option 1:
Use dataType: 'text' as part of ajax request.
Option 2:
Remove the dataType from ajax request as it will try to infer it based on the type of the response.
From your response, In controller, there is no json encoded data, jsonformat looks like, {'responseText':'your text here '}
You are only returning a single value which the JSON parser in JS will not accept. Try this:
public ActionResult UpdateCompanyInfo(Parameter parameter)
{
bool result = false;
if(ModelState.IsValid)
{
bla bla bla
}
return Json(new { success = result }, JsonRequestBehavior.AllowGet);
}
The anonymous object returned by JSON will look like this:
[{ "success": true }]
Which you can then deal with in your success handler, like this:
success: function (data) {
if (data.success) {
// Some Code
}
else {
// Bad things going down
}
},

Why does POST without parameters not return JSON

I have a controller method
[HttpPost]
public ActionResult GetUserData()
{
return Json(GetCurrentUser());
}
I'm calling it $.ajax() through a method like this:
ServerCall: function (method, args, callback) {
$.ajax({
type: 'POST',
url: method,
data: JSON.stringify(args),
contentType: 'application/json;charset=utf8',
dataType: 'json',
success: function (result) {
if (callback) {
callback(result);
}
},
error: function (err) {
}
});
}
with the call being:
ServerCall('GetUserData', null, function(data){
});
As it is, when I make this call, $.ajax returns with success, but 'data' is null. Debugging, responseText is empty. On the server side, GetUserData is called, and it is returning a properly formatted Json object (I've gone so far as to create my own JSON ActionResult and verified that data is indeed being written to the response stream.
If I add a dummy parameter to the server side method:
[HttpPost]
public ActionResult GetUserData(string temp)
{
return Json(GetCurrentUser));
}
everything works perfectly. Browser is IE8. My question is, can anyone explain why this is happening?
UPDATE:
Note workaround solution below: I'd still be interested in knowing the root cause.
No repro.
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult GetUserData()
{
return Json(new { foo = "bar" });
}
}
Index.cshtml view:
<script type="text/javascript">
var serverCall = function (method, args, callback) {
$.ajax({
type: 'POST',
url: method,
data: JSON.stringify(args),
contentType: 'application/json;charset=utf8',
dataType: 'json',
success: function (result) {
if (callback) {
callback(result);
}
},
error: function (err) {
}
});
};
serverCall('#Url.Action("GetUserData")', null, function (data) {
alert(data.foo);
});
</script>
result: 'bar' is alerted (as expected).
I was able to reproduce using Darin's code in IE8. While I don't know the root cause, I think it has something to do with how IE8 JSON.stringify handles null. Changing
data: JSON.stringify(args)
to
data: args ? JSON.stringify(args) : null
fixed the problem.
Note, the problem is intermittent - I was seeing failures in about one out of every ten calls. With the change, over 100 tests, the failure rate was zero.

jQuery.parseJSON not working for JsonResult from MVC controller action

I am trying to use jQuery.parseJSON to parse out the return value from an MVC3 controller action.
Controller:
[HttpPost]
public JsonResult LogOn(LogOnModel model, string returnUrl)
{
.. do stuff ..
if (errors.Count() < 0)
{
return Json(new object[] { true, model, errors });
}
return Json(new object[] { false, model, errors });
}
jQuery:
$.ajax({
url: form.attr('action'),
type: "POST",
dataType: "json",
data: form.serialize(),
success: function (data) {
var test = jQuery.parseJSON(data);
}
});
Json result from fiddler:
Content-Type: application/json; charset=utf-8
[false,{"UserName":"1","Password":"2","RememberMe":false},[{"Key":"","Errors":[{"Exception":null,"ErrorMessage":"The
user name or password provided is incorrect."}]}]]
Fiddler can parse the results:
The call to jQuery.parseJSON is returning null.
My questions is, how can I parse the json return value into an object?
Thanks!
You don't need to call parseJSON in your success handler, because ajax will have already parsed the JSON result (it does this automatically because you specified dataType:'json') into your array.
However, I'd recommend returning some sort of result object (whether you create an actual class in C# or use an anonymous type).
[HttpPost]
public JsonResult LogOn(LogOnModel model, string returnUrl)
{
.. do stuff ..
if (errors.Count() < 0)
{
return Json(new { success=true, model, errors });
}
return Json(new { success=false, model, errors });
}
and at the client
$.ajax({
url: form.attr('action'),
type: "POST",
dataType: "json",
data: form.serialize(),
success: function (result) {
alert(result.success);
// also have result.model and result.errors
}
});
You are actually returning an array of objects and they shoould be accessed like this in the success function:
var booleanValue = data[0];
var yourModel = data[1];
var yourErrors = data[2];
I did give #HackedByChinese an up vote because naming the properties might be a better way to go about it in the end. Howvbere this will solve your immediate problem.

Resources