FromBody string parameter is giving null - c#

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"

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.

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

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
 

In my case I forgot to use
JSON.stringify(bodyStuff).

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

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

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

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.

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

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

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:
Install Microsoft.AspNetCore.Mvc.NewtonsoftJson which is preview
version.
Change to services.AddControllers().AddNewtonsoftJson();
Solution taken from https://stackoverflow.com/a/57652537/4871693

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 ...

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

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.

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.

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

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!

Related

How do I define the Route for a .Net Core 3.1 webapi service call which takes an array of strings as a parameter?

I'm create a .NetCore 3.1 Web API Service.
I am having difficulties figuring out the correct routing definition for the service call.
Consider the following definition:
[HttpPut]
[Route("{paramA}/{paramB}")]
public void Sample(string paramA, string[] paramB)
{
}
What is the correct way to define my routing parameters when passing either
an array of strings? Should I be using List instead?
I suggest that you could pass the array list by query string:
[HttpPut]
[Route("{paramA}")]
public void Sample([FromRoute]string paramA, [FromQuery]string[] paramB)
{
}
Send Request like:https://localhost:5001/xxxxxx/aaa?paramB=ss&paramB=sd.
Result:
I recomend you to pass the Arraay strings on the body of the request in a HttpPost, this is the part of the request where you don't have character limitations, when in the url of the query you will have something like 300 character limits.
For example:
HttpPost]
[Route("MyArrayStrings")]
public async string MyArrayStrings([FromBody] string content)
{
return content;
}
And then, you can use Newtonsoft for serialize the data when sending the request, and for Deserialize when received.

How to convert JSON string to object compatible with xUnit/.NET Core API?

In my xUnit test case I am reading JSON from a file, loading it in string and passing it a function that is originally called by controller.
My controller is:
[HttpPost]
public List<SomeCls> Post([FromBody] object ds)
{
var result = Myservice.DoSomething(ds);
}
This controller API works fine, I tested by posting raw json via Postman. However, in my unit test when I read JSON from a file and pass it into Myservice.DoSomething(ds), I get error:
---- System.ArgumentException : Could not cast or convert from System.String to System.Collections.Generic.List`1[MyDataSet].
In my xUnit test, how to convert JSON string into an object similar to the one that gets passed into the controller method by POST request?
You need to deserialise the string to the object you want. This is done for you by the controller but you'll have to do it manually when unit testing. If you're using Json.Net (Newtonsoft) then the code will look something like this:
var yourObject = JsonConvert.DeserializeObject<List<MyDataSet>>(yourJsonString);
Why not deserialize the object first using Newtonsoft.Json and something like...
List<type> value = JsonConvert.DeserializeObject<type>(ds);
var result = Myservice.DoSomething(value);

how can I validate a web api request object for this scenario?

I have an api/v1/users/search uri within a Web API 2.2 project. This uri accepts a UserSearchRequest object. Here's a sample:
{
"DomainName":"ad.corp.domain",
"NetworkUserId":"jsmith2",
"FirstName":"John",
"LastName":"Smith"
}
The backend search logic will append all of the provided request parameter values to filter the set of users returned. Otherwise, an empty request object will result in all users being returned. However, if a client passes a request like the following then all users will be returned:
{
"UserName":"jsmith2"
}
In the example above, an invalid proprty of UserName was mistakenly used instead of NetworkUserId. However, instead of Web API returning an error, it simply ignored the additional property and returned all Users since no valid search criteria property values were provided.
What would be a proper way to validate the incoming request so that if an invalid prpoerty name is provided then Web API will return a 404 BadRequest, and preferably indicate the invalid property name?
You are facing The "Over-Posting" Problem. This can be handled in a couple of different ways :
Use the Bind attribute and whitelist or blacklist the properties you want.
public ActionResult search([Bind(Exclude="UserName")] Person person)
{
...
}
public ActionResult search([Bind(Include="DomainName, NetworkUserId,
FirstName, LastName")] Person person)
{
...
}
The other solution is to create a Custom Model Binder by extending IModelBinder. This will identify the extra columns and handle that as an error. You can check the implementation here.
In case of incorrect property names or datatypes, the web api will fail to deserialize the json to UserSearchRequest object and api controller parameter will be null. You can check for null which will indicate that input request was not properly formatted. Here is sample code:
public async Task<IActionResult> SearchUsers([FromBody] UserSearchRequest
searchRequest) {
if(searchRequest == null)
{
return BadRequest();
}
//For valid search request, continue search...
}
I think here you are getting the data from Particular x or y table. What you are showing here is returning directory data. It is actually easy part . Controller code you can manage validation. I think you are tying to get "NetworkUserId" but controller you might be changed to username . Please check your datatype . Please attach your controller code and model class.Thank you .

Was a JSON property present on a ASP.NET Core WebAPI HttpPost

I have a Controller in a ASP.NET Core WebAPI. On a HttpPost I receive the body of the post via the [FromBody] referenced input variable.
For the purposes of this question, let's assume I'm expecting to receive the following JSON submission with null being a valid value.
{
"start": null,
"finish": "Far far away"
}
When submitted and start is not present the C# variable simply reflect a null for start.
How can I go about finding out if the start property was present or not in the HttpPost?
You can use JObject if you want more control over the payload. An example is:
[HttpPost]
[Route("api/test")]
public IHttpActionResult Test(JObject item)
{
//Check if start is included
var data = item.ToObject<YourClass>();
...
}
With [FromBody] , deserialization happens on the fly, hence difficult to intercept the result.

ASP.NET Core 1.1.0 nulling values of Dictionary<string,object>

I have an ASP.NET WebAPI Core 1.1.0 app that is not correctly serializing my responses when returned to the caller. I have an object with a property that is a Dictionary<string,object> and the values in the Payload dictionary are being nulled out when the response is returned to the caller.
Object:
ResponseObject {
string ID {get; set;}
DateTime Date {get; set;}
Dictionary<string,object> Payload {get; set;}
...
}
Controller:
[HttpPost]
public async Task<ResponseObject> Post([FromBody] ResponseObject request) {
// Object here is deserialized correctly.
// Do work
ResponseObject response = ...
// Object here has values in the Payload
return response;
}
When the request comes into the app it is deserialized correctly into the ResponseObject, the app does some processing and returns that object back out in a modified form. The keys in the Payload dictionary can change in name and number. When the ResponseObject is about to return I can see in debugging that the data is in the Payload dictionary. Once the debugger leaves the return for the controller and sent back to the caller the dictionaries values are empty.
Based on this I can assume that the problem is happening in the ASP.NET Core app during serialization of the ResponseObject after the return. I do not want to change out the default serializer because everything else in the app works as expected. I know that deserialization and serialization of the ResponseObject using Json.NET works because the app does it flawlessly in other parts of the app. The only thing I can think of is that the default WebAPI serializer is using different options or is a different implementation.
What am I missing? Is there an option I can set for the serializer to get the values to show up? Or will I have to change the default serializer? Or is there some other solution I am not seeing?
I know you said you do not want to use Json.NET. Instead you can try the following
return Json(response);
I ended up changing the return type on the controller to be a string and serializing the object myself directly. That way I have control over how the serialization is done and don't have to rely on WebAPI's magic beans to do it for me. It isn't the most ideal solution and not exactly what I wanted, but it works. The resulting controller that worked is below:
[HttpPost]
public async Task<string> Post([FromBody] ResponseObject request) {
// Do work
ResponseObject response = ...
// I am using Jil here, but you could also use Json.NET
return JSON.SerializeDynamic(response);
// return JsonConvert.SerializeObject(response);
}

Categories