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 in ASP.NET Core API Controllers


:P
On this page:
continued from Page 1

A few years back I wrote a post about Accepting Raw Request Content with ASP.NET Web API. Unfortunately the process to get at raw request data is rather indirect, with no direct way to receive raw data into Controller action parameters and that hasn't really changed in ASP.NET Core's MVC/API implementation. The way the Conneg algorithm works in regards to generic data formats is roughly the same as it was with Web API.

The good news is that it's quite a bit easier to create custom formatters in ASP.NET Core that let you customize how to handle 'unknown' content types in your controllers.

Let's take a look.

Creating a Simple Test Controller

To check this out I created a new stock ASP.NET Core Web API project and changed the default ValuesController to this sample controller to start with:

public class BodyTypesController : Controller { }

JSON String Input

Lets start with a non-raw request, but rather with posting a string as JSON since that is very common. You can accept a string parameter and post JSON data from the client pretty easily.

So given this endpoint:

[HttpPost]
[Route("api/BodyTypes/JsonStringBody")]
public string JsonStringBody([FromBody] string content)
{
    return content;
}

I can post the following:

Figure 1 - JSON String inputs thankfully capture as strings in ASP.NET Core

This works to retrieve the JSON string as a plain string. Note that the string sent is not a raw string, but rather a JSON string as it includes the wrapping quotes:

"Windy Rivers are the Best!"

Don't Forget [FromBody]

Make sure you add [FromBody] to any parameter that tries to read data from the POST body and maps it. It's easy to forget and not really obvious that it should be there. I say this because I've forgotten it plenty of times and scratched my head wondering why request data doesn't make it to my method or why requests fail outright with 404 responses.

No JSON - No Workey

If you want to send a RAW string or binary data and you want to pick that up as part of your request things get more complicated. ASP.NET Core handles only what it knows, which by default is JSON and Form data. Raw data is not directly mappable to controller parameters by default.

So if you trying to send this:

POST http://localhost:5000/api/BodyTypes/JsonPlainBody HTTP/1.1
Accept-Encoding: gzip,deflate
User-Agent: West Wind HTTP .NET Client
Content-Type: text/plain
Host: localhost:5000
Content-Length: 28
Expect: 100-continue

Windy Rivers are the best!

to this controller action:

[HttpPost]
[Route("api/BodyTypes/PlainStringBody")]
public string PlainStringBody([FromBody] string content)
{
    return content;
}

The result is a 404 Not Found.

I'm essentially doing the same thing as in the first request, except I'm not sending JSON content type but plain text. The endpoint exists, but MVC doesn't know what to do with the text/plain content or how to map it and so it fails with a 404 Not Found.

It's not super obvious and I know this can trip up the unsuspecting Newbie who expects raw content to be mapped. However, this makes sense if you think about it: MVC has mappings for specific content types and if you pass data that doesn't fit those content types it can't convert the data, so it assumes there's no matching endpoint that can handle the request.

So how do we get at the raw data?

##AD##

Reading Request.Body for Raw Data

Unfortunately ASP.NET Core doesn't let you just capture 'raw' data in any meaningful way just by way of method parameters. One way or another you need to do some custom processing of the Request.Body to get the raw data out and then deserialize it.

You can capture the raw Request.Body and read the raw buffer out of that which is pretty straight forward.

The easiest and least intrusive, but not so obvious way to do this is to have a method that accepts POST or PUT data without parameters and then read the raw data from Request.Body:

Read a String Buffer
[HttpPost]
[Route("api/BodyTypes/ReadStringDataManual")]
public async Task<string> ReadStringDataManual()
{
    using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
    {  
        return await reader.ReadToEndAsync();
    }
}

This works with the following HTTP and plain text content:

POST http://localhost:5000/api/BodyTypes/ReadStringDataManual HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/plain
Host: localhost:5000
Content-Length: 37
Expect: 100-continue
Connection: Keep-Alive

Windy Rivers with Waves are the best!

To read binary data you can use the following:

Read a Byte Buffer
[Route("api/BodyTypes/ReadBinaryDataManual")]
public async Task<byte[]> RawBinaryDataManual()
{
    using (var ms = new MemoryStream(2048))
    {
        await Request.Body.CopyToAsync(ms);
        return  ms.ToArray();  // returns base64 encoded string JSON result
    }
}

which works with this HTTP:

POST http://localhost:5000/api/BodyTypes/ReadBinaryDataManual HTTP/1.1
Accept-Encoding: gzip,deflate
User-Agent: West Wind HTTP .NET Client
Content-Type: application/octet-stream
Host: localhost:5000
Content-Length: 40
Expect: 100-continue
Connection: Keep-Alive

Wind and Water make the world go 'round.

I'm sending a string here to make it readable, but really the content could be raw binary byte data - it doesn't matter what the content is in this case but it should be considered as binary data.

Running this results in:

Figure 2 - Capturing raw binary request data.

The result in the code is captured as binary byte[] and returned as JSON, which is why you see the base64 encoded result string that masquerades as a binary result.

Request Helpers

If you do this a lot a couple of HttpRequest extension methods might be useful:

public static class HttpRequestExtensions
{

    /// <summary>
    /// Retrieve the raw body as a string from the Request.Body stream
    /// </summary>
    /// <param name="request">Request instance to apply to</param>
    /// <param name="encoding">Optional - Encoding, defaults to UTF8</param>
    /// <returns></returns>
    public static async Task<string> GetRawBodyStringAsync(this HttpRequest request, Encoding encoding = null)
    {
        if (encoding == null)
            encoding = Encoding.UTF8;

        using (StreamReader reader = new StreamReader(request.Body, encoding))
            return await reader.ReadToEndAsync();
    }

    /// <summary>
    /// Retrieves the raw body as a byte array from the Request.Body stream
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static async Task<byte[]> GetRawBodyBytesAsync(this HttpRequest request)
    {
        using (var ms = new MemoryStream(2048))
        {
            await request.Body.CopyToAsync(ms);
            return ms.ToArray();
        }
    }
}

Listing 1 - HttpRequest Extensions to retrieve raw body string and byte data. Github

which allows you to simplify those two previous controller methods to:

[HttpPost]
[Route("api/BodyTypes/ReadStringDataManual")]
public async Task<string> ReadStringDataManual()
{
    return await Request.GetRawBodyStringAsync();
}

[HttpPost]
[Route("api/BodyTypes/ReadBinaryDataManual")]
public async Task<byte[]> RawBinaryDataManual()
{
    return await Request.GetRawBodyBytesAsync();
}

##AD##

Automatically Converting Binary and Raw String Values

If you'd rather use a more deterministic approach and accept raw data through parameters, a little more work is required by building a custom InputFormatter.

Create an MVC InputFormatter

ASP.NET Core has a clean and more generic way to handle custom formatting of content using an InputFormatter. Input formatters hook into the request processing pipeline and let you look at specific types of content to determine if you want to handle it. You can then read the request body and perform your own deserialization on the inbound content.

There are a couple of requirements for an InputFormatter:

  • You need to use [FromBody] to get it fired
  • You have to be able to look at the request and determine if and how to handle the content

So in this case for 'raw content' I want to look at requests that have the following content types:

  • text/plain (string)
  • application/octet-stream (byte[])
  • No content type (string)

You can add others to this list or check other headers to determine if you want to handle the input but you need to be explicit what content types you want to handle.

To create a formatter you either implement IInputFormatter or inherit from InputFormatter. The latter is usually the better approach, and that's what I used to create RawRequestBodyFormatter:

/// <summary>
/// Formatter that allows content of type text/plain and application/octet stream
/// or no content type to be parsed to raw data. Allows for a single input parameter
/// in the form of:
/// 
/// public string RawString([FromBody] string data)
/// public byte[] RawData([FromBody] byte[] data)
/// </summary>
public class RawRequestBodyFormatter : InputFormatter
{
    public RawRequestBodyFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/octet-stream"));
    }


    /// <summary>
    /// Allow text/plain, application/octet-stream and no content type to
    /// be processed
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override Boolean CanRead(InputFormatterContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));

        var contentType = context.HttpContext.Request.ContentType;
        if (string.IsNullOrEmpty(contentType) || contentType == "text/plain" ||
            contentType == "application/octet-stream")
            return true;

        return false;
    }

    /// <summary>
    /// Handle text/plain or no content type for string results
    /// Handle application/octet-stream for byte[] results
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var request = context.HttpContext.Request;
        var contentType = context.HttpContext.Request.ContentType;


        if (string.IsNullOrEmpty(contentType) || contentType == "text/plain")
        {
            using (var reader = new StreamReader(request.Body))
            {
                var content = await reader.ReadToEndAsync();
                return await InputFormatterResult.SuccessAsync(content);
            }
        }
        if (contentType == "application/octet-stream")
        {
            using (var ms = new MemoryStream(2048))
            {
                await request.Body.CopyToAsync(ms);
                var content = ms.ToArray();
                return await InputFormatterResult.SuccessAsync(content);
            }
        }

        return await InputFormatterResult.FailureAsync();
    }
}

Listing 2 - InputFormatter to handle Raw Request inputs for selected content types. GitHub

The formatter uses CanRead() to check requests for content types to support and then the ReadRequestBodyAsync() to read and deserialize the content into the result type that should be returned in the parameter of the controller method.

The InputFormatter has to be registered with MVC in the ConfigureServices() startup code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(o => o.InputFormatters.Insert(0, new RawRequestBodyFormatter()));
}

Accepting Raw Input

With the formatter hooked up to the MVC formatter list you can now handle requests that POST or PUT to the server using text/plain, application/octet-stream or no content types.

Raw String

[HttpPost]
[Route("api/BodyTypes/RawStringFormatter")]        
public string RawStringFormatter([FromBody] string rawString)
{
    return rawString;
}

and you can post to it like this:

POST http://localhost:5000/api/BodyTypes/RawStringFormatter HTTP/1.1
Accept-Encoding: gzip,deflate

Raw Wind and Water make the world go 'round.

or

POST http://localhost:5000/api/BodyTypes/RawStringFormatter HTTP/1.1
Accept-Encoding: gzip,deflate
Content-type: text/plain

Raw Wind and Water make the world go plain.

The controller will now pick up the raw string text.

Note that you can call the same controller method with a content type of application/json and pass a JSON string and that will work as well. The RawRequestBodyFormatter simply adds support for the additional content types it supports.

Binary Data

Binary data works the same way but with a different signature and content type for the HTTP request.

[HttpPost]
[Route("api/BodyTypes/RawBytesFormatter")]
public byte[] RawBytesFormatter([FromBody] byte[] rawData)
{
    return rawData;
}  

and this HTTP request data with 'binary' content:

POST http://localhost:5000/api/BodyTypes/RawBytesFormatter HTTP/1.1
Accept-Encoding: gzip,deflate
Content-type: application/octet-stream

Raw Wind and Water make the world go 'round.

Again I'm sending a string to provide something readable here, but the string is treated as binary data by the method and returned as such as shown in Figure 2.

Source Code provided

If you want to play with this stuff and experiment, I've uploaded my sample project to Github:

The sample HTTP requests are setup in West Wind Web Surge and ready to test against or you can just use the BodyTypes.websurge file and pick out the raw HTTP request traces.

Reading Request.Body Multiple times

If you need to read Request.Body multiple times you'll find that you can only read request data once and get data back. That's because the Request.Body stream is read-once by default. It's not often that this is needed but it comes up if you need to do custom logging or auditing that requires capturing or accessing data from the incoming POST data.

For more information on how you can read Request.Body multiple times please see this follow up post:

Summary

Accepting raw data is not something you have to do all the time, but occassionally it is required for API based applications. ASP.NET MVC/Web API has never been very direct in getting at raw data, but once you understand how the pipeline manages request data and deals with content type mapping it's easy to get at binary data.

In this post I showed two approaches:

  • Manually grabbing the Request.Body and deserializing from there
  • Using a custom InputFormatter that looks at typical 'raw' Content data types

The former is easy to use but doesn't describe the API behavior via the method interface. The latter is a little more work and requires hooking up a custom formatter, but it allows keeping the API's contract visible as part of the controller methods which to me simply feels cleaner.

All of this is making me hungry for some raw Sushi...

Resources

this post created and published with Markdown Monster
Posted in ASP.NET Core  

The Voices of Reason


 

Tony
September 21, 2017

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

Rick,

Is there any plan to make a .NET standard cross platform version of the West Wind WebSurge tool? It would be awesome to be able to use it on Mac and Linux.

Tony


Tracy Dyer
November 02, 2017

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

I cannot get post methods work at all in a core 2.0 app. All get requests work fine but when I try to post anything (using any of the examples you gave) I still get a 404 ... it's driving me crazy!


dapug
November 17, 2017

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

Have you tried forcing a format for a POST? I know this works on a GET:

[Produces("text/plain")]

This way MVC will use the right Content-Type instead of whatever it automagically thinks is best based off the type of client.

See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/formatting


Rick Strahl
November 18, 2017

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

@dapug - I haven't used it but just recently discovered that this option is available. Could be useful for some things but i think I'd rather be explicit in code and decide what gets sent back.


CharlesO
December 01, 2017

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

Hi rick,

How would you adapt this to handle a plain int posted to an endpoint?

Being that a plain int value is indeed valid JSON, i would expect the following to just work

    [HttpPost]
    [Route("reset")]
    public async Task<IActionResult> Reset([FromBody] int id)
    {
      // do something with id      
      return NoContent();
    }

Rick Strahl
December 01, 2017

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

@Charles - the request content type will determine how the input data is handled. If it's application/json it'll be treated as JSON, otherwise the custom handlers will pick up as a string.


Sam
December 15, 2017

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

I'm not able to receive a json object as a string even though content-type is set to application/json. Why would this happen?

Specifically, this is happening while implementing Stripe into my ASP.NET Core API backend. Stripe takes credit card data entered by user and creates what they call a "token" which is a json object. When I send it to my API method, I'm able to hit the method but the string parameter ends up with a null value.

[HttpPost]
public async Task<IActionResult> Process([FromBody] string token)
{
   // I'm able to hit this method but end up with token=null
}

Any idea why I can't receive a json object into a string?


Zeke B
December 17, 2017

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

Hi Rick Can you offer some advice. I've worked with REST API's before but I'm new to .NET. Can you recommend any resources for a beginner on the topic of building/consuming API's in .NET

Thanks


bob mazzo
January 29, 2018

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

hey rick, thanks for the tips in core 2.0. The reader.ReadToEndAsync() ended up helping me to read my formdata coming in from the front-end. For anyone interested, my SO post here: https://stackoverflow.com/questions/48470972/reading-formdata-in-net-core-2-0


Phil
February 05, 2018

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

I am trying to get the first example to work with a Post from a form where jquery converts the form variables into json but the value is always null. I do not understand how you were able to post to the first example successfully. Can you please show me in an html jquery form?


<form method="POST" id="myFrom">
     <input name="name1" id="name_id" type="text" />
     <input id="submitButton" type="button" value="button" />
</form>

 var postData = function () {
            //form encoded data
            var dataType = 'application/json; charset=utf-8';
            var data = $("#myForm").serializeObject();
            console.log($("#myForm").serialize());
            console.log($("#myForm").serializeArray());
            console.log($("#myForm").serializeObject());
            $.ajax({
                url: "/api/PostString",
                type: "POST",
                data: data,
                contentType: dataType,
                success: function (data, textStatus, jQxhr) {
                    console.log(data);//the returned json data
                },
                error: function (jqXhr, textStatus, data) {
                    alert('error', data);
                }
            });
        };

        document.getElementById('submitButton').addEventListener('click', function () {
            postData();
        }, false);

        [HttpPost]
        public string PostJson([FromBody]JsonObject  value)
        {
            return "a";
        }

        [HttpPost]
        public string PostString([FromBody]string value)
        {
            return "a";
        }

Rick Strahl
February 05, 2018

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

@Phil - you're posting form data not which has a specific content type that is handled by ASP.NET Core (ie. application/x-www-form-urlencoded) and treated like form data. What I describe here doesn't apply to that unless you extend the formatted to explicitly handle the form data content type (which it does not now).

However, if you're posting form data you should be able to use the Request.Form[] collection to retrieve individual values or the whole thing.


Juri P
April 13, 2018

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

Hi, Rick. Do you know you know if there's any difference in performance (or any other trade-off) when reading the text/plain string from the Request.Body instead of the application/json and [FromBody] approach? Thanks for the post.


John J Schultz
May 03, 2018

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

The string comparison against "text/plain" or "application/octet-stream" fails when the browser includes other elements in the content-type field, e.g. "text/plain; charset=utf-8". I changed the check to .contains() to get the middleware to work. Without it, I was getting 415 (media not supported errors).. which led me to the cause.


Amrutha
November 26, 2018

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

I am trying to read stream data from HttpRequest.Body but I am getting empty string. The request is send here from .net project

    HttpWebRequest request = null;
    Uri uri = new Uri(**Endpoint**);
    UTF8Encoding encoding = new UTF8Encoding();
    byte[] bytes = encoding.GetBytes(message);
    request = (HttpWebRequest)WebRequest.Create(uri);
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = bytes.Length;
    request.UseDefaultCredentials = true;
    using (Stream writeStream = request.GetRequestStream()) {
        writeStream.Write(bytes, 0, bytes.Length);
    }
    try {
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        if (response.StatusCode == HttpStatusCode.OK) {
            return true;
        } else {
            return false;
        }
    } catch {
        lock (endpointLock) {
            _pushHttpEndpoint = null;
        }
        return false;
    }

The request is send here.This is .net core 2.1 application. I am trying to read the data in request body but that is returning empty

[HttpPost]
public string Post()
{
    var bodyStr = "";
    var req = HttpContext.Request;           
    req.EnableRewind();            
    using (StreamReader reader
              = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
    {
        bodyStr = reader.ReadToEnd();
    }    

    req.Body.Seek(0, SeekOrigin.Begin);
    //do other stuff
    return bodyStr;
 }

Can you please help me with this. We are in position where we cannot change the .net solution code. Any changes should be done in .net core solution side. We are trying to fit the new api in place of existing Endpoint. 😦


Fred
January 07, 2019

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

This is what I did ... .net core 2.1... react js, axios is the rough environs... not super clean by any means but gets the job done:

On the c# side (in controller):

        public class SideWipe
        {
            public string contents;
        }
        [HttpPost]
        [Route ("UpdateCustomer")]
        public JsonResult UpdateCustomer([FromBody]SideWipe incoming)
        {
            Customer customer = Newtonsoft.Json.JsonConvert.DeserializeObject<Customer>(incoming.contents);
      ... blah blah

On the react side:

const jcust = JSON.stringify(this.state.customerRec); axios({ method: 'post', url: 'https://localhost:44373/api/BotAdmin/UpdateCustomer', data: }) .then((response) ⇒ { console.log(response);

 })
Seems to work so far.

Dave Hoogendoorn
February 18, 2019

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

Hi Rick, great work! Could you explain on how to read context.Request multiple times using GetRawBodyStringAsync. I tried to extend it with Request.EnableRewind, but I cannot get a hold of it. Thanks, Dave


ST
February 20, 2019

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

Nice post. For application/x-www-form-urlencoded, is there a build in IInputFormatter?

I'm using a [FormBody] and the HTTP Post can be a full HTML form submit or an AJAX JSON post.


Rick Strahl
February 20, 2019

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

@ST - not sure what you're asking. If you have form data it should map to object properties when using [FromBody] with application/x-www-form-urlencoded. If content is application\json it should deserialize into the object.


Sebastian Redl
March 12, 2019

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

Excellent information. But your handling of content type and the body data is fragile. You should use the MediaType struct from Mvc as a helper. Then you can do things like this:

private static readonly MediaType TextPlain = new MediaType("text/plain");

var mediaType = new MediaType(httpContext.Request.ContentType);
if (mediaType.IsSubsetOf(TextPlain)) // deals with capitalization and header parameters
{
  // deals with encoding parameter in content type and falls back to UTF-8 if missing or invalid
  var reader = new StreamReader(httpContext.Request.Body, mediaType.Encoding ?? Encoding.UTF8);
  var content = await reader.ReadToEndAsync();
}

Clarence Hwang
April 18, 2019

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

I refer to the article above on "Reading Request.Body for Raw Data... without parameters"

I'm using .NET Core 2.2 with attribute routing, i.e. at client side $.post(URL, Data) where URL is like https://domain.com/ctrl/id/name/contact (id/name/contact are parameters in attribute routing)

At the controller side, I'm using the codes suggested in your article above, i.e. read Request.Body as a stream and is always returning empty string.

After some trials and errors, I observed that without using attribute routing, i.e. URL becomes "https://domain.com/ctrl", the read from Request.Body works!

I tried Request.EnableBuffering() and rewinding Request.Body using Request.Body.Position=0 and it crashed without error, i.e. my VS just do nothing perpetually.

I'm new to Web Development, and wonder if it is conceptually sound to make attribute routing and Request.Body both work?


Paul Mrozowski
June 19, 2019

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

I see some people ran into this a while ago, but for future visitors:

If you're also receiving parameters via the [FromBody] attribute and also need access the raw request body, you have to reset the stream otherwise you'll get an empty string back.

Here's an example:

        public static async Task<string> GetRawBodyStringAsync(this HttpRequest request, Encoding encoding = null)
        {
            if (encoding == null)
                encoding = Encoding.UTF8;

            request.Body.Seek(0, SeekOrigin.Begin);
            
            using (StreamReader reader = new StreamReader(request.Body, encoding))
                return await reader.ReadToEndAsync();
        }

Jason
August 28, 2019

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

Hi, Rick, First I really appreciate your detailed instruction on how to get text/plain posted to mvc route, which resolves half of my issue. But my second half of the puzzle is that I need to use Route("") for my Controller to get the posted text from the root path /. When I deploy the app to AWS Lambda I always get a response {"message": "Missing Authentication Token"} when I tested with Postman but I did get correct response on AWS API Gateway. I wonder if you have come across anything like this before.

Thanks again for sharing this article.


zuhry
March 03, 2020

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

how to check if content from body or from form?


Rick Strahl
March 03, 2020

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

@zuhry - the content is always the same. How it's handled in your action method depends on the way you specify the parameters (ie. [FromBody] vs. not).


Mike
May 07, 2020

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

This is a fantastic article. Thank you so much for explaining this. Do you have an example of setting up an InputFormatter for .NET Core 3.1? This seems to no longer work. I would love to be able to use FromBody so my Swagger docs correctly show that the body is required.

Thank you.


Brandon Moore
June 07, 2020

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

With this, it seems you must include [FromBody] and you cannot include more than one parameter with this attribute. Is there any way around this limitation to allow for multiple primitive parameters? I used the previous incarnation of this solution you shared for web api to handle multiple primitive parameters and it worked fabulously... can't seem to find a way to do the same here though 😦


Ronnie
August 24, 2020

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

You should try setting the AllowEmptyInputInBodyModelBinding on MvcOptions which allows a default (null for reference types) to be deserialised:

`csharp services.AddOptions<MvcOptions>().Configure( o => o.AllowEmptyInputInBodyModelBinding = true ); `


Mike Rickerby
September 14, 2020

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

Is there any way to do use [NakedBody] attribute with ASPNET Core 3?


Rick Strahl
September 15, 2020

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

@Mike - not unless you build it 😄...

That attribute was built specifically for Web API on full framework and the semantics and logic for that with core would be completely different as the pipeline is completely different. Should actually be easier as the APIs are a lot cleaner. But I haven't built it...

If you need raw request access using the Request Helpers is probably the best way to do this. I'm guessing the reason you ask is for porting an existing application?


Tomáš
March 19, 2021

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

Really helpful and it saved me a lot of experiments with .NET core! Now I understand also the purpose of the formatters. Thank you very much.


Sankar
March 22, 2021

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

Hi Rick,

Thank you very much for the useful information. I wanted to receive a XML file in my POST request then validate the xml for any validations using the XSD and then process the file. What is best way to achieve this? I am testing through postman passing the file in the request body as binary but not working. Any suggestion is greatly accepted.


Rick Strahl
March 22, 2021

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

@Sankar - Simplest: Read the raw body stream and stream into an XmlDocument or XLinq structure on an [HttpPost] action that doesn't have explicit parameters.

You can also create a custom filter for XML to parse generically but you'd be replacing the default XML entity parser that ASP.NET provides. Probably not worth it unless you have a lot of requests that share common parse logic.


Sankar
March 23, 2021

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

Thank you!

Using the following with in the Post method, without any parameters. It works. Xdocument xDoc = Xdocument(Request.Body);


Jonathan Ramos
June 17, 2021

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

Thanks for this! I was frustrated for not being able to receive the a JSON string to work without Binding. To be able to Deserialize to different classes I removed escape characters from the string.

rawBodyStream.Replace('\n', ' ').Replace('\r', ' ')


Ragnorok
July 06, 2021

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

You, Mr Strahl, are awesome. I've been scouring the web for days trying to discover how this works, to migrate a "framework" system to "core". I knew it was simple, but finding what simple thing would make their brain-dead automagic happy was apparently a state secret. Thanks for blowing the whistle! (wink) In case it helps anyone else:

[HttpPost]
public async Task<string> Post()
{
   using(StreamReader body = new StreamReader(Request.Body, Encoding.UTF8)) {
      System.Xml.Linq.XElement xlog = System.Xml.Linq.XElement.Parse(await body.ReadToEndAsync());
   }
}

That's all it takes to get parse XML from a non-form POST. The string return isn't required, that's detritus from me fiddling with it.


Evgenii Koksharov
October 12, 2021

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

Don't Forget [FromBody]

OMG thank you!


David
March 29, 2023

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

Thank you, this is the article I was looking for. Btw may I know what you use for your great and fast website? I'm also planning to create a simple website for my business, but I need a domain and hosting recommendation. I hear asphostportal has a good reputation for that. Can you recommend them or are there any alternatives?


Rick Strahl
April 10, 2023

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

@David - I self host a Windows Server on a VPC server I manage (on Vultr). Performance and price is excellent and I run 30+ sites on this server.


Alik
April 11, 2023

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

Hi I notice you are sending Expect 100 continue. I have some issues understanding this. For example, lets say I have a API Method Called GetData. And I am asked to send back 100 status code. I tried this and the caller just hangs even when using return StatusCode(100,"Hi"). But according to all your samples you don't handle or return the Except 100continue just send it as a header. Am I missing something?


Rick Strahl
April 11, 2023

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

@Alik - I'm not sending this - the browser is when making the request in JSON calls. ASP.NET Core decides how to send back the data and it may send it all at once or send partial responses that correspond to the Expect 100.

Frankly I don't know how those semantics work - it happens behind the scenes, and it's not something that you have to manage with your API code, unless you explicitly set options on the API configuration.


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