Mvc 3 posting content type application/json, actio

2019-07-05 10:34发布

问题:

I have an interesting situation that has me stumped. It seems that posting appliction/json content type makes the basic routing engine unable to bind action method arguments.

Using the default route:

Routes.MapRoute(
  "Default", // Route name
   "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);

I have an action method that looks like this:

//Controller name: TestController
[HttpPost]
public ActionResult SaveItem(int id, JsonDto dto)
{
  // if content type of the request is application/json id = 0, JsonDto is populated
  // if content type of the request is application/x-www-form-urlencode id = 1
}

I am posting to this url /Test/SaveItem/1 + the json object.

The reason that I need to id and the JsonDto is that the id argument references the parent object that the JsonDto object need to releate to.

I suppose I could change the dto to contain the parent id as a property and work around this whole problem.

It just strikes me as strange that the id argument does not get populated when I post a application/json request.

回答1:

OK, you haven't shown how you are calling this action so we can only be guessing here. Here's an example of something that works perfectly fine for me and gets everything populated as expected in the SaveItem method:

Model:

public class JsonDto
{
    public string Foo { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult SaveItem(int id, JsonDto dto)
    {
        return Content("success", "text/plain");
    }
}

Index View:

<script type="text/javascript">
    $.ajax({
        url: '@Url.Action("SaveItem", new { id = 123 })',
        type: 'POST',
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify({
            foo: 'bar'
        }),
        success: function (result) {
            // TODO: handle the results
        }
    });
</script>


回答2:

I have figured out my problem.

The problem is that the Json data that is being posted to the action method includes an Id

property, in addition to the id route value from the default route. So when binding the JSON

object, its Id property wins over the route value in the URL. So to tweak Darin's example:

<script type="text/javascript">
    $.ajax({
        url: '@Url.Action("SaveItem", new { id = 123 })',
        type: 'POST',
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify({
            "Id": 456
        }),
        success: function (result) {
            // TODO: handle the results
        }
    });
</script>

When the action method exectures, the int id argument contains 456 and not 123 as I

(apparently mistakenly) expected.

So the workaround for me was to change my default route to:

Routes.MapRoute(
  "Default", // Route name
  "{controller}/{action}/{urlId}", // URL with parameters
  new { controller = "Home", action = "Index", urlId = UrlParameter.Optional } // 
);

Renaming the default id route parameter to urlId and updating my action methods solved the

conflict for me.