Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Accepting Raw Request Body Content with ASP.NET Web API


:P
On this page:
continued from Page 1

If you're looking for an ASP.NET Core version of this post you can find it here: Accepting Raw Request Body Content in ASP.NET Core API Controllers. This post only covers classic ASP.NET WebApi on the Full .NET Framework.

ASP.NET Web API is a great tool for building HTTP services with ASP.NET. It makes many things HTTP, that used to be ill defined in .NET easy and allows you to build a variety of services very easily. Like other complex abstraction frameworks it makes complex scenarios super easy, while some very simple and common operations are not quite as straight forward or as transparent as they should be.

During this year’s MVP summit it was pointed out that ASP.NET Web API makes some very simple operations that a newbie might try when getting started very non-obvious. One of those I wrote about last year, which is mapping post values to controller method parameters and turns out to require a fairly complex workaround. Another one that was brought to my attention by Scott Hanselman and is the topic of this post, is how to capture the raw content of an HTTP request.

Capturing raw request content is by no means difficult with Web API, but it’s not exactly obvious either. Yet, it’s one of those first steps that somebody kicking the tires of ASP.NET Web API is very likely to do.

“Hey let’s take a string and just post it to the server and see what happens, right?”

Yeah well… if you try that, you’re likely going to hit a wall because Web API’s behavior for simple value content mapping is not well defined nor easy to discover.

So in this post I’ll look at posting raw data – not JSON or XML, but just plain text or binary data – to an Web API controller and picking up that data in a controller method. In the process we’ll create a [NakedBody] attribute (the name is Scott’s idea, but it I like it!) that’ll handle the task of capturing raw request data to a parameter.

Complex Values are easy

ASP.NET Web API makes it pretty easy to pass complex data in the form of JSON or XML to the server. It’s easy to do and intuitive. If you want to capture an object you can simply create a controller method that has an object parameter and Web API automatically handles decoding JSON or XML into the object's structure for you.

[HttpPut] 
public HttpResponseMessage PutAlbum(Album album)
{
}

You don't have to do anything special to get album to parse from say JSON or XML - Web API's Conneg logic automatically detects the content type, maps it to a MediaFormatter and converts the incoming JSON or XML (or whatever other formatters are configured) data and converts it to the type of the parameter of the controller method.

If the data happens to be POST form data (ie. urlencoded key value pairs), Web API’s Model Binding can automatically map each of the keys of the form data to the properties of the object, including nested object paths.

So that's very easy and as it should be, and it actually addresses most of the realistic use cases. This is the ‘complex stuff is easy’ part.

Capturing the raw Request Body

But things are not so easy and transparent when you want to pass simple parameters like strings, numbers, dates etc. Because Web API is based on a host of conventions, some things that should be easy, such as easily picking up the raw content of a POST or PUT operation and capturing it, aren't quite so transparent as the automatic type mapping shown above.

One thing you might expect to be able to easily do, is to pick up the entire content body and map it to a parameter like this:

[HttpPost]
public string PostRawBuffer(string raw)
{
    return raw;
}

Quick, what do you think is required by the client to call this method the string parameter? I’ll wait here while you ponder…

The answer is - not easily and not without some additional ‘hints’.

There are a number of issues here that actually make this one of the worst parameter signatures in Web API.
This parameter signature does not work with any of these posted values:

  • Raw Content Buffer Data (entire buffer)
  • A JSON string with application/json content type
  • A UrlEncoded Form Variable
  • A QueryString Variable

In fact, no matter what you pass here in posted body content – the string parameter is always null. The same is true if you have any other simple parameter type – numbers, bools, dates, byte[] etc. Plain parameter mapping (without special attributes) works only complex types like objects and arrays.

If you really think about how Web API’s parameter bindings work, this sort of makes sense with the exception of the JSON string parameter. Parameter bindings map based on media types (ie. content-type header) using the Conneg algorithm by default and try to map parameters as whole entities. If you post a a raw string or buffer Web API internally really has no idea how to map this to anything. Should it map the entire buffer? A form variable? A JSON string? So it needs some hints to do it’s thing.

Why doesn’t a JSON string work?

That explains raw strings, but not the JSON string. It’s definitely baffling that a JSON string posted with an application/json content type doesn’t work. So if you POST something like this:

POST http://dev.west-wind.com/aspnetwebapi/samples/PostJsonString HTTP/1.1
Host: dev.west-wind.com
Content-type: application/json; charset=utf-8 
Content-Length: 24

"Posting a JSON string."

this is a valid JSON request, but it still fails to map.

[FromBody] to retrieve Content

I mentioned hints, and Web API allows you to use parameter binding attributes to provide these hints. These allow you to tell Web API where the content is coming from explicitly. There are [FromBody] and [FromUri] attributes that can force content to be mapped from POST or query string content for example.

Using [FromBody] like this:

[HttpPost]
public string PostJsonString([FromBody] string text)
{
    return text;
}

now allows JSON or XML content to be mapped from the body. Same goes for other simple parameter types like numbers, dates, bools etc.

So if you now post:

POST http://rasxps/aspnetwebapi/samples/PostRawBuffer HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: rasxps
Content-Length: 22
 
"Hello World Suckers."

you now get the raw parameter mapped properly because the input string is in JSON format.

[FromBody] works for properly formatted content – ie. JSON, XML and whatever other media formatters that are configured in the Conneg pipeline. It requires that the data is formatted in JSON or XML. [FromBody] also works with a single POST form variable in urlencoded form data, but because it only works with a single parameter it’s kind of limited for that.

But [FromBody] still doesn’t work if you just want to capture the entire raw content – so if the data is not JSON or XML encoded and you just want the raw data [FromBody] is no help.

Capturing the raw Content

Without extending Web API with custom parameter bindings, you can capture the raw HTTP request content, but it doesn’t capture the raw content to a parameter. This is pretty easy to do with code like this:

[HttpPost]
public async Task<string> PostRawBufferManual()
{
    string result = await Request.Content.ReadAsStringAsync();            
    return result;
}

There are additional overloads to capture the raw Request content in various formats like byte[] or stream so it’s easy to read the data however you like it.

This solves the problem, but it’s not quite as expressive as having a typed parameter filled describing what’s happening in this operation at a glance. Nor does the parameter show up in the WebAPI controller documentation.

Creating a [NakedBody] custom Attribute

So in order to capture raw content into a parameter we have to take parameter binding process into our own hands. This solution is similar to the way [FromBody] works by applying an attribute to a single string or byte array parameter. This is a little bit more involved, but it’s generic tooling that you can reuse on any method in any application once created.

In order to accomplish this we need to create two components:

  • A custom parameter binding class
  • A [NakedBody] attribute that hooks up the binding to a parameter

Creating the NakedBodyParameterBinding

The first thing needed is a parameter binding that can pull the data from the Request content, and feed it to the parameter.

Parameter bindings are applied against each of the parameters in any Web API controller method. Each parameter is automatically mapped to a parameter binding typically based on the Conneg algorithm that determines the parsing and formatting pipeline. There are default bindings that process on standard Conneg media types and handle ModelBinding or raw data binding from JSON/XML etc. objects directly to parameters.

Using attributes  like [FromBody], [FromUri] or this [NakedBody] override the default parameter bindings and explicitly allow binding custom parameter bindings to a given parameter. Parameter bindings are called when Web API parses the controller method signature and goes through each of the parameters one by one to unbind the request content into each of the parameters if possible.

Here’s what the NakedParameterBinding() class looks like:

/// <summary>
/// Reads the Request body into a string/byte[] and
/// assigns it to the parameter bound.
/// 
/// Should only be used with a single parameter on
/// a Web API method using the [NakedBody] attribute
/// </summary>
public class NakedBodyParameterBinding : HttpParameterBinding
{
    public NakedBodyParameterBinding(HttpParameterDescriptor descriptor)
        : base(descriptor)
    {

    }


    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                                                HttpActionContext actionContext,
                                                CancellationToken cancellationToken)
    {
        var binding = actionContext
            .ActionDescriptor
            .ActionBinding;

        if (binding.ParameterBindings.Length > 1 || 
            actionContext.Request.Method == HttpMethod.Get)            
            return EmptyTask.Start();            

        var type = binding
                    .ParameterBindings[0]
                    .Descriptor.ParameterType;

        if (type == typeof(string))
        {
            return actionContext.Request.Content
                    .ReadAsStringAsync()
                    .ContinueWith((task) =>
                    {
                        var stringResult = task.Result;
                        SetValue(actionContext, stringResult);
                    });
        }
        else if (type == typeof(byte[]))
        {
            return actionContext.Request.Content
                .ReadAsByteArrayAsync()
                .ContinueWith((task) =>
                {
                    byte[] result = task.Result;
                    SetValue(actionContext, result);
                });
        }

        throw new InvalidOperationException("Only string and byte[] are supported for [NakedBody] parameters");
    }

    public override bool WillReadBody
    {
        get
        {
            return true;
        }
    }
}

The workhorse method of a parameter binding is the ExecuteBindingAsync() method which handles conversion of the parameter. Of the parameters passed the actionContext is vital in that it provides us the information needed to determine if the parameter should be handled and what the type of the parameter is. In this case we need to check that the request is not a GET request and that there’s only a single string or byte[] parameter passed – otherwise this binding is ignored.

If those requirements are fulfilled we can go ahead and read the request body either as a string or byte buffer and then call SetValue() on the ParameterBiding() to assign the value to the parameter.

The most complex thing about this code is the async logic as couple of Task operations are strung together – first reading the request context, then picking up the result and assigning it with the Task’s result string or byte[] values. We also need an ‘empty’ task to return when we just want to continue on without processing anything since the ExecuteBindingAsync method always has to return a task and you can’t return null or else you get a server exception. Here’s my EmptyTask class:

/// <summary>
/// A do nothing task that can be returned
/// from functions that require task results
/// when there's nothing to do.
/// 
/// This essentially returns a completed task
/// with an empty value structure result.
/// </summary>
public class EmptyTask
{
    public static Task Start()
    {
        var taskSource = new TaskCompletionSource<AsyncVoid>();
        taskSource.SetResult(default(AsyncVoid));
        return taskSource.Task as Task;
    }

    private struct AsyncVoid
    {
    }
}

Creating the [NakedBody] Attribute

In order to attach the NakedBodyParameterBinding() we need a mechanism to let Web API know that a parameter is requesting this binding. I played around with just applying this binding as a default binding as the very last binding, but there that didn’t work reliably because Web API would choke before ever getting to this point if the content type didn’t match one of the media formatters installed. So an explicit attribute seems to be the only way this can work reliably.

Creating the attribute is easy enough:

/// <summary>
/// An attribute that captures the entire content body and stores it
/// into the parameter of type string or byte[].
/// </summary>
/// <remarks>
/// The parameter marked up with this attribute should be the only parameter as it reads the
/// entire request body and assigns it to that parameter.    
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public sealed class NakedBodyAttribute : ParameterBindingAttribute
{
    public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
    {
        if (parameter == null)
            throw new ArgumentException("Invalid parameter");

        return new NakedBodyParameterBinding(parameter);
    }
}

The NakedBodyAttribute inherits from the ParameterBindingAttribute class and it’s sole purpose is to dynamically determine the binding that is to be used on that parameter. All that needs to happen is creating an instance of our NakedBodyParameterBinding class described above and passing the parameter in.

Using the [NakedBody] Parameter Attribute

To use the attribute is pretty easy. Simply create a method with a single string or byte[] parameter and mark it up with the [NakedBody] attribute:

[HttpPost]
public string PostRawBuffer([NakedBody] string raw)
{
    return raw;
}

[HttpPost]
public string PostBinaryBuffer([NakedBody] byte[] raw)
{
    return raw.Length + " bytes sent";
}

You can now send data to the first one using this HTTP trace. Here’s the string version trace of the request data:

POST http://dev.west-wind.com/aspnetwebapi/samples/PostRawBuffer HTTP/1.1
Content-Type: text/plain; charset=utf-8
Host: dev.west-wind.com
Content-Length: 30
Connection: Keep-Alive

This is a raw string buffer...

In this case you get a the original string back as a JSON response – JSON because the default format for Web API is JSON. BTW, I noticed that if omitted Accept headers and I used a Content-Type of text/xml the response would be XML. If the content type is application/json or any non-mapped media format, JSON is returned. News to me that the input Content type had an impact on output format, but it appears that it does in some cases when an Accept header is missing.

Web API should address some of these simple Use Cases

The  good news is that solutions like this to work around some of the simple limitations of Web API are possible and not very complex. While these types of solutions often are not very discoverable, they are relatively easy to implement if you poke around a little. To create this [NakedBodyAttribute] I basically looked at the [FromBody] attribute and followed the pattern laid out there to create the attribute and parameter binding which is fairly simple. As you can see the solution is really just a few lines of code in a couple of classes. But… finding out exactly what to do for this is not exactly easy to discover. Luckily you won’t have to since it’s done for you with the code here 😃

But it does gall a bit that some of these simple scenarios are not addressed out of the box. While these are not the most practical operations, they are the kinds of things that new users typically want to try first. I can’t tell you how often I’ve forgotten to use [FromUri] for example when I wanted parameters mapped from the query string, or when wanted to pick up 2 or 3 form variables without having to create a new class just to model bind those 2 or 3 values or as described here when I wanted to capture the raw request input. Instead bindings are simply falling through and returning null or empty values – this simply shouldn’t happen in most cases. Attributes are a reasonable solution, but again it’s not really discoverable if you’re just starting out and it’s not the easiest to find out about since it’s considered a specialty scenario.

I suspect there are good reasons why these use cases are not ‘in the box’ – performance for one as mapping these scenarios requires reading the entire content buffer up front to determine whether parameters can actually be bound. But still… this should be addressed in some way in the future to make the ‘first time tire kicking experience’ easier and more rewarding.

Posted in Web Api  

The Voices of Reason


 

Steve
December 13, 2013

# re: Accepting Raw Request Body Content with ASP.NET Web API

Thanks much for this, very helpful.

I suspect that the dev group working on Web API touched on this and said, "Why would anyone do a simple string? That's so un-cool." and left it out.

Vince
December 14, 2013

# re: Accepting Raw Request Body Content with ASP.NET Web API

Even I asked myself why anyone would want this but I'm sure there's a use case somewhere.

Even so I learned a little more about parameter bindings. Thanks Rick.

Rick Strahl
December 14, 2013

# re: Accepting Raw Request Body Content with ASP.NET Web API

@Vince - Think of this as a raw HTTP handler type input. Web API is being pitched as the solution for all non-HTML data situations and raw data is not uncommon at call. Any kind of 'raw data' upload would have to be handled this way. If you think of Web API as a generic HTTP solution, then yes, I think this is not an uncommon scenario.

frantisek
January 02, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

if content body starts with "=", then binding to raw string works out of the box, IMHO

Rick Strahl
January 02, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

@frantisek - Ah yes, forgot about that, but that's not very useful since it's some oddball format. If you're building an API would you really ever ask anyone, but when you send your content just add an = at the front? :-) I don't even consider that a feature...

Tim Hardy
April 07, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

The following...
[HttpPost]
public string PostJsonString([FromBody] string text)


does NOT work with property formatted json...
{ "name": "widget", "description":"some description" }

It always returns null for text, and it's very frustrating. That's with Content-Type: application/json; While "Posting a JSON string" might be a valid json request, I've never seen one that's a simple string. Practical json requests, anything starting with a curly brace, don't work.

It works if you put an = in front of the first curly brace, but I'm not going to change the client code to hack that in. The only way I can get dynamic json to be accepted in a web api controller is to accept it as a JObject, and incur a couple of extra transformations that I'd rather avoid.

I'll try your NakedBody attribute, but I'd like something that at least validates the body is valid json. It's crazy to me that this isn't possible in Web Api out of the box.

Rick Strahl
April 07, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

@Tim - That makes no sense to me. If you want the raw body why would you want to validate the JSON? You get a string or raw buffer because that's what you're asking for with 'raw' data. If you want validation for your JSON match it to an object or use dynamic and get back a JsonValue object you can iterate through without typing.

Mick
April 17, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

I change this code to put more parameters.

In NakedBodyParameterBinding in ExecuteBindingAsync

I change:
if (binding.ParameterBindings.Length > 1 ||
actionContext.Request.Method == HttpMethod.Get)
return EmptyTask.Start();

var type = binding
.ParameterBindings[0]
.Descriptor.ParameterType;


To

if (binding.ParameterBindings.Length == 0 ||
actionContext.Request.Method == HttpMethod.Get)
return EmptyTask.Start();

var parameter = binding.ParameterBindings.FirstOrDefault(x => x.Descriptor.ParameterBinderAttribute !=null && x.Descriptor.ParameterBinderAttribute.GetType() == typeof(NakedBodyAttribute));
if(parameter ==null)
return EmptyTask.Start();
var type = parameter.Descriptor.ParameterType;

Isaac
July 31, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

Small adjustment to fix issues where the current thread values (Principal, etc.) are being lost before it gets to the controller.

Instead of:

return actionContext.Request.Content
                    .ReadAsStringAsync()
                    .ContinueWith((task) =>
                    {
                        var stringResult = task.Result;
                        SetValue(actionContext, stringResult);
                    });


Do this:

var rawData = actionContext.Request.Content.ReadAsStringAsync().Result;
SetValue(actionContext, rawData);
return EmptyTask.Start();

Highdown
September 21, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

I have been reading your posts on Web API, and they have been very helpful in solving some of my client/server communication issues. Here are two other sites that I have garnered information to deal with uploading/downloading byte[]'s.

http://byterot.blogspot.com/2012/04/aspnet-web-api-series-part-5.html

http://stackoverflow.com/questions/9541351/returning-binary-file-from-controller-in-asp-net-web-api

I am using TypeScript/AngularJS on the client, and there wasn't much information readily available for something as simple as a byte[] upload/download with Web API. I was used to Silverlight and WCF RIA Services where you could easily upload strings, numbers, and multiple byte arrays in a single server upload.

I have finally been able to get a working client/server setup to upload/download a single byte[]. However, what I was hoping to achieve was to be able to upload more than one byte array at the same time. I do not think the Web API technology is designed to support this, but I could be wrong. Any thoughts?

What I may do is to merge my byte arrays on the client and then break them apart on the server. This is a little messier, but so far it is the only option that I have come up with.

Anyway, thanks for all your posts.

Regards...

Chandana
November 11, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

Thanks a ton!! works perfectly for my need.

Phil
November 14, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

This is cool and helped me handle this scenario, but this whole block is unnecessary, as well as the empty task class.

var binding = actionContext
            .ActionDescriptor
            .ActionBinding;
 
        if (binding.ParameterBindings.Length > 1 || 
            actionContext.Request.Method == HttpMethod.Get)            
            return EmptyTask.Start();            
 
        var type = binding
                    .ParameterBindings[0]
                    .Descriptor.ParameterType;


1. Assuming it can be the only parameter period is false - A FromUri parameter, or any other parameter whose binding returns false for WillReadBody, should work fine alongside this. (note that if you pair this with FromBody, instead of failing gracefully like this attempts the controller just refuses to bind)
2. Hardcoding GET as not working with this is kind of unnecessary.
3. The type parameter is searching through an array for, what ends up being, "this".
You can replace it with
var type = Descriptor.ParameterType;

Rob
November 21, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

I like it. Can this be done in VS2008 with .NET framework 3.5 or are more current versions required? Thanks

Rick Strahl
November 21, 2014

# re: Accepting Raw Request Body Content with ASP.NET Web API

@Rob - You need .NET 4.0 at least.

Hemang
January 28, 2015

# re: Accepting Raw Request Body Content with ASP.NET Web API

I am a newbie to angular and wanted your help on consuming the following method using $resource service instead of $http from my angular script, how can I achieve this?

[HttpPost]
public string PostJsonString([FromBody] string text)
{
return text;
}

Thanks in advance.

Gang Luo
November 26, 2015

# re: Accepting Raw Request Body Content with ASP.NET Web API

It's really helpful. Thanks for sharing.
Add two my cents, it's not necessary to limit this to the only parameter since developers may need pass other parameters from the Url's query string. EmptyTask is not necessary. My version is as below, it works for me.
    public class RawHttpReqeustBodyParameterBinding : HttpParameterBinding
    {
        public RawHttpReqeustBodyParameterBinding(HttpParameterDescriptor descriptor)
            : base(descriptor) { }
 
        public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                                                    HttpActionContext actionContext,
                                                    CancellationToken cancellationToken)
        {
            if (actionContext.Request.Method == HttpMethod.Get)
            {
                SetValue(actionContext, null);
 
                var tsc = new TaskCompletionSource<object>();
                tsc.SetResult(null);
                return tsc.Task;
            }
            else if (Descriptor.ParameterType == typeof(string))
            {
                return actionContext.Request.Content
                        .ReadAsStringAsync()
                        .ContinueWith((task) =>
                        {
                            var stringResult = task.Result;
                            SetValue(actionContext, stringResult);
                        });
            }
            else if (Descriptor.ParameterType == typeof(byte[]))
            {
                return actionContext.Request.Content
                    .ReadAsByteArrayAsync()
                    .ContinueWith((task) =>
                    {
                        byte[] result = task.Result;
                        SetValue(actionContext, result);
                    });
            }
 
            throw new InvalidOperationException("Only string and byte[] are supported for [RawHttpRequestBody] parameters");
        }
 
        public override bool WillReadBody
        {
            get
            {
                return true;
            }
        }
    }


Usage:
public HttpResponseMessage DoSmoething(string para1, [RawHttpRequestBody] string body = null, string para2, ...)
{
}

AK3800
December 16, 2015

# re: Accepting Raw Request Body Content with ASP.NET Web API

I've been using this for a while and it has been working fine as long as the parameter marked with this attribute is the only parameter, but now I have a need to do this along with another parameter from the URI. @Gang Luo and @Phil, how are you getting your versions to work with additional parameters. I tried making the changes as you suggested, at least the routing made it to the method but when I try to access the other variable that was part of the route, I get this error:
Cannot obtain value of the local variable or argument because it is not available at this instruction pointer, possibly because it has been optimized away.


Here's a snippet of the method where I'm using this:
      <Route("audit/lastxdays/{days:int}")>
      <HttpPost>
      Public Function GetOppAuditLastXDays(days As Integer, <RawContent> fieldlist As String) As HttpResponseMessage

In this example, the days parameter gives me the error when I try to access it, but the fieldlist parameter is available as expected. Thoughts?

Bradley
January 07, 2016

# re: Accepting Raw Request Body Content with ASP.NET Web API

What you you suggest for binding httpFile? WebApi doesn't seem to understand how to accept mutli/part data. I understand I can access this data in HttpContext.Current.Request.Files or MultipartFormDataStreamProvider, but neither of these approaches seem very web api because they undo some of the magic model binding. In mvc, I used the signature upload(int id, httpFile file), but again this does not work for web api. It seems like you could write a custom mediaformatter, but I have doubts about going down that path. Any thoughts Rick? You always seem to have the best answers for the more complicated stuff? Thanks,
Bradley

jeremy
June 02, 2016

# re: Accepting Raw Request Body Content with ASP.NET Web API

this works great, but is there a way I can have multiple parameters??? I have tinkered with the code, and i can't even enter the ExecuteBindingAsync with more than 1 parameter otherwise I think I could get it to work

Rick Strahl
June 03, 2016

# re: Accepting Raw Request Body Content with ASP.NET Web API

@Jeremy - Web API doesn't make that easy. Wrote a separate post about accepting multiple POST paramters as parameters rather than objects: http://weblog.west-wind.com/posts/2012/May/08/Passing-multiple-POST-parameters-to-Web-API-Controller-Methods

Alastair
August 11, 2016

# re: Accepting Raw Request Body Content with ASP.NET Web API

Thanks for your blogs - always like reading yours Rick.

I may have misread and I realise this is from 2013 and WebApi has come on since then, but I landed here because I'm trying to not only capture the raw request payload (for logging purposes) but I want to do that transparently to the rest of the pipeline so that I still make use of model binding.

What I suspect I'll end up doing is hooking into the default binder implementation and extend / override it but ignoring my particular model binding requirement, I was able to get at the raw http request by using the following signature:

public void FooEvent(HttpRequestMessage request)


Then I just call the ReadAsStringAsync() method on the request.Content property.

string requestContent = request.Content.ReadAsStringAsync().Result;


(Yes I know I'm blocking :) )

I'm actually posting XML to this endpoint but you can post anything you want.

At first glance that appears to be what you're doing here but as I say I may have misread and that was a comparatively long time ago :) I also think your custom attribute is more elegant and expressive.

My sample is from an OWIN-hosted WebApi so not sure if the objects are the same as your article - the HttpRequestMessage object is in the System.Net.Http namespace.

Good stuff!
Alastair

Erik
February 09, 2017

# re: Accepting Raw Request Body Content with ASP.NET Web API

Hello @RickStrahl nice post, I'm new in the .NET world. I can't read the body content from a Request(System.Web...) object. always is null. I've been trying different ways, is an MVC Project created in Xamarin. The thing is that the JSON sended in the POST request may has differents properties.


Kashif
August 30, 2017

# re: Accepting Raw Request Body Content with ASP.NET Web API

Excellent post, is there anything similar available for .net core.


Kashif
September 10, 2017

# re: Accepting Raw Request Body Content with ASP.NET Web API

Hi, I have written an post for accepting raw request body content with asp.net web api .net core 2 https://medium.com/@kashifsoofi/accepting-raw-request-body-content-with-asp-net-core-2-web-api-ba8f0072a0eb.


Daniel
November 15, 2017

# re: Accepting Raw Request Body Content with ASP.NET Web API

Hi Rick, thanks for this article. Works for me. I would like to use more than one parameter for the method besides [NakedBody].

I.e, I want to do something like this:

public string Operation([NakedBody] string body, string other, int other, ...){...}

Any way to do this? I know you stated explicitly that you can only use 1 parameter (the NakedBody parameter), but I was hoping to do something like this.

Currently if I try something like this I get the response:

{
    "Message": "The request is invalid.",
    "MessageDetail": "The parameters dictionary does not contain an entry for parameter 'body' of type 'System.String' for method 'System.String GetLicense(System.Net.Http.HttpRequestMessage, AbacusLicensingServer.Providers.ProductName, System.String, System.String)' in 'AbacusLicensingServer.Controllers.LicensingController'. The dictionary must contain an entry for each parameter, including parameters that have null values."
}

Thanks

Daniel


John Dana
January 01, 2018

# re: Accepting Raw Request Body Content with ASP.NET Web API

Hi Rick,

Terrific article. Not only is it informative, but your relaxed style of prose makes it an easy read. I'm using an ASP.NET Web API between an Ember.js UI and an MSSQL DB. Ember.js now uses the JSON API standard for JSON, and while I think the JSON API (or something like it) is the direction the development community is headed, it feels like round peg / square hole development right now. The number of tweaks I'm having to make to make it work just right is becoming overwhelming. Anyway, thanks for the great post. (The windsurfer image is impressive.)

John


Khadija
June 14, 2018

# re: Accepting Raw Request Body Content with ASP.NET Web API

Thank you for your explanation and also your captivating style. It answered perfectly to my use case (sent a text not a file in CSV format).


Mark Landgraf
July 20, 2018

# re: Accepting Raw Request Body Content with ASP.NET Web API

Thank you so much! I'm going to these insights in dealing with OData v4 in Microsoft.OData.Client's DataServiceContext.SaveChangesAsync()'s insistence on using the odata.bind annotation in a json body when POSTing a new record with a related entity. Currently (07/2018) Web API doesn't support odata.bind on the server side. So now, I can inspect a json body and extract a parent entity's ID from the odata.bind annotation for a new child's foreign key value.

Thanks again


Suren
July 26, 2018

# re: Accepting Raw Request Body Content with ASP.NET Web API

I needed to access the RAWS body of the message (JSON) as well. Found the code snippet below on another site and it works for me inside the default Post method generated by VS 2017.

var bodyStream = new System.IO.StreamReader(System.Web.HttpContext.Current.Request.InputStream); bodyStream.BaseStream.Seek(0, System.IO.SeekOrigin.Begin); string bodyText = bodyStream.ReadToEnd();

Anyone tried this and see any issues with it?


Itai Roded
June 05, 2019

# re: Accepting Raw Request Body Content with ASP.NET Web API

Hi Suran both worked! This works var bodyStream = new System.IO.StreamReader(System.Web.HttpContext.Current.Request.InputStream); bodyStream.BaseStream.Seek(0, System.IO.SeekOrigin.Begin); string bodyText = bodyStream.ReadToEnd();

Also the nakedbody attribute code. Thank you BOTH!


Mehdi
July 12, 2021

# re: Accepting Raw Request Body Content with ASP.NET Web API

it does not work for me I use .net 5 web api Error: "The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1." Do you have any solutions


Rick Strahl
July 12, 2021

# re: Accepting Raw Request Body Content with ASP.NET Web API

@Mehdi - .NET Core requires different code. Check this post instead:

Accepting Raw Request Body Content in ASP.NET Core API Controllers


Chuck Dee
November 23, 2021

# re: Accepting Raw Request Body Content with ASP.NET Web API

I added the following:

        if (type == typeof(Stream))
        {
            return actionContext.Request.Content
            .ReadAsStreamAsync()
            .ContinueWith((task) =>
            {
                SetValue(actionContext, task.Result);
            });
        }

This works, but there's an annoying instance where sometimes it does not- the stream is zero bytes when I get here. It seems to be based on web server configuration, but I can't figure out what it could be. Any ideas on what I might be missing?


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024