public class TestController : ApiController
{
public HttpResponseMessage Get(string id)
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
public HttpResponseMessage Post(string id)
{
var content = Request.Content.ReadAsStringAsync().Result;
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
Could you explain on this simple scenario why would this test pass:
var uri = new Uri(baseUri, "test/asdf/someId") response = client.GetAsync(uri).Result;
response.EnsureSuccessStatusCode();
And this fail:
var uri = new Uri(baseUri, "test/asdf/someId") response = client.PostAsync(uri, new StringContent("some content")).Result;
response.EnsureSuccessStatusCode();
The secod test fails with InvalidOperationException: 'Headers.ContentType' must be set before 'ObjectContent' can serialize its content. The controllers Post method isn't invoked. The test passes if the 'id' parameter of the Post action is removed.
The reason you are getting the InvalidOperationException on the Post is due to the fact that there is not a "text/plain" media type formatter in the framework. You will need to create a custom MediaTypeFormatter that understands this content-type.
Also, make sure to add the new formatter to your HttpConfiguration formatters collection during application startup.
I should elaborate on this response. WebAPI is trying to model bind your "someId" parameter, and looks for a plain/text formatter and pukes since there is not one available. If you remove your parameter (a parameterless Post method) then it works fine.
Thanks for your replies. They were really helpful.
Instead of creating MediaTypeFormatter for 'text/plain' I'm now relying on built in formatter for application/xml by sending an object with content type set to 'application/xml'. Now it works fine but one thing. My {id} URL segment is bound to GET action
as expected, but it's null on POST.
The controller:
public class TestController : ApiController
{
public HttpResponseMessage Get(string id)
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
public HttpResponseMessage Post(string id, Content content)
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
public class Content
{
public string Value { get; set; }
}
When GET request is sent with this code:
var response = client.GetAsync("test/asdf/someId");
I can see in the debugger the value of 'id' argument in Get action being 'someId'.
When POST is sent:
var request = new HttpRequestMessage();
var content = request.CreateContent(new Content { Value = "some content" });
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/xml");
var response = client.PostAsync("test/asdf/someId", content).Result;
in the debugger in the Post action I see 'content' argument being model bound correctly and 'id' being null.
What's the move here to get 'id' bound to Post action as well?
I actually have the exact same problem. With POST and body parameters, I can either get complex working or primitives working, but not both at the same time.
To get a complex parameter working, I need to change the Read Policy to AsSingleObject - but this breaks the other parameters that were correctly bound by model binding.
Vistor
Member
12 Points
9 Posts
Model binding on POST
Mar 03, 2012 07:20 PM|LINK
I have this simple controller:
public class TestController : ApiController { public HttpResponseMessage Get(string id) { return new HttpResponseMessage(HttpStatusCode.OK); } public HttpResponseMessage Post(string id) { var content = Request.Content.ReadAsStringAsync().Result; return new HttpResponseMessage(HttpStatusCode.OK); } }The route is this:
routes.MapHttpRoute( "test", "{controller}/asdf/{id}"Could you explain on this simple scenario why would this test pass:
And this fail:
The secod test fails with InvalidOperationException: 'Headers.ContentType' must be set before 'ObjectContent' can serialize its content. The controllers Post method isn't invoked. The test passes if the 'id' parameter of the Post action is removed.
Thanks
davebettin
Member
313 Points
94 Posts
Re: Model binding on POST
Mar 04, 2012 01:48 AM|LINK
The reason you are getting the InvalidOperationException on the Post is due to the fact that there is not a "text/plain" media type formatter in the framework. You will need to create a custom MediaTypeFormatter that understands this content-type. Also, make sure to add the new formatter to your HttpConfiguration formatters collection during application startup.
Cheers,
Dave
@dbettin
davebettin
Member
313 Points
94 Posts
Re: Model binding on POST
Mar 04, 2012 02:57 AM|LINK
I should elaborate on this response. WebAPI is trying to model bind your "someId" parameter, and looks for a plain/text formatter and pukes since there is not one available. If you remove your parameter (a parameterless Post method) then it works fine.
@dbettin
Vistor
Member
12 Points
9 Posts
Re: Model binding on POST
Mar 04, 2012 07:54 AM|LINK
Thanks for your replies. They were really helpful.
Instead of creating MediaTypeFormatter for 'text/plain' I'm now relying on built in formatter for application/xml by sending an object with content type set to 'application/xml'. Now it works fine but one thing. My {id} URL segment is bound to GET action as expected, but it's null on POST.
The controller:
public class TestController : ApiController { public HttpResponseMessage Get(string id) { return new HttpResponseMessage(HttpStatusCode.OK); } public HttpResponseMessage Post(string id, Content content) { return new HttpResponseMessage(HttpStatusCode.OK); } } public class Content { public string Value { get; set; } }When GET request is sent with this code:
var response = client.GetAsync("test/asdf/someId");I can see in the debugger the value of 'id' argument in Get action being 'someId'.
When POST is sent:
var request = new HttpRequestMessage(); var content = request.CreateContent(new Content { Value = "some content" }); content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/xml"); var response = client.PostAsync("test/asdf/someId", content).Result;in the debugger in the Post action I see 'content' argument being model bound correctly and 'id' being null.
What's the move here to get 'id' bound to Post action as well?
Thanks,
Vitaly
OCedHrt
Member
11 Points
8 Posts
Re: Model binding on POST
Mar 16, 2012 02:31 AM|LINK
I actually have the exact same problem. With POST and body parameters, I can either get complex working or primitives working, but not both at the same time.
To get a complex parameter working, I need to change the Read Policy to AsSingleObject - but this breaks the other parameters that were correctly bound by model binding.
Vistor
Member
12 Points
9 Posts
Re: Model binding on POST
Mar 17, 2012 08:44 PM|LINK
This was confirmed as a bug which should be fixed in the next version.