Poul K. Sørensen
Poul K. Sørensen

Reputation: 17540

ASP.NET Core 3.0 [FromBody] string content returns "The JSON value could not be converted to System.String."

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?

Upvotes: 71

Views: 106547

Answers (11)

ameya
ameya

Reputation: 1668

If you are using ASP.NET Core 3.0+ then this has built-in JSON support with System.Text.Json. I have used 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();

}

Upvotes: 49

Vlad Rudenko
Vlad Rudenko

Reputation: 2809

The reason for this error is:

System.Text.Json doesn't deserialize non-string values into string properties

(source).

That means if you have a controller with the simple [FromBody] string argument:

[HttpPost("save")]
public async Task Save([FromBody] string content)
{

this request will succeed:

curl -H "Content-Type: application/json" -X POST -d "\"abcdefgh\"" https://localhost:5000/save -v

but this will fail:

curl -H "Content-Type: application/json" -X POST -d "{\"content\":\"abcdefgh\"}" https://localhost:5000/save -v

In fact, a similar error occurs not only for string but for other simple types like int, bool, etc. For example, if you change the argument type to int in the code above, then sending JSON {"content":123} in the body will give JSON value could not be converted to System.Int32 error.

To avoid this error either:

  • fix the request to pass the argument in the body as "some string" (instead of JSON)
  • pass argument in request as [FromQuery] or [FromForm]
  • or move your argument into the property of some class (don't forget the getter and setter for this member because class fields are not deserialized):
public class Content
{
    public string Value { get; set;}
}
...
[HttpPost("save")]
public async Task Save([FromBody] Content content)
{

Tested on ASP.NET Core 7.0

Upvotes: 5

Prayoon Hansmer
Prayoon Hansmer

Reputation: 1

using Newtonsoft.Json.Linq;
using Newtonsoft.Json;

     
[HttpPost]
        public IActionResult SaveScreen([FromBody] JObject value)
        {
            JObject result = new JObject();
            _log.LogInformation(JsonConvert.SerializeObject(value,Formatting.Indented));
            return Content(JsonConvert.SerializeObject(result), "application/json; charset=UTF-8");
        }

Not sure if it's what you want. but i use this code and get the result I want. I just want to post json string into controller.

Upvotes: 0

Leandro Bardelli
Leandro Bardelli

Reputation: 11578

In my case I was working with Angular and NET 6.0

So the controller:

    public string? Post([FromBody] string word)
    {
    }

and the call from angular:

using

import { HttpClient, HttpHeaders } from '@angular/common/http';

the code:

const headers = new HttpHeaders({
  'Content-Type': 'application/json'
}); 

will mark the petition as json.

const body = JSON.stringify("myvalue");

  this.http.post(this.baseUrl + 'controller', body, { headers: headers, responseType: 'text', withCredentials: true }).subscribe(result => {
      this.mycontent = result;
    }, error => console.error(error));

In the above example, The responstype is only because the controller is returning also a string.

Upvotes: 0

Shakti Srivastav
Shakti Srivastav

Reputation: 300

Use JsonElement instead of string or object. {yourcontrollername([FromBody] JsonElement yourJsondata)}

Upvotes: 0

Wafeelka
Wafeelka

Reputation: 67

you can create another class contains your json field.

Upvotes: 0

CrazeydAVE
CrazeydAVE

Reputation: 111

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!

Upvotes: 9

Chev Paredes
Chev Paredes

Reputation: 771

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

Upvotes: 35

Benjamin Soddy
Benjamin Soddy

Reputation: 577

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());
    });

Upvotes: 1

Hamza Ali
Hamza Ali

Reputation: 390

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

Upvotes: -2

Tony
Tony

Reputation: 20102

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();

Upvotes: 75

Related Questions