Posting raw JSON to Web API

Tags: WebApi

I see questions almost weekly on StackOverflow where someone is trying to POST JSON and receive it as a string.  The problem is that Web API has two modes, “serialized object” and “HTTP message”.  Receiving raw JSON as a string falls between the two.

image

Usually the question goes something like, “Why does the jsonBody parameter in the following controller method always contain null when I POST a JSON document?” 

 

 
public HttpResponseMessage Post([FromBody]string jsonBody)
{
    // Do something with the string here
 
    return new HttpResponseMessage(HttpStatusCode.Created);
}

Before I go into why it doesn’t work, let me first try and justify why someone might want to get access to the string instead of just letting Web API convert the JSON into an instance a corresponding model object.  Sometimes it is useful to create endpoints where you can POST different shaped JSON documents to the same resource.  Or maybe you want to do some kind of pre-processing on the JSON before deserializing it.  Maybe you only want to extract a small subset of the data and ignore the rest.  I’m sure the many other justifications.

But why?

The reason the parameter is null is because when the Web API framework attempts to populate that jsonBody parameter,  it uses a JsonMediaTypeFormatter, which internally does the following,

JsonConvert.DeserializeObject<string>(jsonBody)

in order to try and convert the body into the method parameter.  The problem is that the only JSON document that will be handled correctly is one that looks like this,

“Hey I am a string

However, if you POST a JSON document that is an object or an array like,

{
   “message” : “Here is some text”
}

then the deserialize method will not be able to translate that into a string. and therefore the incoming parameter will be null.

HTTP everything!

So what is the solution.  As I mentioned before, Web API works in two modes.  You can go full HTTP on it and resolve the problem.

public async Task<HttpResponseMessage> Post(HttpRequestMessage request)
{
    var jsonString = await request.Content.ReadAsStringAsync();
            
    // Do something with the string 
return new HttpResponseMessage(HttpStatusCode.Created); }

With this solution, we can access the Content property of the HttpRequestMessage and read it as a string. The problem with this approach is that the ReadAs methods on HttpContent are all async, so in order to do this cleanly, we need to make the controller method async.  It seems like a lot of complication just to read a string.  Fortunately there is an easier way.

The magic type

The JSON.Net deserializer will happily convert any arbitrary JSON document into a JToken instance.

public HttpResponseMessage Post([FromBody]JToken jsonbody)
{
    // Process the jsonbody 

    return new HttpResponseMessage(HttpStatusCode.Created);
}

This gives you the JSON document as a DOM object.  If you really want it as a string, then a simple ToString() will do that.

XML too

This approach is useful for more than just incoming content that is application/json.  For request bodies that are application/xml you can use the XElement class to do pretty much the same thing. 

public HttpResponseMessage Post([FromBody]XElement xmlbody)
{
    // Process the xmlbody 
    return new HttpResponseMessage(HttpStatusCode.Created);
}

And HTML forms

The other media type that developers regularly run into is application/x-www-form-urlencoded.  This is the media type that is generated by a web browser when it submits a form using the POST method. 

This last example shows how to coerce Web API into translating the form body into a collection of form data values.  In this case, the magic class is called FormDataCollection.

public HttpResponseMessage Post([FromBody]FormDataCollection formbody)
{
    // Process the formbody 
    var field1Values = formbody.GetValues("field1");
    var field2Values = formbody.GetValues("field2");

    return new HttpResponseMessage(HttpStatusCode.Created);
}

Once you know the magic classes that correspond with the media types that you want to process, all is rosy!

 

Image credit : https://www.flickr.com/photos/piulet/

No Comments

Add a Comment

comments powered by Disqus