When coding up Ajax calls in ASP.Net MVC we have a lot of options as far as issuing calls, handling them on the server, and dealing with successes and failures on the client. S
What is the best way to inject the URL of the action for the URL into the Ajax call?
There's no convention about this and it also largely depends how your content has been brought to the client. Was it via views/partial views (in which case you could generate URLs with Html
/Url
helper methods or is it purely generated on the client (templating and such). It's quite common that we just want some usual HTML elements to extend with Ajax behaviour (like forms, buttons and links). In this situation it's best to provide correct URLs that we can also read from them and use them in our scripts (like href
in links). It's also more clear to users where things are pointing to. Whether we convert them to GET/POST/DELETE/PUT requests doesn't really matter any more. I would suggest to avoid hard-coding URLs in your scripts because you may change routing or provide other means of processing. By using Html
helpers wherever possible is a much better way in terms of maintenance. When adding URLs to elements that don't support them automatically (no href
or src
attribute) you can always add them as custom attributes especially when you use data attribute (like data-href
or similar). Read John Resig's blog post about these.
What are the considerations when choosing JsonBehavior?
It's best to make POST requests when you have to return Json. In GET requests you have to be aware of the possible implications JSON Hijacking that's also the reason why you have to explicitly specify that you allow get requests with JSON results.
What is the best way to handle errors on the server side?
The best way to process server errors (that can't and shouldn't be suppressed) is to actually inform the user of the error. Details of it are usually not important since users are not development savvy but telling them something went wrong is ok. Especially if you can offer them some sort of a solution. And with Ajax call it's bets to return errors as errors that can be handled with error handler Javascript functions. I've written a blog post that details the whole process and also provides the code that shows how to correctly handle validation errors. I personally think that returning errors as success is wrong. hence I don't. But that's argumentative. My controller actions are rather simplified by the code I use:
[HandleModelStateException]
public ActionResult AddUser(User user)
{
if (!this.ModelState.IsValid)
{
throw new ModelStateException(this.ModelState);
}
// process valid data
}
Should the client side error() callback be triggered by any errors (ie unexpected OutOfMemoryException
), or only by foreseeable errors (ie invalid input)?
As stated previously errors should be reported to users when they will get unexpected results from what they initiated. Don't suppress errors that should be reported to them. But don't go into too much detail. Out of memory exception is a bit too complex to explain tu usual computer users. Report something human friendly and short.
What is the best way of exporting errors in a way that the error()
callback will be triggered.
Look at my blog post and check the code to see how to create errors that are handled by the error()
function
Best way of ensuring the error callback gets the correct status code and response text.
Same blog post
Should validation errors result in an error StatusCode
or should they be part of a responding validation object.
Validation errors are actually warnings by nature and should be treated as such. That's why you can see positive form validation pattern on some sites that also report when data is valid instead of just informing users that they've made yet another mistake. Validation errors are usually rooted in invalid user interface experience and insufficient information.
What is the best way to handle errors on the client side?
This largely depends on your application but I suggest you follow proven patterns because users are likely to know the drill and won't confuse them. Why do you think OpenOffice uses almost identical UI as Microsoft Office? Because users know how it works so it's more likely to be acquainted with the app fast and painlessly. But if you can, don't treat users as dumb people. Use positive validation. Some errors should also be displayed as errors. Oh. Btw. I use unobtrusive infos in my app where information such as successful operation is reported to users but doesn't intrude their work. It automatically disappears after a while. Similar to what GMail does. It just informs you that mail has been sent and this info doesn't need any confirmation.
jQuery ajax()
functin supports success and error functions, so it's wise to use both. Again: Returning errors as success is not right. We wouldn't need error functions than at all. My HandleModelState action filter returns errors with particular 400 error code that can be used and its result as well.
$.ajax({
url: $(this).attr("href"),
data: someDataObj,
type: "POST",
success: function(data){
// process data
},
error: function(xhr, status, err){
if (xhr.status = 400) {
// handle my error in xhr.resposeText
}
else {
// handle other errors that are cause by unknown processing
}
}
});
Should unexpected errors server-side be displayed in a similar way as the validation summary? Maybe a just "something-went-wrong" dialog? Should the client be able to distinguish between the two?
People don't like the big red alerts. If something is a warning by nature it should be treated friendlier than actual errors that prevent your app to work properly. Validation in particular should be very friendly. Errors should be confirmed by users, warnings maybe shouldn't. Depends on their nature. But if they should be then they're probably errors.
I hope this clears some things for you.
What is the best way to inject the url of the action for the url into the ajax call?
Personally I use HTML5 data-* attributes on DOM elements which I AJAXify. For example:
<div id="foo" data-url="@Url.Action("foo")">Foo Bar</div>
and then:
$('#foo').click(function() {
var url = $(this).data('url');
// TODO: perform the AJAX call
});
Of course if the DOM element represents an anchor or a form I would use the native property (action or href).
What are the considerations when choosing JsonBehavior?
No specific considerations. Bear in mind that GET request might be cached by client browsers so you might need to specify { cache: false }
on the client. Oh and of course AllowGet on the server for Json.
What is the best way to handle errors on the server side?
Personally I use FluentValidation.NET to handle validation errors on my view models. There could also be errors coming from the service layer.
What is the best way to handle errors on the client side?
If the server returns JSON I usually have a structure which looks the following:
{ error = 'some error message', result: null }
or:
{ error = null, result: { foo: 'bar' } }
depending on whether there was an error, and on the client:
success: function(data) {
if (data.error != null && data.error != '') {
// error
} else {
// use data.result
}
}
I use the error
callback for everything that is unhandled exceptions on the server in which case simply show some generic error message. A global error handler could be specified using $.ajaxSetup
.
To ease the generation of these JSON results on the server and DRY my actions I use custom action filters which would test whether the request was an AJAX request and if it was an AJAX request test whether there were validation errors added to the ModelState and if they were replace the action result with a custom error JsonResult in order to handle the error case.
Here's how I do things in some recent projects:
1) I inject the ajax url in the view with a Url.Action call.
2) Jqgrid uses GETs to fetch grid data, so in the fill grid controller actions I need to use the JsonRequestBehavior.AllowGet switch in the Json method call.
3) I recently switched to Microsoft's unobtrusive client-side validation, which has a neat ModelState.IsValid method you can run server-side in your controller action to make sure the same exact validation is also running on the server side without code duplication. It took some work meshing it with jquery.maskedinput and so on to mask/unmask data, here's the article on how to get all that to work.
4) Regarding displaying the error client side, that's a design issue that has to be fleshed out by the stakeholders. Personally, I didn't like the validation next to the textboxes or a validation summary at the top of the page. I prefer the errors to jump out at your face, so I tweaked Microsoft's jquery.validate.unobtrusive.js to show the error in a popup modal dialog. You can see the code in the solution above in the jquery.validate.unobtrusive.MOD.js file (notice that my version is suffixed with ".mod.js") and you can see the error dialog in the Site.Master.
Example of controller action:
[HttpPost]
public ActionResult Index(PersonalInformation model)
{
if (ModelState.IsValid)
{
// TODO: sell personal information to sleazeballs
TempData["PersonalInformation"] = model;
return RedirectToAction("Success");
}
// If we got this far, something failed, redisplay form
Index_Load();
return View(model);
}
Hope this helps,
Robert