Getting raw POST data from Web API method

I have the following Web API method in an ApiController class:

public HttpResponseMessage Post([FromBody]byte[] incomingData)
{
  ...
}

I want incomingData to be the raw content of the POST. But it seems that the Web API stack attempts to parse the incoming data with the JSON formatter, and this causes the following code on the client side to fail:

new WebClient().UploadData("http://localhost:15134/api/Foo", new byte[] { 1, 2, 3 });

Is there a simple workaround for this?


Solution 1:

For anyone else running into this problem, the solution is to define the POST method with no parameters, and access the raw data via Request.Content:

public HttpResponseMessage Post()
{
  Request.Content.ReadAsByteArrayAsync()...
  ...

Solution 2:

If you need the raw input in addition to the model parameter for easier access, you can use the following:

using (var contentStream = await this.Request.Content.ReadAsStreamAsync())
{
    contentStream.Seek(0, SeekOrigin.Begin);
    using (var sr = new StreamReader(contentStream))
    {
        string rawContent = sr.ReadToEnd();
        // use raw content here
    }
}

The secret is using stream.Seek(0, SeekOrigin.Begin) to reset the stream before trying to read the data.

Solution 3:

The other answers suggest removing the input parameter, but that will break all of your existing code. To answer the question properly, an easier solution is to create a function that looks like this (Thanks to Christoph below for this code):

private async Task<String> getRawPostData()
{
    using (var contentStream = await this.Request.Content.ReadAsStreamAsync())
    {
        contentStream.Seek(0, SeekOrigin.Begin);
        using (var sr = new StreamReader(contentStream))
        {
            return sr.ReadToEnd();
        }
    }
}

and then get the raw posted data inside your web api call like so:

public HttpResponseMessage Post ([FromBody]byte[] incomingData)
{
    string rawData = getRawPostData().Result;

    // log it or whatever

    return Request.CreateResponse(HttpStatusCode.OK);
}

Solution 4:

In MVC 6 Request doesn't seem to have a 'Content' property. Here's what I ended up doing:

[HttpPost]
public async Task<string> Post()
{
    string content = await new StreamReader(Request.Body).ReadToEndAsync();
    return "SUCCESS";
}