string value is Empty when using FromBody in asp.net web api - c#

I am using asp.net core web api. below is my simple post function which is having a single string parameter. The problem is when I use [FromBody] the string stays null. I am using PostMan to test my service. I want raw data to pass from client to my controller. In Postman I am selecting body type RAW and
I set the header Content-Type text/plain. The Raw Body contains Just "Hello World" string.
[HttpPost]
[Route("hosted-services/tokenize-card")]
public IActionResult Test([FromRoute]decimal businessKey,[FromBody] string body)
{
var data = businessKey;
return new JsonResult("Hello World");
}

Like the doc says :
When a parameter has [FromBody], Web API uses the Content-Type header
to select a formatter.
Only XML and JSON content-types are supported by default. So you need to use application/xml, application/json or register a custom IInputFormatter.
Next, you need to send a content that match the selected content-type.
For json, if the parameter is int send a number. If it's a class, send a json object. If it's a string, send a json string. Etc.
int => 14
string => "azerty"
class => { "propName" : "value" }
Array => []
... => ...
In your case you should send application/json content-type and as content :
"Hello string"
And not just
Hello string
Aspnet core json input formatter implementation
Aspnet core xml input formatter implementation
Example: Creating a CSV Media Formatter

I did make it work by passing as row data in PostMan. Do not forget to add "="
sign as prefix in to the values.
public string Post([FromBody]string value)
{
return value;
}

Frombody means that get body directly.it is not necessary to use variable and value pairs.
For Example:
Your controller is like this.
[HttpPost]
public IActionResult GetStringFromBody([FromBody] string token)
{
...
}
False:
True:

Related

Http Post is sending request with null body

I have a .NET API which is as follows
public List<long> Compare ([fromBody] String NumberOfDatasets)
I am trying to access the API from an angular app. GET requests works fine, but when i try to send a POST request it goes without a body and the API receives a null parameter.
I have checked the API by accessing it from fiddler and it works fine.
Also i have enabled
CORS
HttpPost
HttpOptions
for the api.
Also the headers are getting added properly which i checked through Chrome dev tool. Am i missing anything here ?
My angular app component's TS file is as follows :
myFunction()
{
let httpHeaders = new HttpHeaders({
'Content-Type' : 'application/json'
});
let options = {
headers: httpHeaders
};
return this.httpClient.post(myUrl, "5", options);
}
Posting a string as body when using the content-type of application/json is not acceptable as the
json will try to parse from an object {} or array [].
To send a single string as the request body change the content type to application/x-www-form-urlencoded and adding a equal sign (=) infront of the string
Try it this way
myFunction()
{
let httpHeaders = new HttpHeaders({
'Content-Type' : 'application/x-www-form-urlencoded'
});
let options = {
headers: httpHeaders
};
return this.httpClient.post(myUrl, "=5", options);
}
"5" is not a valid JSON, thus MVC cannot deserialize it and returns null.
Correct way of passing string as JSON is to encapsulate it in quotation marks:
"\"5\"" //that's the way you should write it in JS
or use
JSON.stringify("5")
which will give you correct JSON string of any object you'd like to.
Moreover, passing { NumberOfDatasets: "5" } doesn't work, because proper deserialized object for it would have to look like this:
public class DatasetsNumber
{
public string NumberOfDatasets { get; set; }
}
which clearly isn't string on its own.

Reading the post body directly

I am using Google's re-captcha v2 and Google's Javascript at run time generates a form parameter, with the key g-recaptcha-response, dynamically. The markup is this:
<form method="post">
<div class="g-recaptcha" data-sitekey="6LcXjGYUAAAAA...g1UKiZ"></div>
<input type="submit" id="submit" value="Enter" />
</form>
<script src='https://www.google.com/recaptcha/api.js'></script>
You will notice that there is no <input> element with name g-recaptcha-response as it is generated by the Javascript dynamically. (Note: the above won't work for you as-is as the page url must match what has been configured at Google using data-sitekey.)
Upon clicking Submit, the request body is like this:
g-recaptcha-response=03AEMEkE....nLXmlhwEE&__RequestVerificationToken=CfDJ8Oe....93pb
I don't know how to use a model for such a scenario. So I am trying to read the request body directly using the following code:
public async Task<IActionResult> OnPostAsync()
{
String result;
using (StreamReader reader = new StreamReader(Request.Body)) {
result = await reader.ReadToEndAsync();
return RedirectToPage("/test");
}
}
The result is always an empty string, even though I can see that there is data from inspecting the Http stream. Are there any errors in the above code? It compiles and runs without error.
Or is there a built-in class or method that can return the post data in, say, Json?
...is there a built-in class or method that can return the post data in, say, Json?
Your example is posting form url encoded data. The built-in [FromForm] attribute can bind that to a Dictionary<string, string> model. Newtonsoft can convert that model to JSON for server side use, and a JsonResult can convert that model to JSON for client-side use.
[HttpPost]
public IActionResult Post([FromForm] Dictionary<string,string> model)
{
// convert to JSON
var json = Newtonsoft.Json.JsonConvert
.SerializeObject(model, Newtonsoft.Json.Formatting.Indented);
Console.WriteLine(json);
// return JSON
return new JsonResult(model);
}
If we receive a post like this...
POST http://localhost:5000/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:5000
Content-Length: 85
Content-Type: application/x-www-form-urlencoded
g-recaptcha-response=03AEMEkE....nLXmlhwEE&__RequestVerificationToken=CfDJ8Oe....93pb
...we will then see the following console output.
{
"g-recaptcha-response": "03AEMEkE....nLXmlhwEE",
"__RequestVerificationToken": "CfDJ8Oe....93pb"
}

Is there any difference between Body.Write and returning of a string?

When you return a response form an asp .net core controller you can return data in two ways (there may be more but I am just focusing on these two). My question is what is the difference between the two methods (if any); return a value vs writing directly to the body?
[HttpGet("Fetch_Write")]
public void Fetch_Write()
{
HttpContext.Response.ContentType = "application/json";
var s = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { data = "my_fetched_data" }));
HttpContext.Response.Body.Write(s, 0, s.Length);
}
In the method above the return type of the function is void and I am writing a content directly to the response body, but the version below my function returns a string. When using postman I get the same response from both api calls, is there a difference between the two? Should I use one over the other?
[HttpGet("Fetch_Return")]
public string Fetch_Return()
{
HttpContext.Response.ContentType = "application/json";
return JsonConvert.SerializeObject(new { data = "my_fetched_data" });
}
My guess is is that the function that returns a string does something similar later down the line where it writes the content to the body as I have done in the first code snippet function but I am not sure.
There isn't much difference. But in practice you should avoid both as it's boiler plate and doesn't fully utilize ASP.NET Core MVC tooling.
It's best to use IActionResult instead as return type and use either the helper methods (Ok, BadRequest, NotFound, File etc.) or directly create the OkObjectResult/OkResult classes and return them. This allows you to set status codes and let ASP.NET Core choose the correct formatter (XML or json, later on maybe even OData, protobuf or even custom formatters) which depend on accepted header of the caller.
For example:
[HttpGet("Fetch_Return")]
[Produces("application/json"),Produces("application/xml")]
public string Fetch_Return()
{
return Ok(new { data = "my_fetched_data" });
}
[Produces("application/json"),Produces("application/xml")] will only allow XML and json formatting. So if a user calls this action with Accept: application/xml he will receive an xml file and if he calls with Accept: application/json. If you request application/text, the browser will return Http Code 415 "Unsupported Media Type".

No parameterless constructor defined when POST request in JSON Content Type with ASMX Web Service

As title,
When I use fiddler to test the page -
http://localhost:59583/JSONtest.asmx/Test
Content-Type: application/json; charset=utf-8
and my request body is
{"header":{"sig":"abcdefg","timestamp":"2016-03-25T04:25:09.8395853Z"}}
It will response back to this error message:
No parameterless constructor defined for type of \u0027System.String\u0027
But if I put the JSON format with backslashes:
{"header":"{\"sig\":\"abcdefg\",\"timestamp\":\"2016-03-25T04:25:09.8395853Z\"}"}
The response will show the result I want which is correct.
How do I insert without backslashes JSON format?
This is my asmx code.
[WebMethod]
[ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)]
public string Test(string header)
{
return header;
}
That is because "header" in your first snippet is a class with properties, not a single string as in your code. The error message is just confusing in my opinion.
The second snippet does have just one string as value for "header", so that would be fine. If you really want "header" to be an object, you have to make a separate class for it in order to get it serialized right.
before sending the Ajax post request You can wrap both header object and it's value inside JSON.stringify()
var myHeader = {"header":JSON.stringify(headerValue)};
var myParams = JSON.stringify(myHeader);
then you can send the myParams variable as the data parameter into the ajax request
$.ajax('url',data:myParams .....

ServiceStack default format

I would like to set ServiceStack's default format to JSON, as opposed to the HTML formatted response it normally returns when a service is accessed from a browser. I know this can be specified on each request by sending a ?format=json parameter or setting the Accept header to application/json. Is there a way to change this without having to rely on these hints from the request?
In addition to specifying it on the QueryString with ?format=json, by appending the format .ext to the end of the route, e.g: /rockstars.json, or by specifying the HTTP Header (in your HttpClient): Accept: application/json.
Otherwise if your HttpClient doesn't send an Accept header you can specify JSON as the default content type in your AppHost with:
SetConfig(new HostConfig {
DefaultContentType = MimeTypes.Json
});
All Configuration options in ServiceStack are set here.
The issue when calling web services from a web browser is that they typically ask for Accept: text/html and not JSON which by contract ServiceStack obliges by returning back HTML if it is enabled.
To ensure JSON is returned you may also want to disable the HTML feature with:
SetConfig(new HostConfig {
EnableFeatures = Feature.All.Remove(Feature.Html),
});
Different ways to specify the Response Content Type
Otherwise if you want to override the Accept header you can force your service to always return json with any of these ways to Customize the HTTP Response, e.g:
Using a filter (AddHeader is built-in):
[AddHeader(ContentType=MimeTypes.Json)]
public object Any(Request request) { ... }
Setting the Response in the service:
public object Any(Request request)
{
base.Response.ContentType = MimeTypes.Json;
return dto;
}
Returning a decorated response:
return new HttpResult(dto, MimeTypes.Json);
I use the PreRequestFilter to force JSON responses to a browser. You still see the ?format=json on the querystring, but it's useful if you've disabled html & xml.
this.PreRequestFilters.Add( (req, res) =>
{
const string queryString = "format=json";
var jsonAccepted = req.AcceptTypes.Any(t => t.Equals(ContentType.Json, StringComparison.InvariantCultureIgnoreCase));
var jsonSpecifiedOnQuerystring = !string.IsNullOrEmpty(req.QueryString["format"]) && req.QueryString["format"].Equals("json", StringComparison.InvariantCultureIgnoreCase);
if (!jsonAccepted && !jsonSpecifiedOnQuerystring)
{
var sb = new StringBuilder(req.AbsoluteUri);
sb.Append(req.AbsoluteUri.Contains("?") ? "&" : "?");
sb.Append(queryString);
res.RedirectToUrl(sb.ToString(), HttpStatusCode.SeeOther);
res.Close();
}
});
Late to the question, but since I couldn't find the answer anywhere, I finally figured it out from ServiceStack's source code :)
The simplest way I found to default to Json instead of Html from the browser was this:
HttpRequestExtensions.PreferredContentTypes = new[] { MimeTypes.Json, MimeTypes.Xml };
Call this at the startup of your app, and it will override default's ServiceStack mime types and start with json (which will work with your browser's requests since / will match it).
Note that you should still disable Html and make Json the default mime type:
SetConfig(new HostConfig {
DefaultContentType = MimeTypes.Json
EnableFeatures = Feature.All.Remove(Feature.Html),
});
For the curious: ServiceStack uses internally HttpRequestExtensions.GetResponseContentType (see HttpRequestExtensions.cs), which loops through preferred content types. Because it contains MimeTypes.Html, it will catch the first accept type from the browser (text/html) and ignore whatever is coming after. By overriding this, text/html is not seen as a preferred content type, and it then skips to */* which defaults to json as expected.

Categories

Resources