Model Binding Issue with ASP.NET5 MVC6

Im trying to post some JSON data on an angular form to my ASP.NET5 MVC6 Controller action. The model binder does not seem to be working. Not sure what I'm missing here.

My ASP Controller:

public class DefaultController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public IActionResult SubmitTest(QTestViewModel model)
    {
        return Json("true");
    }
}

My Angular Controller:

angular.module("testActiveMq", [])
.controller("MqTestController", ["$scope", "$http", function ($scope, $http) {
    // Submit Form
    $scope.submitForm = function () {
        debugger;
        var formData = (this.data) ? angular.toJson(this.data) : null;
        if (formData && this.qForm && this.qForm.$valid) {
            $http({
                url: "/Default/SubmitTest",
                data: formData,
                method: "POST",
                dataType: "json",
                contentType: "application/json; charset=utf-8"
            })
            .then(function successCallback(response) {
                debugger;
                // this callback will be called asynchronously
                // when the response is available
            }, function errorCallback(response) {
                debugger;
                // called asynchronously if an error occurs
                // or server returns response with an error status.
            });
        }
    };
}])

My View Model:

public class QTestViewModel
{
    public string MqBrokerUri { get; set; }

    public string ClientId { get; set; }

    public string UserName { get; set; }

    public string Password { get; set; }

    public int TotalRequests { get; set; }

    public int MaxConcurrentRequests { get; set; }

    public int DelayBetweenThreads { get; set; }
}

When I make a request, the HTTP Headers are ..

POST /Default/SubmitTest HTTP/1.1
Host: localhost:50877
Connection: keep-alive
Content-Length: 225
Accept: application/json, text/plain, */*
Origin: http://localhost:50877
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://localhost:50877/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8

My form data looks like so ..

{"MqBrokerUri":"ssl://broker-uri:1616?transport.acceptInvalidBrokerCert=true","ClientId":"MqLoadTest","UserName":"myunm","Password":"mypwd","TotalRequests":100,"MaxConcurrentRequests":10,"DelayBetweenThreads":1}

I feel like I'm missing something super obvious. Why is my JSON data not binding to my model? Surely I dont need a custom model binder for something so simple?

Answers


Your code was enough in MVC 5 and earlier versions to receive the model in your controller. However in MVC 6 you also need to set the [FromBody] parameter in your controller action:

[HttpPost]
public IActionResult SubmitTest([FromBody]QTestViewModel model)
{
    return Json("true");
}

Not sure why this is a requirement in MVC 6, but your model will keep its default values if you dont add the FromBody attribute.

  • Check for example the Web API tutorial in the official docs.

  • After some digging into the source, it seems the BodyModelBinder will only accept models that specifically enabled the body binding source, which is done adding the [FromBody] attribute.

    var allowedBindingSource = bindingContext.BindingSource;
    if (allowedBindingSource == null ||
        !allowedBindingSource.CanAcceptDataFrom(BindingSource.Body))
    {
        // Formatters are opt-in. This model either didn't specify [FromBody] or specified something
        // incompatible so let other binders run.
        return ModelBindingResult.NoResultAsync;
    }
    

PS. Angular is stringifying by default the json objects, but if in case you use something like jQuery you also need to manually call JSON.stringify.


Sometime it's happened with json better to use it

var jsonResult=json("true");
jsonResult.maxJsonLength=int32.maxValue
return jsonresult;

Hope it will help for you.


[edit] It appears this answer is incorrect. My issue was caused by the Action parameter being named the same as one of the properties of the object. This resulted in MVC using a prefix to prevent ambiguity. I don't agree that it is ambiguous and am discussing this on Github.


I've just logged an error on Github about this. It seems that if your parameter isn't named as the CamelCase of the Type of the parameter, then it expects that the fields in the payload to be prefixed with the parameter name.

You can fix this by adding [Bind(Prefix="")] before the Action's parameter. public IActionResult SubmitTest([Bind(Prefix="")]QTestViewModel model)


Ok @Daniel J.G. answere did not work for me I had to use [FromForm] instead of [FromBody] To get the binging working

  [HttpPost]
public IActionResult SubmitTest([FromForm]QTestViewModel model)
{
    return Json("true");
}

Need Your Help

Scraping HTML from Financial Statements

c# visual-studio-2010 web-scraping html-agility-pack

First attempt at learning to work with HTML in Visual Studio and C#. I am using html agility pack library. to do the parsing.

Confounded by special CSS case

javascript html css adobe-brackets

I was watching this video, it's a short demo for the Brackets IDE. I'm new to web development so I anticipate being confused by the code I see but there was something in there that I really don't g...