Tutorials ASP.NET Core MVC Mastery
JsonResult
On this page
JsonResult in ASP.NET Core MVC β Returning Data for AJAX & APIs
Not every controller action needs to return a full HTML page. When your front-end JavaScript makes AJAX calls, or when you're building lightweight API endpoints within an MVC project, you need to return structured JSON data. JsonResult is your tool for exactly this β clean, serialized JSON responses from within MVC controllers.
1. WHAT is JsonResult?
JsonResult is an ActionResult type that serializes an object to JSON format and sends it as the HTTP response body with the content type application/json. It's part of the Microsoft.AspNetCore.Mvc namespace and is returned using the Json() helper method.
ActionResult Family Tree
ActionResult
βββ ViewResult β Returns HTML page
βββ JsonResult β Returns JSON data β You are here
βββ PartialViewResult β Returns HTML fragment
βββ RedirectResult β Redirects to URL
βββ ContentResult β Returns raw string
βββ FileResult β Returns file download
βββ StatusCodeResult β Returns HTTP status code
2. WHY Use JsonResult?
- AJAX Responses: When JavaScript on your page calls the server (using
fetch()orjQuery.ajax()), it expects JSON, not HTML - Hybrid MVC/API: You're building an MVC app but need a few endpoints that return data for charts, dropdowns, or live search
- SPA Communication: Your Razor views load initially, then React/Vue components make API calls for dynamic data
- Performance: Sending a small JSON payload is far faster than rendering a full Razor view
3. REAL-TIME PRODUCTION EXAMPLES
Example 1: Basic Usage β Returning Product Data
public class ProductsController : Controller
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
=> _productService = productService;
// Returns JSON for AJAX calls
public async Task<IActionResult> GetProductJson(int id)
{
var product = await _productService.GetByIdAsync(id);
if (product == null)
return NotFound(new { success = false, message = "Product not found" });
// The Json() method serializes the object and sets Content-Type: application/json
return Json(new
{
success = true,
data = new
{
product.Id,
product.Name,
product.Price,
product.Category,
product.InStock
}
});
}
}
Example 2: AJAX Live Search with JsonResult
// Controller β Search endpoint returning JSON
[HttpGet]
public async Task<IActionResult> Search(string query)
{
if (string.IsNullOrWhiteSpace(query) || query.Length < 2)
return Json(new { results = Array.Empty<object>() });
var products = await _productService.SearchAsync(query, limit: 10);
return Json(new
{
results = products.Select(p => new
{
p.Id,
p.Name,
p.Price,
imageUrl = p.ImageUrl ?? "/img/placeholder.png",
url = Url.Action("Details", "Products", new { id = p.Id })
}),
totalFound = products.Count
});
}
// Front-end β JavaScript calling the endpoint
const searchInput = document.getElementById('searchBox');
searchInput.addEventListener('input', async function() {
if (this.value.length < 2) return;
const response = await fetch('/Products/Search?query=' + encodeURIComponent(this.value));
const data = await response.json();
// data.results is the JSON array from our controller
const html = data.results.map(item =>
'<a href="' + item.url + '" class="search-result">' +
'<img src="' + item.imageUrl + '" /> ' + item.name +
' β $' + item.price + '</a>'
).join('');
document.getElementById('searchResults').innerHTML = html;
});
Example 3: Custom JSON Serialization Options
// When you need camelCase, custom date formats, or null handling
public IActionResult GetOrderDetails(int id)
{
var order = _orderService.GetById(id);
// Custom serialization options per-response
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
WriteIndented = false // Compact in production
};
return new JsonResult(order, options);
}
// Global configuration in Program.cs (applies to ALL JSON responses)
builder.Services.AddControllersWithViews()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
4. JsonResult vs Ok() vs ContentResult
| Method | Returns | Content-Type | Best For |
|---|---|---|---|
Json() | JSON object | application/json | MVC controllers returning data for AJAX |
Ok() | 200 + optional body | application/json | API controllers (ControllerBase) |
Content() | Raw string | text/plain | Returning plain text, XML, or custom formats |
5. Best Practices
β DO
- Always wrap responses in a consistent envelope:
{ success, data, message } - Configure global JSON options in
Program.csfor consistency - Use
camelCasenaming to match JavaScript conventions - Return appropriate HTTP status codes alongside JSON
- Use DTOs β never expose database entities directly in JSON
β DON'T
- Don't return EF Core entities directly β circular references cause serialization errors
- Don't ignore null handling β use
WhenWritingNullto keep payloads clean - Don't forget CSRF token for POST requests from AJAX (use
[ValidateAntiForgeryToken]) - Don't mix MVC and API patterns β if you need full APIs, use
ApiController
6. Interview Mastery
Q: "When would you use JsonResult in an MVC controller instead of creating a separate Web API controller?"
Architect Answer: "I use JsonResult when the AJAX endpoint is tightly coupled to a specific Razor view β like a live search box, a cascade dropdown, or a 'like' button. These are UI-driven interactions, not domain APIs. If the endpoint serves multiple consumers (mobile app, third-party integration, separate SPA), I create a proper [ApiController] with versioning, content negotiation, and proper REST semantics. The distinction is: JsonResult is for view support, ApiController is for domain services."