Why Use AJAX in an MVC Application?
AJAX (Asynchronous JavaScript and XML, though modern usage almost always means JSON) lets a browser send a request to an MVC action and update part of the page with the response without a full page reload. In practice this means a controller action returns JsonResult via return Json(data) for a JavaScript-driven UI update, or PartialViewResult via return PartialView("_ProductRow", model) when you want the server to render a chunk of HTML that jQuery's $.ajax success callback then injects into the DOM with $("#container").html(response), avoiding the cost and jarring UX of reloading the entire page for a small interaction like adding an item to a cart.
Cricket analogy: It is like a stadium's electronic scoreboard updating just the 'runs' digit the instant a boundary is hit, instead of the entire scoreboard going dark and rebooting; fans watching a run-chase like Rohit Sharma's chase-master innings never lose sight of the overall picture during the update.
Returning JSON and Partial Views
A controller action intended purely for data exchange typically returns JsonResult, and when the request originates from a script rather than a browser address bar, GET requests returning JSON must explicitly pass JsonRequestBehavior.AllowGet — return Json(data, JsonRequestBehavior.AllowGet) — because MVC blocks JSON GET responses by default as a defense against a JSON hijacking vulnerability. When the AJAX call needs to update a chunk of markup rather than raw data, returning PartialView("_ProductRow", product) renders just that Razor partial's HTML server-side and sends it back, letting the client simply swap it into the DOM instead of the client reconstructing HTML from JSON in JavaScript.
Cricket analogy: It is like a broadcaster deciding whether to send viewers raw ball-tracking data for their own fantasy app, or a fully rendered highlight clip ready to play — sometimes you want structured numbers, other times pre-rendered content, similar to how Hawk-Eye data feeds differ from broadcast replay packages.
// Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddToCart(int productId, int quantity)
{
var product = db.Products.Find(productId);
if (product == null)
return Json(new { success = false, message = "Product not found" });
cartService.Add(productId, quantity);
return Json(new { success = true, cartCount = cartService.GetItemCount() });
}
public ActionResult ProductRow(int id)
{
var product = db.Products.Find(id);
return PartialView("_ProductRow", product); // renders _ProductRow.cshtml
}$('#add-to-cart-btn').on('click', function () {
var productId = $(this).data('product-id');
$.ajax({
url: '/Cart/AddToCart',
type: 'POST',
data: {
productId: productId,
quantity: 1,
__RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
},
success: function (response) {
if (response.success) {
$('#cart-count').text(response.cartCount);
} else {
alert(response.message);
}
},
error: function () {
alert('Something went wrong. Please try again.');
}
});
});CSRF Protection and Error Handling in AJAX Calls
POST actions decorated with [ValidateAntiForgeryToken] still require the anti-forgery token even when called via AJAX, so the token generated by @Html.AntiForgeryToken() in the view must be read from the DOM and included explicitly in the $.ajax data payload, as shown above — omitting it causes every AJAX POST to fail with a 400 error. On the client, every $.ajax call should define both success and error callbacks, because network failures, server exceptions, and validation failures all surface differently: a 500 error lands in the error callback, while a deliberately returned Json(new { success = false, errors = ModelState... }) lands in success but still needs application-level handling to show the user what went wrong.
Cricket analogy: It is like an umpire refusing to allow a bowler to bowl the next over without physically checking the ball hasn't been tampered with, no matter how routine the over looks; skipping that verification step, even in a friendly match, invalidates the delivery, similar to how ball-checks are mandatory even in a low-stakes fixture.
For AJAX forms rendered with Ajax.BeginForm() (from the Microsoft AJAX helpers, requiring jquery.unobtrusive-ajax.js), the anti-forgery token is included automatically as a hidden field inside the form and submitted along with the rest of the form data, so no manual token handling is needed the way it is with a raw $.ajax call.
Never skip the error callback in a $.ajax call. Silent failures — where a 500 error or network timeout leaves the UI looking like nothing happened — are one of the most common usability complaints in AJAX-heavy MVC applications, and users are left clicking a button repeatedly with no feedback.
- AJAX lets an MVC action update part of a page without a full reload, using JsonResult for data or PartialViewResult for server-rendered HTML fragments.
- GET actions returning JSON must pass JsonRequestBehavior.AllowGet to bypass MVC's default JSON hijacking protection.
- PartialView("_Name", model) renders a Razor partial server-side, which the client injects directly into the DOM.
- [ValidateAntiForgeryToken] still applies to AJAX POSTs — the token must be read from the DOM and sent explicitly.
- Every $.ajax call should define both success and error callbacks to handle network failures and server exceptions distinctly.
- Application-level failures (like validation errors) are often returned as a 200 response with a success:false payload, requiring explicit handling even inside the success callback.
- Ajax.BeginForm() with jquery.unobtrusive-ajax.js automatically includes the anti-forgery token, unlike raw $.ajax calls.
Practice what you learned
1. Why must an action return Json(data, JsonRequestBehavior.AllowGet) instead of just Json(data) for a GET request?
2. What is the benefit of returning PartialView("_ProductRow", product) instead of Json(product) for an AJAX call that adds a row to a table?
3. Why does a raw $.ajax POST to an action decorated with [ValidateAntiForgeryToken] fail with a 400 error if not handled?
4. What is a key reason to always define an error callback on $.ajax calls?
5. Why might a $.ajax success callback still need to check response.success even though the HTTP status was 200?
Was this page helpful?
You May Also Like
Model Binding in MVC
How ASP.NET MVC automatically maps incoming HTTP request data—form fields, route values, and query strings—into strongly typed action method parameters.
Data Annotations and Validation
How to use Data Annotation attributes to declaratively validate model properties, and how MVC surfaces those rules on both server and client.
Working with Entity Framework in MVC
How ASP.NET MVC applications use Entity Framework's DbContext and LINQ to query and persist data behind controller actions.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics