Search code examples
c#asp.net-mvcasp.net-web-apiasp.net-core-mvc

ASP.NET Core v2 (2015) MVC : How to get raw JSON bound to a string without a type?


Similar to this old question about prior ASP.NET versions, I want to get the request body of an HTTP POST to be bound to a string. It seems that the method binds, but that value is null, when ASP.NET invokes my controller method:

namespace Demo.Controllers
{

    [Route("[controller]")]
    public class WebApiDemoController : Controller
    {
    ...

    // POST api/values
    [HttpPost]
    public System.Net.Http.HttpResponseMessage Post([FromBody]string value)
    {
       // expected: value = json string, actual: json = null.
    }

Do I still have to go grab the body from a stream? Or should this just work? When testing the above method, I used the following http headers:

Accept: Application/json
Content-Type: Application/json;charset=UTF-8

I'm passing in the following in the body: { "a": 1 }

I do NOT want to bind to a string variable named a. I want to bind any JSON I get, and then I want to use the JSON content, any arbitrary content at all, from within my method.

If I understood the documentation, the [FromBody] attribute should have done what I wanted, but I'm guessing that the ASP.NET core MVC binding mechanism won't bind a json to a "string value", but perhaps I could do something else that gets me an equivalent level of flexibility.

A similar question here gives me the idea maybe I should have written [FromBody] dynamic data instead of using [FromBody] string value.

Update: There are answers here for .net core 6 and other modern .net core versions.


Solution

  • The cleanest option I've found is adding your own simple InputFormatter:

    public class RawJsonBodyInputFormatter : InputFormatter
    {
        public RawJsonBodyInputFormatter()
        {
            this.SupportedMediaTypes.Add("application/json");
        }
    
        public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
        {
            var request = context.HttpContext.Request;
            using (var reader = new StreamReader(request.Body))
            {
                var content = await reader.ReadToEndAsync();
                return await InputFormatterResult.SuccessAsync(content);
            }
        }
    
        protected override bool CanReadType(Type type)
        {
            return type == typeof(string);
        }
    }
    

    And in your Startup.cs inside ConfigureServices:

    services
        .AddMvc(options =>
        {
            options.InputFormatters.Insert(0, new RawJsonBodyInputFormatter());
        });
    

    That will let you get at the raw JSON payload in your controllers:

    [HttpPost]
    public IActionResult Post([FromBody]string value)
    {
        // value will be the request json payload
    }