I am trying to use both FromUri and FromBody in web api 2 to populate an incoming request model. I understand I need to write a custom model binder to do that. Here is the example everyone references. This solution has been incorporated into the WebAPIContrib nuGet pacakge whose source code can be seen here on github.
I'm having trouble getting the MvcActionValueBinder to work with application/json body content. Here is part of the source that is throwing the exception.
class MvcActionBinding : HttpActionBinding
{
// Read the body upfront , add as a ValueProvider
public override Task ExecuteBindingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
HttpRequestMessage request = actionContext.ControllerContext.Request;
HttpContent content = request.Content;
if (content != null)
{
FormDataCollection fd = content.ReadAsAsync<FormDataCollection>().Result;
if (fd != null)
{
IValueProvider vp = new NameValuePairsValueProvider(fd, CultureInfo.InvariantCulture);
request.Properties.Add(Key, vp);
}
}
return base.ExecuteBindingAsync(actionContext, cancellationToken);
}
}
This line is throwing the exception:
FormDataCollection fd = content.ReadAsAsync<FormDataCollection>().Result;
Here is the exception:
System.AggregateException
{"Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Net.Http.Formatting.FormDataCollection' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'creditLimit', line 2, position 17."}
How can I get the model binder to work with applciation/json content instead of x-www-form-urlencoded content? Here is a similar question with no answer on the asp.net forums.
Update: Here is the controller method:
[Route("{accountId:int}/creditlimit")]
[HttpPut]
public async Task<IHttpActionResult> UpdateAccountCreditLimit(int accountId, [FromBody] RequestObject request)
{
// omitted for brevity
}
Here is the RequestObject:
class RequestObject
{
public int AccountId { get; set; }
public decimal CreditLimit { get; set; }
}
Here is the postman endpoint to test, its a PUT:
http://localhost/api/accounts/47358/creditlimit
The body I have set to application/json. Here is sample content.
{ "creditLimit": 125000.00 }
And yes, I realize I could change the controller method to do all FromUri or all FromBody instead. I do not have the liberty of doing that. Thanks.
creditLimitshould be populated based on your details, if theaccountIdparameter in the url is expected to be the same in the incoming model (request)then why not set it in the body of the action itself. i.e:request.AccountId = accountId;. I'm assuming that you want that property populated in the model without having to put it in the body of the PUT request