Web API HttpPost body with spaces and quotes - asp.net

I want to create an HttpPost controller method which takes a string which may include spaces or single quotes. The code I have now looks like:
[HttpPost]
[Route("ValidateExpression")]
public bool ValidateExpression([FromBody] string testExpression )
{
// ...
}
and I post to it via JS (Angular) like:
$http.post(myRootUrl +'ValidateExpression', "'token1' || 'token2'");
I can walk through the JS and see that the test expression is what I expect it to be, and it also looks fine in the body of the outgoing post (as seen in Fiddler & Firebug). But when I breakpoint in the first line of ValidateExpression on the server side, testExpression has been truncated to the first space, and stripped of quotes, so what I receive is token1.
How can I avoid this unwanted transformation?
I guess that I could create a model to wrap a single string, and send my string as field value in a JSON object matching the model...or I could do some escaping on the string, but the first seems ugly/unnecessary and the second seems like I'd be trying to hack around behaviour I don't really understand.

You need to pass in JSON object with key equal to that of parameter name.
// POST api/values
public string Post(string value) {
return value;
}
$.post('/api/values', { value: 'Dave' });
Alternatively, you can satisfy Web API's encoding requirement these ways:
$.post('api/values', "=" + value);
or
$.post('api/values', { '': value });
More information here: http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
Edit:
Also, note that angualrjs http.post and jquery.post behaves differently:
The difference is in how jQuery and AngularJS serialize and transmit
the data. Fundamentally, the problem lies with your server language of
choice being unable to understand AngularJS’s transmission
natively—that’s a darn shame because AngularJS is certainly not doing
anything wrong. By default, jQuery transmits data using Content-Type:
x-www-form-urlencoded and the familiar foo=bar&baz=moe serialization.
AngularJS, however, transmits data using Content-Type:
application/json and { "foo": "bar", "baz": "moe" } JSON
serialization, which unfortunately some Web server languages—notably
PHP—do not unserialize natively.
http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/

Related

Why shouldn't custom methods use the URL for transferring data?

TL,DR; When implementing custom methods, "the HTTP configuration [...] must use the body:* clause and all remaining request message fields shall map to the HTTP request body.". Why?
I have a problem with Google's API Design Guide which I'm attempting to follow with gRPC with Cloud Endpoints.
The HttpRule is used to transcode HTTP/JSON to gRPC. The HttpRule reference states:
Note that when using * in the body mapping, it is not possible to have
HTTP parameters, as all fields not bound by the path end in the body.
[...] The common usage of * is in custom methods which don't use the
URL at all for transferring data.
...an opinion also repeated in Google's Custom Methods documentation and reinforced with Google's API Linter,
When using a named representation in the body mapping there is a well defined space left to add metadata in the form of querystring parameters; E.g. for pagination, links, deprecation warnings, error messages).
service Messaging {
rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
option (google.api.http) = {
put: "/v1/messages/{message_id}"
// A named reference makes it possible to use querystring params
// and the HTTP body.
body: "data"
};
}
}
message UpdateMessageRequest {
message Data {
string foo = 1;
string bar = 2;
string baz = 3;
}
// mapped to the URL as querystring params
bool format = 1;
string revision = 2;
// mapped to the body
Data data = 3;
}
This allows for an HTTP PUT request to /v1/messages/123456?format=true&revision=2 with a body
foo="I am foo"
bar="I am bar"
baz="I am baz"
Since the mapping binds body to the type UpdateMessageRequest.Data, the remaining fields end up in the querystring. This is the approach used in standard methods, but not with custom) methods.
Custom methods must map body to *. The same API with a custom method would be
service Messaging {
rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
option (google.api.http) = {
put: "/v1/messages/{message_id}"
// Every field not bound by the path template should be
// mapped to the request body.
body: "*"
};
}
}
message UpdateMessageRequest {
message Data {
string foo = 1;
string bar = 2;
string baz = 3;
}
// mapped to the body
bool format = 1;
string revision = 2;
Data data = 3;
}
If the same metadata is used across both standard and custom) methods, it must be added either as querystring params, or placed in the body.
For example, an Angular app would use HttpParams
// standard method
const params = new HttpParams().append('format', true).append('revision', 2);
const request = {
foo: "I am foo",
bar: "I am bar",
baz: "I am baz",
}
this.http.post<Document>(url, request, {params});
However, a custom method requires the client to place everything in the body:
// custom method
const request = {
format: true,
revision: 2,
data: {
foo: "I am foo",
bar: "I am bar",
baz: "I am baz",
},
}
this.http.post<Document>(url, request);
Question: What is the reason for this?
Great question.
For reference, I wrote the AIP on this topic as well as the lint rule, and am also the current maintainer of the design guide that you referenced.
First off, I will mention that our latest guidance (linked above) specifically says should rather than must for this. In other words, it is the right thing to do the majority of the time, but there may be exceptions. Nothing in the gRPC transcoding implementation stops you from using a different body -- we tell you to use * for custom methods, but we do not place any technical barriers against doing something else.
I can think of a couple of good "exception cases" where a body other than * might make sense. The first would be a custom method that is modeled on one of the standard methods, but should be custom for some reason. The second would be if a custom method accepted a full resource, and wanted to set the body to that resource. This would make that method consistent with Create and Update, which obviously has value to the API's users.
If you have a case where you have a clear argument for using something else as the body (particularly if that something is the resource itself), by all means, use a different body and tell the linter to be quiet. We wrote "should" for a reason.
You also asked: Why do we have that recommendation in the first place?
There are a few reasons. The biggest one is that the exceptional cases described above are rare. I do hundreds of API reviews internally, and I can not actually think of one off the top of my head (which is not to say they do not exist). The majority of the time, the best thing for users is for the request message to mirror the HTTP payload.
Another reason is a key limitation: assigning a particular field to be the body limits what you can add outside of that field, since query strings are limited in what they can represent in both type (just primitives) and quantity (URI length constraints). Because altering the body later constitutes a breaking change, this ties your hands somewhat. That might be fine for your use case, obviously, but it is important to note.
Anyway, I hope that helps -- oh, and thanks for using my stuff. :-)

How to send integer array as a parameter from endpoint without querystring from url in asp.net core webapi

I have an endpoint that has a parameter that type of integer array.I want send some value from body as a json string.When I tried it , I was getting null value from parameter so I tried to send integer array from url but happen problem about of url length so I want to know that is possible to send it from request body or how to fix url length problem for 5.000 item
request body that I tried
{
"Ids": [349]
}
endpoint function
[HttpGet]
public void GetModels([FromBody]List<int> Ids)
{
}
First, in general including a body in a GET request is often not considered very RESTful. It is no longer specifically "banned" by RFC, but it is not typical. That said, you can make this work in ASP.Net Core using the [FromBody] attribute.
The issue has to do with how you are formatting your JSON body. Using the signature for GetModels that you have listed above, the JSON body doesn't match the parameters. Your JSON represents a top-level object with a property Ids that is an array of int, not just an array of it (or List).
If you want to use public void GetModels([FromBody]List<int> Ids) then your JSON body should simply be an array (e.g. [349,350,351]) and nothing else (no brackets, no "Ids" property name).
If you want to use the JSON body you list above, then you need another class to use for model binding, a DTO. That DTO would look something like:
public class IdDto
{
public List<int> Ids { get; set; }
}
and then your GetModels method would look like:
[HttpGet]
public void GetModels([FromBody] IdDto idDto)
{
var myIds = idDto.Ids;
}
Lastly, be sure that your GET request has a Content-Type set to application/json or ASP.Net will return a 415 "Unsupported Media Type".

Return JSON via MVC controller vs webmethod

I'm trying to convert a web forms page with a number of code-behind webmethod functions into an MVC view with a controller.
The data I get back is different. They have properly formatted JSON, but the webmethod returns JSON like this:
{"d":"{\"Success\":true,\"Data\":{\"QuoteId\":340439,\"LoginId\":40,
And the controller returns:
"{\"QuoteId\":340444,\"LoginId\":40,
Its not wrapping it in data.d like it is set up to handle in the javascript and there are no Success or Data objects. And when I try to parse it ($.parseJSON(data)) like I did with the webmethod, it gives me the old error at line 1 message.
I'm sure that if I played with it enough I could get it to work with the way the data is coming through, but I have many pages that I need to covert in the future and I'm just wondering if there is an easy way to get the controller to format it like the webmethod does.
I'm pretty sure that I know why the data is formatted differently, but it would make my life easier if I could just return the data in the same way. I've tried returning a JsonConvert.SerializeObject(obj), which is just a string, and a Return Json(obj), which I'm guessing is just a string as well, but they both return stuff the same, non-data.d way.
Okay, so I've decided to wrap the data in a "d", so that I can have a "data.d" in the Ajax result.
But for some reason the dates aren't being formatted correctly when I just send the data back via:
return Json(thewrappeddata);
they turn out weird.
So basically what I have to do is this:
public class JSONReturn {
public object d;
public static object Wrap(object data) {
return new JSONReturn() { d = data };
}
}
var thedata = new TonsofData();
return Json(
JSONReturn.Wrap(
JsonConvert.SerializeObject(thedata,
new JsonSerializerSettings {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}
)
)
);
It seems like I'm "jsonifying" the data twice here, once with JsonConvert.SerializeObject() and once with Json() (I THINK that serializes), but it seems to work and now I get what I need and all the JS just works as it did before. If I leave out the JsonConvert.SerializeObject(), the Json() formats the dates incorrectly and it causes all sorts of failure down the line.

Angular2 HTTP Post ASP.NET MVC Web API

How do you properly create a Web API POST of complex object or multiple parameters using Angular2?
I have a service component in Angular2 as seen below:
public signin(inputEmail: string, inputPassword: string): Observable<Response> {
return this.http.post('/api/account/signin', JSON.stringify({ Email: inputEmail, Password: inputPassword}), this.options);
}
The targeted web api is seen below:
[HttpPost]
[Route("signin")]
public async Task<IActionResult> Signin(string email, string password)
{
....
}
This does not work because I need to convert the parameters of the web api into a single POCO class entity with Email and Password properties and put the [FromBody] attribute: Signin([FromBody] Credential credential)
Without using [FromURI] (POST requests with query strings?), how can I make POSTs of multiple parameters or complex objects without converting these parameters into a single POCO class?
Because what if I have numerous Web API POST actions with parameters like (string sensitiveInfo1, string name, int sensitiveInfo2) or (ClassifiedInfo info, string sensitiveInfo1, string sensitiveInfo2), do I need to convert them all to POCO classes and always use [FromBody]?
PS.
I was using RestangularJS before and it can posts anything (mulitple primitive objects and complex objects) without my Web API actions having [FromBody] attributes. Will about to investigate how RestangularJS do it.
Without using [FromURI] (POST requests with query strings?), how can I make POSTs of multiple parameters or complex objects without converting these parameters into a single POCO class?
I know its not what you want to hear but out of the box this is not possible. It is not a limitation of the browser code that is making the request. This means it does not matter if you are using Angular, JQuery, straight JavaScript, or even RestangularJS. This is a limitation (I use that word loosely as I am sure this is by design) of Web API (any version). Here is the documentation on this design: Parameter Binding in ASP.NET Web API by Mike Wasson.
At most one parameter is allowed to read from the message body. So this will not work:
// Caution: Will not work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }
So the question becomes, what are your options?
Create a model
This is the thing you were trying to avoid but I list it first because this is how Web API was intended to behave. I have not yet heard a compelling reason not to do this. This approach allows you to extend your model easily without having to change the method signature. It also allows for model validation on the model itself. Personally I really like this approach.
public class SignInModel{
public string Email {get;set;}
public string Password {get;set;}
}
[HttpPost]
[Route("signin")]
public async Task<IActionResult> Signin(SignInModel signInModel)
{
// ....
}
I did not repeat your existing JavaScript code because what you have works as is with the above web api code
URL
Again, what you were trying to avoid. This does make what you want possible with the limitation that you have to pass these parameters using the Query string on the URL. The JavaScript would change but the signature you had on the Web API method would not.
public signin(inputEmail: string, inputPassword: string): Observable<Response> {
return this.http.post('/api/account/signin/?email=inputEmail&password=inputPassword', null, this.options);
}
I did not repeat your existing Web API code because what you have works as is with the above web JavaScript code (by default FromUri is assumed I believe)
Custom Model Binder
See Passing multiple POST parameters to Web API Controller Methods by Rick Strahl. This option allows you to create a custom model binder that could do what you are asking. It is a whole bunch of extra code though for, IMHO, not much benefit. Maybe there are situations where it would be useful although I really cannot think of any off the top of my head.
Dynamic
Finally you could also pass in a dynamic object as the parameter of your Web API. This is essentially the same as receiving the JSON as a string and making your Controller code responsible for the deserialization of content. Again, I believe that this would make your code worse in most situations as you have to implement custom validation and type checks. This answer was proposed previously on SO by Bes Ley. Again, maybe there are situations where it would be useful although I really cannot think of any off the top of my head.
If you call Web API 2.2 post method from Angular 2 type script, dont forget to add following header content and parameter object.
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
var params = new URLSearchParams();
params.set('userid', '102');
params.set('username', 'foo');
return this._http.post('http://localhost:6579/api/PostUser', params.toString(), { headers: headers }).map(res => res.json());
Perhaps you should post with options:
{
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
}
and encode data like
jQuery.param({user:'bla', password: 'bla'});
WebAPI does not provide this out of the box. If you try to get understanding of web API bindings, you might be able to figure out why.
I think this article might help.
The generic rules are:
– simple, string-convertible parameters (value types, strings, Guids, DateTimes and so on) are by default read from URI
– complex types are by default read from the body
– collections of simple parameters are by default read from the body too
– you cannot compose a single model based on input from both URI and request body, it has to be one or the other
I have fixed the issue of Angular2 HTTP Post ASP.NET MVC Web API
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
let params: URLSearchParams = new URLSearchParams();
params.set('value', '2');
let options = new RequestOptions({
headers: headers//,
//search: params
});
let content = new URLSearchParams();
content.set('StudentName', 'Inderjit Singh';
content.set('Mobile', '+919041165398');
content.set('Nationality', 'Indian');
content.set('AdmissionNo', '6');
content.set('SectionCode', '1');
content.set('Gender', 'Male');
content.set('RegNo', '18585');
content.set('ClassCode', '1');
this.http.post('YOUR_URL', content.toString(), { headers: headers }).map((res: Response) => { console.log("data is==>" + res.text()); }).subscribe();
WebApi will be able to deserialize your Credential object provided the JSON object has the same field names (I am not sure about case so you may be right here). You seem to be missing the headers from the post call in your Angular2 component.
Can you check the Content-Type using Chrome Debugger or Fiddler? It should be application/json.
Try this, passing a complex class object into a single data parameter.
var SearchQuery = function () {
this.Alphabet = null;
this.Search = false;
this.Keyword = null;
this.RegionList = null;
};
var para = new SearchQuery();
{ data: JSON.stringify(para) } - Post Data
you can receive it using a JObject in your API controller and deserialize it as according to your classes.

mvc default model binder only binds first word of a string?

Why does my action method only bind the first word of a string I pass into it using a query string?
For example, in jquery, I build a queryString from the results of an ajax call:
success: return(resultData){
var queryString = "?ok=true&message=" + resultData.message;
}
Then I try to load a view into a dialog by calling a controller and passing the queryString
$dialogHandle.load("/Account/RegisterStatus" + queryString, function() { ... });
At this point the queryString correctly hold an entire message. However if I break in my controller:
public ActionResult RegisterStatus(bool ok, string message)
{
//break here
}
I notice that ok binds correctly but message only contains the first word of the error message passed in.
How can I pass a sentence as one string parameter?
Is there a better way to do this, without query string?
EDIT: hmm now that I think about it does make sense since urls cant have space but then how do I accomplish this... is there a specific word delimiter in the default model binder?
It's all about URL escaping: escape("It's me!") // result: It%27s%20me%21
Do that around your resultData.Message and it should work better. For debugging purposes, use Fiddler2 or some Web Inspector to see what request is being send. This is really valuable when you are debugging AJAX...
And of course, do the reverse in C#: HttpUtility.UrDecode Method (String)

Resources