438

I am trying to create controller actions which will return either JSON or partial html depending upon a parameter. What is the best way to get the result returned to an MVC page asynchronously?

tereško
  • 58,060
  • 25
  • 98
  • 150
NathanD
  • 8,061
  • 7
  • 30
  • 26

11 Answers11

550

In your action method, return Json(object) to return JSON to your page.

public ActionResult SomeActionMethod() {
  return Json(new {foo="bar", baz="Blech"});
}

Then just call the action method using Ajax. You could use one of the helper methods from the ViewPage such as

<%= Ajax.ActionLink("SomeActionMethod", new AjaxOptions {OnSuccess="somemethod"}) %>

SomeMethod would be a javascript method that then evaluates the Json object returned.

If you want to return a plain string, you can just use the ContentResult:

public ActionResult SomeActionMethod() {
    return Content("hello world!");
}

ContentResult by default returns a text/plain as its contentType.
This is overloadable so you can also do:

return Content("<xml>This is poorly formatted xml.</xml>", "text/xml");
Eduardo Molteni
  • 38,786
  • 23
  • 141
  • 206
Haacked
  • 58,045
  • 14
  • 90
  • 114
  • 10
    sorry phil! this doesnt actually answer the question does it? its definitely useful but as brad says you need to find out somehow what they are asking for and return the result accordingly. – Simon_Weaver Jan 27 '09 at 05:07
  • see my somewhat related (well the one that led me here) question at http://stackoverflow.com/questions/482363/should-my-mvc-controller-really-know-about-json – Simon_Weaver Jan 27 '09 at 05:08
  • 9
    if you find an answer, link it in the question itself. Also i don't think checking this as the answer is the right thing. – Cherian Feb 18 '09 at 14:32
  • http://stackoverflow.com/questions/320291/how-to-post-an-array-of-complex-objects-with-json-jquery-to-asp-net-mvc-controll is related – Cherian Feb 18 '09 at 14:33
  • What is the fully qualified name of that Json class? – Josh Withee Dec 19 '18 at 17:35
  • @Haacked: I have read your [Json Hijacking](https://haacked.com/archive/2009/06/25/json-hijacking.aspx/) blog, would you be able to explain why changing `Get` to `Post` prevents the attack? I have asked this question here: https://stackoverflow.com/questions/61995800/how-to-get-value-of-input-tag-in-javasctipt/61995917#61995917 – Hooman Bahreini May 27 '20 at 22:07
  • Because the attack relies on the attacker using a script tag with the src set to your code. If your code only responds to POST, it won't get loaded in someone's script tag. – Haacked May 29 '20 at 00:39
115

I think you should consider the AcceptTypes of the request. I am using it in my current project to return the correct content type as follows.

Your action on the controller can test it as on the request object

if (Request.AcceptTypes.Contains("text/html")) {
   return View();
}
else if (Request.AcceptTypes.Contains("application/json"))
{
   return Json( new { id=1, value="new" } );
}
else if (Request.AcceptTypes.Contains("application/xml") || 
         Request.AcceptTypes.Contains("text/xml"))
{
   //
}

You can then implement the aspx of the view to cater for the partial xhtml response case.

Then in jQuery you can fetch it passing the type parameter as json:

$.get(url, null, function(data, textStatus) {
        console.log('got %o with status %s', data, textStatus);
        }, "json"); // or xml, html, script, json, jsonp or text
starball
  • 20,030
  • 7
  • 43
  • 238
James Green
  • 1,827
  • 1
  • 13
  • 13
  • 5
    Thanks James, that could be very useful for creating sort of a website and a REST API using the same Controller Actions. – NathanD Oct 01 '09 at 13:34
  • If I have many methods like this in my controller is there any way that I can do this more generically? – Seph Jun 10 '12 at 13:45
  • In which namespace is the Json class ? What is the dependency for project.json ? Thanks in advance – Andrei Jul 22 '16 at 21:16
  • 1
    That's the [JsonResult class from System.Web.Mvc](https://msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult(v=vs.118).aspx) (in System.Web.Mvc.dll) @Andrei – James Green Jul 26 '16 at 07:58
  • Thank you, found it. Maybe update the answer to reflect the new API? Btw, I'm using dotnet core where it's Microsoft.AspNetCore.Mvc.JsonResult. – Andrei Jul 26 '16 at 16:26
84

Another nice way to deal with JSON data is using the JQuery getJSON function. You can call the

public ActionResult SomeActionMethod(int id) 
{ 
    return Json(new {foo="bar", baz="Blech"});
}

Method from the jquery getJSON method by simply...

$.getJSON("../SomeActionMethod", { id: someId },
    function(data) {
        alert(data.foo);
        alert(data.baz);
    }
);
radbyx
  • 9,352
  • 21
  • 84
  • 127
SaaS Developer
  • 9,835
  • 7
  • 34
  • 45
  • 16
    This does not answer the question at all. – Aaronaught Sep 14 '11 at 14:12
  • 2
    @Aaronaught Actually the first part `return Json(new {foo="bar", baz="Blech"});` does! – SparK Mar 21 '16 at 18:28
  • Also consider $.post https://stackoverflow.com/questions/751218/how-to-use-getjson-sending-data-with-post-method ( ASP.Net MVC defaults to disable JSON Get requests for security reasons ) – Greg Apr 24 '18 at 15:21
57

I found a couple of issues implementing MVC ajax GET calls with JQuery that caused me headaches so sharing solutions here.

  1. Make sure to include the data type "json" in the ajax call. This will automatically parse the returned JSON object for you (given the server returns valid json).
  2. Include the JsonRequestBehavior.AllowGet; without this MVC was returning a HTTP 500 error (with dataType: json specified on the client).
  3. Add cache: false to the $.ajax call, otherwise you will ultimately get HTTP 304 responses (instead of HTTP 200 responses) and the server will not process your request.
  4. Finally, the json is case sensitive, so the casing of the elements needs to match on the server side and client side.

Sample JQuery:

$.ajax({
  type: 'get',
  dataType: 'json',
  cache: false,
  url: '/MyController/MyMethod',
  data: { keyid: 1, newval: 10 },
  success: function (response, textStatus, jqXHR) {
    alert(parseInt(response.oldval) + ' changed to ' + newval);                                    
  },
  error: function(jqXHR, textStatus, errorThrown) {
    alert('Error - ' + errorThrown);
  }
});

Sample MVC code:

[HttpGet]
public ActionResult MyMethod(int keyid, int newval)
{
  var oldval = 0;

  using (var db = new MyContext())
  {
    var dbRecord = db.MyTable.Where(t => t.keyid == keyid).FirstOrDefault();

    if (dbRecord != null)
    {
      oldval = dbRecord.TheValue;
      dbRecord.TheValue = newval;
      db.SaveChanges();
    }
  }

    return Json(new { success = true, oldval = oldval},
                JsonRequestBehavior.AllowGet);
}
Shanerk
  • 5,175
  • 2
  • 40
  • 36
12

To answer the other half of the question, you can call:

return PartialView("viewname");

when you want to return partial HTML. You'll just have to find some way to decide whether the request wants JSON or HTML, perhaps based on a URL part/parameter.

Brad Wilson
  • 67,914
  • 9
  • 74
  • 83
7

Alternative solution with incoding framework

Action return json

Controller

    [HttpGet]
    public ActionResult SomeActionMethod()
    {
        return IncJson(new SomeVm(){Id = 1,Name ="Inc"});
    }

Razor page

@using (var template = Html.Incoding().ScriptTemplate<SomeVm>("tmplId"))
{
    using (var each = template.ForEach())
    {
        <span> Id: @each.For(r=>r.Id) Name: @each.For(r=>r.Name)</span>
    }
}

@(Html.When(JqueryBind.InitIncoding)
  .Do()
  .AjaxGet(Url.Action("SomeActionMethod","SomeContoller"))
  .OnSuccess(dsl => dsl.Self().Core()
                              .Insert
                              .WithTemplate(Selector.Jquery.Id("tmplId"))
                              .Html())
  .AsHtmlAttributes()
  .ToDiv())

Action return html

Controller

    [HttpGet]
    public ActionResult SomeActionMethod()
    {
        return IncView();
    }

Razor page

@(Html.When(JqueryBind.InitIncoding)
  .Do()
  .AjaxGet(Url.Action("SomeActionMethod","SomeContoller"))
  .OnSuccess(dsl => dsl.Self().Core().Insert.Html())
  .AsHtmlAttributes()
  .ToDiv())
Vlad
  • 231
  • 2
  • 6
6

You may want to take a look at this very helpful article which covers this very nicely!

Just thought it might help people searching for a good solution to this problem.

http://weblogs.asp.net/rashid/archive/2009/04/15/adaptive-rendering-in-asp-net-mvc.aspx

yoozer8
  • 7,361
  • 7
  • 58
  • 93
Paul Hinett
  • 1,951
  • 2
  • 26
  • 40
5

PartialViewResult and JSONReuslt inherit from the base class ActionResult. so if return type is decided dynamically declare method output as ActionResult.

public ActionResult DynamicReturnType(string parameter)
        {
            if (parameter == "JSON")
                return Json("<JSON>", JsonRequestBehavior.AllowGet);
            else if (parameter == "PartialView")
                return PartialView("<ViewName>");
            else
                return null;


        }
3

For folks who have upgraded to MVC 3 here is a neat way Using MVC3 and Json

Sarath
  • 2,719
  • 1
  • 31
  • 39
2
    public ActionResult GetExcelColumn()
    {            
            List<string> lstAppendColumn = new List<string>();
            lstAppendColumn.Add("First");
            lstAppendColumn.Add("Second");
            lstAppendColumn.Add("Third");
  return Json(new { lstAppendColumn = lstAppendColumn,  Status = "Success" }, JsonRequestBehavior.AllowGet);
            }
        }
sakthi
  • 21
  • 2
2

Flexible approach to produce different outputs based on the request

public class AuctionsController : Controller
{
  public ActionResult Auction(long id)
  {
    var db = new DataContext();
    var auction = db.Auctions.Find(id);

    // Respond to AJAX requests
    if (Request.IsAjaxRequest())
      return PartialView("Auction", auction);

    // Respond to JSON requests
    if (Request.IsJsonRequest())
      return Json(auction);

    // Default to a "normal" view with layout
    return View("Auction", auction);
  }
}

The Request.IsAjaxRequest() method is quite simple: it merely checks the HTTP headers for the incoming request to see if the value of the X-Requested-With header is XMLHttpRequest, which is automatically appended by most browsers and AJAX frameworks.

Custom extension method to check whether the request is for json or not so that we can call it from anywhere, just like the Request.IsAjaxRequest() extension method:

using System;
using System.Web;

public static class JsonRequestExtensions
{
  public static bool IsJsonRequest(this HttpRequestBase request)
  {
    return string.Equals(request["format"], "json");
  }
}

Source : https://www.safaribooksonline.com/library/view/programming-aspnet-mvc/9781449321932/ch06.html#_javascript_rendering

Mannan Bahelim
  • 1,289
  • 1
  • 11
  • 31