ASP.Net Web API: Why is the Request.Content empty when the model is populated?

Hackered
Friday, October 2, 2015
by Sean McAlinden

A few years ago I had the pleasure of dubugging an issue where a member of my team was just seeing empty strings in the Request.Content of an ASP.Net Web API controller method, even though he knew the request body was sent.

It is important to understand that ASP.Net Web API and ASP.Net MVC are different, they definitely have similarities when we're coding with them but ultimately they are solving different problems.

One huge similarity is model binding.

Model Binding

Model binding is the process of hydrating strongly typed classes or parameters from an incoming http request.

It is without a doubt one of the most useful features when working with either ASP.Net MVC or ASP.Net Web API.

Using model binding feels pretty much the same in MVC and Web API but they have different underlying behaviour.

ASP.Net MVC is predominantly used for building websites and has the usual ASP.Net pipeline, Web API however is built for fast and potentially very chatty RESTful API's.

As Model Binding is a relatively expensive process, its benefits would soon disappear if using it meant poor performing Web API calls.

Forward-Only Streaming 

An incoming request to the ASP.Net Web API pipeline is read as a forward-only stream for super speed.

The caveat when using a forward-only stream is in the name - it can only go forward therefore once it has been read it cannot be read again.

The reason the Request.Content is empty following model binding is because the single possible read of the request stream was used to perform the model bind.

To populate the Request.Content property the forward-only stream woud have to be read again which is not possible.

In Action

[RoutePrefix("api/mywebapi")]
public class MyWebApiController : ApiController
{
    [Route("with/modelbinding")]
    public async Task<IHttpActionResult> WithModelBinding([FromBody] MyModel myModel)
    {
        var body = await Request.Content.ReadAsStringAsync();

        var hasBody = body != string.Empty;
            
        // Has body: false
        return Ok("Has body: " + hasBody);
    }

    [Route("without/modelbinding")]
    public async Task<IHttpActionResult> WithoutModelBinding()
    {
        var body = await Request.Content.ReadAsStringAsync();

        var hasBody = body != string.Empty;

        // Has body: true
        return Ok("Has body: " + hasBody);

    }
}

As you can see the first endpoint api/mywebapi/with/modelbinding utilizes model binding therefore has no content.

The second endpoint api/mywebapi/without/modelbinding does not use modelbinding therefore still has the Request.Content property populated.

Conclusion

Essentially it comes down to this:

if you want the huge benefit model binding provides then you will not have the ability to inspect the Request.Content property.

If you want to use the Request.Content property then you cannot utilize model binding.

For the most part model binding absolutely outways the loss of the Request.Content property, however in some circumstances such as creating a proxy endpoint, the Request.Content property suddenly becomes very important therefore model binding can be skipped in favour of more control.

Hope this has been helpful.