Reputation: 17540
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
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
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:
"some string"
(instead of JSON)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
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
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
Reputation: 300
Use JsonElement instead of string or object. {yourcontrollername([FromBody] JsonElement yourJsondata)}
Upvotes: 0
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
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
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
Reputation: 390
You need to convert the Json Object to string and then send it to server. Like JSON.stringify(jsonObj).
Upvotes: -2
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