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.
Related
Good morning,
I am using ASP.NET HTTP API as Actions on Google fullfilment. I have an error message when I try to send tokens to Actions on Google project. Regarding to another question I read I made sure to have clear int value (not decimal) and it doesn't work.
My JSON which I send to Actions on Google:
{"token_type":"Bearer","access_token":"edeaa27e-12b7-43a1-bc7c-e6bbf9af71c3","refresh_token":"4344383b-cf07-4d12-a5c2-44b6481f5f48","expires_in":86400}
Error message:
{
insertId: "1o7lj2bmu"
jsonPayload: {
#type: "type.googleapis.com/google.identity.accountlinking.type.AccountLinkingError"
errorReason: "Can't parse the response. The response needs to be JSON format."
response: {
body: ""{\"token_type\":\"Bearer\",\"access_token\":\"edeaa27e-12b7-43a1-bc7c-e6bbf9af71c3\",\"refresh_token\":\"4344383b-cf07-4d12-a5c2-44b6481f5f48\",\"expires_in\":86400}""
status: 200
}
step: "AUTH_CODE_EXCHANGE"
}
logName: "projects/smartlightproject-f47f4/logs/accountlinking-pa.googleapis.com%2Ferror"
receiveTimestamp: "2022-07-12T12:32:40.739532642Z"
resource: {2}
severity: "ERROR"
timestamp: "2022-07-12T12:32:40.539Z"
}
What's wrong with that JSON? Or maybe I should do it in some other way?
I will be very very grateful if you could help me with that.
Here's my code after some improvements:
var result_acceess_token = "edeaa27e-12b7-43a1-bc7c-e6bbf9af71c3";
var result_refresh_token = "4344383b-cf07-4d12-a5c2-44b6481f5f48";
var expires_expires_in = 86400;
var resultObj = new
{
token_type = "Bearer",
access_token = result_acceess_token,
refresh_token = result_refresh_token,
expires_in = expires_expires_in
};
return Ok(resultObj);
Earlier I tried to serialize JSON with JsonConvert and return it as JsonResult or raw string value.
It looks like the response you're sending back is the JSON as a quoted string. That is - there is a quote at the beginning and end of the JSON. While this is a valid JSON string (it is, after all, a quoted string), it sounds like it is looking for a JSON object.
Verify the body of the response and make sure it does not contain the opening and closing quotes.
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".
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:
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 .....
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.