34
votes

Using [FromBody] string content on an ApiController in ASP.NET Core 3.0 returns a validation error:

{"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
 "title":"One or more validation errors occurred.",
 "status":400,
 "traceId":"|9dd96d96-4e64bafba4ba0245.",
 "errors":{"$":["The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1."]}}

when the client post data with content-type : application/json

How do I get the raw json data as a string in my api controller in .NET Core 3.0? Without the client having to update its content type?

6
Does this works in .net core 2.1? Post sample of your json.cdev
I decided to just go with StreamReader and read the Request.Body myself. Its a new project and havent testet on 2.1, but might have bound the body to JToken in the past instead of string.Poul K. Sørensen

6 Answers

54
votes

Not sure this help but I think they made some change in .net core 3.0 Newtonsoft.JSON package so you can try this

Install Microsoft.AspNetCore.Mvc.NewtonsoftJson package.

In your startup.cs add

services.AddControllers().AddNewtonsoftJson();

28
votes

If you are using asp.net core 3.0 then this has built-in JSON support. I have use the following and it works without setting the custom input handler.

[HttpPost]
public async Task<IActionResult> Index([FromBody] JsonElement body)
{

    string json = System.Text.Json.JsonSerializer.Serialize(body);
    return Ok();

}
14
votes

Change [FromBody] string content to [FromBody] object content and then if you want/need to read as string use content.ToString()

5
votes

If you change the parameter [FromBody] String value to [FromBody] YourType value it is automatically deserialised for you.

From:

// POST api/<SelectiveCallRulesController>
[HttpPost]
public async Task Post([FromBody] String rule)        
{
...

To:

// POST api/<SelectiveCallRulesController>
[HttpPost]
public async Task Post([FromBody] SelectiveCallRule rule)        
{
...

It had me going around until I realised the error message regarding deserialisation is correct!

-1
votes

You need to convert the Json Object to string and then send it to server. Like JSON.stringify(jsonObj).

-2
votes

I had to write a custom IInputFormatter to ensure my body content was always interpreted as a string.

I also was in the situation where updating all of the API clients was infeasible.

The following will ensure that any [FromBody] parameters will be interpreted as strings, even if they are not quote-wrapped by the caller.

public class JsonStringInputFormatter : TextInputFormatter
{
    public JsonStringInputFormatter() : base()
    {
        SupportedEncodings.Add(UTF8EncodingWithoutBOM);
        SupportedEncodings.Add(UTF16EncodingLittleEndian);

        SupportedMediaTypes.Add(MediaTypeNames.Application.Json);
    }

    public override bool CanRead(InputFormatterContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        return context.ModelType == typeof(string);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(
        InputFormatterContext context, Encoding encoding)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        using (var streamReader = new StreamReader(
            context.HttpContext.Request.Body,
            encoding))
        {
            return await InputFormatterResult.SuccessAsync(
                (await streamReader.ReadToEndAsync()).Trim('"'));
        }
    }
}

Trimming quotes from the body allows this to be forwards-compatible for body content that is correctly formatted and quote-wrapped.

Ensure that it is registered in your startup before the System.Text.Json formatter:

services.AddControllers()
    .AddMvcOptions(options =>
    {
        options.InputFormatters.Insert(
            0,
            new JsonStringInputFormatter());
    });