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

C#asp.net Mvcasp.net Web-Apiasp.net Core-Mvc

C# Problem Overview


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: This kind of trick should be thought about before doing it, because if you wanted to have the .net core framework handle JSON and XML encoding for you, you just killed that capability. Certain types of REST servers can and often do have requirements to support both XML and JSON content-types, at least ones I have encountered which have standards documents.

C# Solutions


Solution 1 - C#

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
}

Solution 2 - C#

The following works in .net core 1.x, but not in .net core 2.x.

As I commented, the solution is to use [FromBody]dynamic data as my parameter list, using dynamic instead of string, and I will receive a JObject.

Caution: If your architecture calls for a single WebApi server to be equally fluent in producing XML and JSON, depending on content-type header entries, this kind of direct-JSON-consumption strategy can backfire on you. (Supporting both XML and JSON on the same service is possible with sufficient work, but then you're taking stuff that was further UP the MVC asset pipeline and moving it down into your controller methods, which turns out to be against the spirit of MVC, where models come to you as POCOs already parsed.)

Once you convert to a string inside the method, converting the incoming JObject (Newtonsoft.JSON in memory data type for JSON) to a string.

Found at other answer here.

Sample code, thanks to Jeson Martajaya:

With dynamic:

[HttpPost]
public System.Net.Http.HttpResponseMessage Post([FromBody]dynamic value)
{
   //...
}

Sample code with JObject:

[HttpPost]
public System.Net.Http.HttpResponseMessage Post([FromBody]Newtonsoft.Json.Linq.JObject value)
{
   //...
}

Solution 3 - C#

Found a solution for ASP.NET Core 3.1 Web API.

Looks like following:

public async Task<IActionResult> PutAsync([FromBody] System.Text.Json.JsonElement entity)
{ 
    // your code here
}

Solution 4 - C#

The following two methods works in ASP.NET core 2 to read the raw json string.

  1. This one has better performance.

     [HttpPost]
     public async Task<ActionResult<int>> Process()
     {
         string jsonString;
         using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
         {
             jsonString = await reader.ReadToEndAsync();
         }
    
    [HttpPost]
    public async Task<ActionResult<int>> Process([FromBody]JToken jsonbody)
    {
        var jsonString = jsonBody.ToString();

Solution 5 - C#

Alternatively, you could also just accept a JObject and you would be able to use Linq to Json ot even directly ToString() if you really need the string.

Solution 6 - C#

Based on Saeb Amini's excellent answer above, this extends his solution to be for plain-text as well. The only changes here are adding the "text/plain" mime-type, and adding a namespace and required usings.

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Formatters;

namespace AspExtensions // or whatever
{
	// see: https://stackoverflow.com/a/47807117/264031
	public class RawStringBodyInputFormatter : InputFormatter
	{
		public RawStringBodyInputFormatter()
		{
			this.SupportedMediaTypes.Add("text/plain");
			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)) {
				string content = await reader.ReadToEndAsync();
				return await InputFormatterResult.SuccessAsync(content);
			}
		}

		protected override bool CanReadType(Type type)
		{
			return type == typeof(string);
		}
	}
}

Solution 7 - C#

If you don't mine forgoing the automagic binding, this can be placed directly in an Http handler on a Controller:

using StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8);
var value = reader.ReadToEndAsync().GetAwaiter().GetResult();

Solution 8 - C#

I see that Sam has already been down voted for saying pretty much the same thing, but in testing using Postman I find that if I set the request body to just a simple double quoted string ASP binds it fine with the default '[FromBody]string value' argument.

"just send your string like this without any curly braces"

Not sure whether application/json is supposed to accept data in this format. Hopefully by posting this someone knowledgeable will pipe up and state whether this is valid or not.

Solution 9 - C#

You need a type to bind the data. Example:

public class Person
{
   public string Name {get; set;}
}

for data { "Name" : "James"}

Solution 10 - C#

If you want to receive a string you need to pass it as a string. Your JSON should be enclosed in quotes:

'{ "a": 1 }'

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWarren PView Question on Stackoverflow
Solution 1 - C#Saeb AminiView Answer on Stackoverflow
Solution 2 - C#Warren PView Answer on Stackoverflow
Solution 3 - C#Sergei TishkovView Answer on Stackoverflow
Solution 4 - C#Xavier JohnView Answer on Stackoverflow
Solution 5 - C#Fabio SalvalaiView Answer on Stackoverflow
Solution 6 - C#Nicholas PetersenView Answer on Stackoverflow
Solution 7 - C#BCSView Answer on Stackoverflow
Solution 8 - C#NeutrinoView Answer on Stackoverflow
Solution 9 - C#KiranView Answer on Stackoverflow
Solution 10 - C#SamView Answer on Stackoverflow