87

This is probably something very basic, but I am having trouble figuring out where I am going wrong.

I am trying to grab a string from the body of a POST, but "jsonString" only shows as null. I also want to avoid using a model, but maybe this isn't possible. The piece of code that I am hitting with PostMan is this chunk:

[Route("Edit/Test")]
[HttpPost]
public void Test(int id, [FromBody] string jsonString)
{
    ...
}

Maybe it is something I am doing incorrectly with postman, but I have been trying to use "=test" (as seen in other questions asked about this topic) in the value section of the body - x-www-form-urlencoded section with the key as jsonString and nothing. I have also tried using raw - text and raw - text/plain. I get the id so I know the url is correct. Any help with this would be greatly appreciated.

PostMan is set up like this currently:

POST http://localhost:8000/Edit/Test?id=111
key = id  value = 111
Body - x-www-form-urlencoded
key = jsonString  value = "=test"
Nkosi
  • 235,767
  • 35
  • 427
  • 472
Robert Prine
  • 2,633
  • 5
  • 14
  • 17
  • Can you please provide your full http request including URL & body in your question. – Matt Hensley Nov 28 '16 at 20:57
  • At least `Request.Content.ReadAsStringAsync()` should work. – Fabio Nov 28 '16 at 21:01
  • 3
    I believe this is possible. Set your header `Content-Type: application/x-www-form-urlencoded`. Body should be `=test` (nothing else). – Igor Nov 28 '16 at 21:23
  • Related question for Asp.Net Core https://stackoverflow.com/questions/31952002/asp-net-core-mvc-how-to-get-raw-json-bound-to-a-string-without-a-type – Michael Freidgeim Nov 18 '17 at 01:18
  • I have been batling with this for two days and after reading every article I could find about it, it turned out to be as simple as formatting the JSON string correctly in the WebRequest: The data must start and end with double quotes (I.E. Add double quotes inside your string of data around the json data) and if you then use single quotes throughout your json data it all plays nice. – Gineer Jun 07 '18 at 13:49
  • Just had same issue when trying to use Swagger UI on an API with [FromBody] string. The string is interpreted as JSON: I was typing in 'test' (without quotes) instead of '"test"' (with double quotes). – Etherman Jul 12 '20 at 13:27
  • You can find my answer by going to this link: https://stackoverflow.com/questions/41559050/string-value-is-empty-when-using-frombody-in-asp-net-web-api/69975991#69975991 – CagriK Nov 15 '21 at 14:30

17 Answers17

85

By declaring the jsonString parameter with [FromBody] you tell ASP.NET Core to use the input formatter to bind the provided JSON (or XML) to a model. So your test should work, if you provide a simple model class

public class MyModel
{
    public string Key {get; set;}
}

[Route("Edit/Test")]
[HttpPost]
public void Test(int id, [FromBody] MyModel model)
{
    ... model.Key....
}

and a sent JSON like

{
    key: "value"
}

Of course you can skip the model binding and retrieve the provided data directly by accessing HttpContext.Request in the controller. The HttpContext.Request.Body property gives you the content stream or you can access the form data via HttpContext.Request.Forms.

I personally prefer the model binding because of the type safety.

Ralf Bönning
  • 14,515
  • 5
  • 49
  • 67
  • 2
    @Fabio - then [FromBody] will not work, because it tells the framework you want to bind the data to a model class. In order to avoid the binding, skip this parameter and access the sent data directly as hinted in the last paragraph. Hope that will help. – Ralf Bönning Nov 28 '16 at 21:10
  • This worked for me - you just have to use RAW instead of FORM in the POST options for POSTMan. – Codeman Jun 19 '18 at 23:48
  • worked like a charm, thank you. don't know why we can't just pass a string from body, but... meh... hehe thank you – jPhizzle Jul 22 '19 at 18:54
  • 3
    If it is just a single string then you don't need to create a model - you just need to pass a JSON string, which is wrapped in double quotes, i.e. "test" instead of just test. – Etherman Jul 12 '20 at 13:28
  • So this won't work with simple variable types like strings. Once thing to note is that [FromBody] will not work with async jQuery/javascript (client side) - it will always be null even if you give it a model. data: is completely ignored in POST/PUT calls. – MC9000 Oct 01 '22 at 19:01
65

Referencing Parameter Binding in ASP.NET Web API

Using [FromBody]

To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter:

[Route("Edit/Test")]
[HttpPost]
public IHttpActionResult Test(int id, [FromBody] string jsonString) { ... }

In this example, Web API will use a media-type formatter to read the value of jsonString from the request body. Here is an example client request.

POST http://localhost:8000/Edit/Test?id=111 HTTP/1.1
User-Agent: Fiddler
Host: localhost:8000
Content-Type: application/json
Content-Length: 6

"test"

When a parameter has [FromBody], Web API uses the Content-Type header to select a formatter. In this example, the content type is "application/json" and the request body is a raw JSON string (not a JSON object).

In the above example no model is needed if the data is provided in the correct format in the body.

For URL encoded a request would look like this

POST http://localhost:8000/Edit/Test?id=111 HTTP/1.1
User-Agent: Fiddler
Host: localhost:8000
Content-Type: application/x-www-form-urlencoded
Content-Length: 5

=test
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Is there anyway to achieve this with `multipart/form-data`? AFAIK this one doesn't allow keyless entries. – Rudey Jan 28 '20 at 22:39
45

When having [FromBody]attribute, the string sent should not be a raw string, but rather a JSON string as it includes the wrapping quotes:

"test"

Based on https://weblog.west-wind.com/posts/2017/Sep/14/Accepting-Raw-Request-Body-Content-in-ASPNET-Core-API-Controllers

Similar answer string value is Empty when using FromBody in asp.net web api

 

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
11

I know this answer is kinda old and there are some very good answers who already solve the problem. In order to expand the issue I'd like to mention one more thing that has driven me crazy for the last 4 or 5 hours.

It is VERY VERY VERY important that your properties in your model class have the set attribute enabled.

This WILL NOT work (parameter still null):

/* Action code */
[HttpPost]
public Weird NOURLAuthenticate([FromBody] Weird form) {
    return form;
}
/* Model class code */
public class Weird {
    public string UserId {get;}
    public string UserPwd {get;}
}

This WILL work:

/* Action code */
[HttpPost]
public Weird NOURLAuthenticate([FromBody] Weird form) {
    return form;
}
/* Model class code */
public class Weird {
    public string UserId {get; set;}
    public string UserPwd {get; set;}
}
Andrew
  • 18,680
  • 13
  • 103
  • 118
Some random IT boy
  • 7,569
  • 2
  • 21
  • 47
11

In my case I forgot to use

JSON.stringify(bodyStuff).
Andrew
  • 18,680
  • 13
  • 103
  • 118
Eugene Ihnatsyeu
  • 1,299
  • 13
  • 15
  • Thanks for this! For anyone using Axios, this is likely the solution you are looking for. Axios handles request body strings in a very specific way, even if you hardcode the literal like `"test"`, the request body will reflect `test` ... which is not a valid json literal. Thanks Eugene for saving me hours here! – spencer741 Feb 10 '21 at 07:41
11

Post the string with raw JSON, and do not forget the double quotation marks!

enter image description here

Mohammad Alqurm
  • 577
  • 6
  • 23
10

You are on the right track.

On your header set

Content-Type: application/x-www-form-urlencoded

The body of the POST request should be =test and nothing else. For unknown/variable strings you have to URL encode the value so that way you do not accidentally escape with an input character.


See also POST string to ASP.NET Web Api application - returns null

Community
  • 1
  • 1
Igor
  • 60,821
  • 10
  • 100
  • 175
5

Finally got it working after 1 hour struggle.

This will remove null issue, also gets the JSON key1's value of value1, in a generic way (no model binding), .

For a new WebApi 2 application example:

Postman (looks exactly, like below):

POST    http://localhost:61402/api/values   [Send]
Body
     (*) raw             JSON (application/json) v
         "{  \"key1\": \"value1\" }"

The port 61402 or url /api/values above, may be different for you.

ValuesController.cs

using Newtonsoft.Json;
// ..

// POST api/values
[HttpPost]
public object Post([FromBody]string jsonString)
{
    // add reference to Newtonsoft.Json
    //  using Newtonsoft.Json;

    // jsonString to myJsonObj
    var myJsonObj = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(jsonString);

    // value1 is myJsonObj[key1]
    var valueOfkey1 = myJsonObj["key1"];

    return myJsonObj;
}

All good for now, not sure if model binding to a class is required if I have sub keys, or, may be DeserializeObject on sub key will work.

Manohar Reddy Poreddy
  • 25,399
  • 9
  • 157
  • 140
5

If you don't want/need to be tied to a concrete class, you can pass JSON directly to a WebAPI controller. The controller is able to accept the JSON by using the ExpandoObject type. Here is the method example:

public void Post([FromBody]ExpandoObject json)
{
    var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)json);
}

Set the Content-Type header to application/json and send the JSON as the body. The keyValuePairs object will contain the JSON key/value pairs.

Or you can have the method accept the incoming JSON as a JObject type (from Newtonsoft JSON library), and by setting it to a dynamic type, you can access the properties by dot notation.

public void Post([FromBody]JObject _json)
{
    dynamic json = _json;
}
Derek Wade
  • 697
  • 8
  • 11
3

The whole day has gone for me to resolve similar issue.

You must know that built-in serializor and Newtonsoft work differently. Im my case built-in cannot parse JSON number to System.String. But I had no obvious exception or details, just data came as null.

I discovered it only when I logged ModelState like that:

logger.LogInformation($"ModelState = {ModelState.IsValid}");
string messages = string.Join("; ", ModelState.Values
                    .SelectMany(x => x.Errors)
                    .Select(x => x.ErrorMessage));
logger.LogInformation($"ModelMessages = {messages}");

And then I saw specific exception in logs:

The JSON value could not be converted to System.String

As a fix I did:

  1. Install Microsoft.AspNetCore.Mvc.NewtonsoftJson which is preview version.
  2. Change to services.AddControllers().AddNewtonsoftJson();

Solution taken from https://stackoverflow.com/a/57652537/4871693

Anton Semenov
  • 411
  • 5
  • 11
2

For .net core 3.1 post(url, JSON.stringify(yourVariable)) worked like charm at the controller MyMethod([FromBody] string yourVariable)

HydPhani
  • 592
  • 6
  • 13
1

After a long nightmare of fiddling with Google and trying out the wrong code in Stack Overflow I discovered changing ([FromBody] string model) to ([FromBody] object model) does wonders please not i am using .NET 4.0 yes yes i know it s old but ...

1

Try the below code:

[Route("/test")]
[HttpPost]
public async Task Test()
{
   using (var reader = new StreamReader(Request.Body, Encoding.UTF8))
   {
      var textFromBody = await reader.ReadToEndAsync();                    
   }            
}
Venkataraman R
  • 12,181
  • 2
  • 31
  • 58
KSitnik
  • 29
  • 4
1

This might also come in handy. I needed to pass a JSON string to my API controller. But the model was unknown upfront. Using JObject as an object type works perfectly. You can serialize later on to get a string.

[FromBody] JObject unknownStringJsonObject
Cyril Mestrom
  • 6,042
  • 4
  • 19
  • 27
1

You can just use "Object" instead of string like this:

public async Task<IActionResult> Method([FromBody] Object plainJson)

Then to print the object:

Console.WriteLine(plainJson.ToString());

And that's it!

Daspuru
  • 287
  • 4
  • 6
0

I just ran into this and was frustrating. My setup: The header was set to Content-Type: application/JSON and was passing the info from the body with JSON format, and was reading [FromBody] on the controller.

Everything was set up fine and I expect it to work, but the problem was with the JSON sent over. Since it was a complex structure, one of my classes which was defined 'Abstract' was not getting initialized and hence the values weren't assigned to the model properly. I removed the abstract keyword and it just worked..!!!

One tip, the way I could figure this out was to send data in parts to my controller and check when it becomes null... since it was a complex model I was appending one model at a time to my request params. Hope it helps someone who runs into this stupid issue.

chethandb
  • 1,021
  • 1
  • 9
  • 17
0

Also, if you're using a Postman "environment," make sure the environment is selected before you run the API script that uses it. Otherwise, it will just send the variable strings -- {{varname}} -- instead of their associated values, which the API appropriately rejects.