WebApi POST works without [FromBody]? - c#

I have this controller action :
[HttpPost]
[ActionName("aaa")]
public HttpResponseMessage aaa(Z z) //notice - no [FromBody]
{
return Request.CreateResponse(HttpStatusCode.OK, 1);
}
Where Z is :
public class Z
{
public string a { get; set; }
}
But when I post via fiddler :
POST http://localhost:11485/api/profiles/aaa HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: localhost:11485
Content-Length: 3
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,he;q=0.6
Cache-Control: no-cache
Connection: keep-alive
a=1
I can actually see it is working :
Question:
If so , how does it working without the [FromBody] attribute ? and do I still need /not write this attribute ?
Also , what is the scenario where not writing this attribute - will cause problems ?

Binding is different for MVC and Web API. By default, ASP.NET Web API binds complex types from the request message body and simple types from URI, query string, etc. Since you specified Z, which is a class (complex type), it populates the action method parameter from body without you having to specify [FromBody]. On the other hand, if your request is http://localhost:11485/api/profiles/aaa?z=1 without a body, it will NOT bind to your complex parameter automatically. In that case, you will need to specify [FromUri] like this: public HttpResponseMessage aaa([FromUri]Z z).
On the other hand, say your action method is public HttpResponseMessage aaa(string a). We now have string, which is a simple type. In this case, http://localhost:11485/api/profiles/aaa?a=1 without a message body will let Web API bind the parameter correctly without you having to specify [FromUri]. Now, if you want to bind from body in this case, you will need to specify public HttpResponseMessage aaa([FromBody]string a). Of course, for this body must be =1, for application/x-www-form-urlencoded, without the key name of a.
Bottom line - your parameter (simple type or complex type) determines how Web API binds. To make it work differently from the default behavior, you need to tell via FromUri or FromBody.
PS. Whatever I mentioned above holds good purely for the good old ASP.NET Web API (including 2). In ASP.NET 5.0 a.k.a ASP.NET vNext or ASP.NET MVC 6.0, MVC and Web API have been unified. In order for it to bind a complex type from body, you must specify [FromBody].

Related

How to use multiple parameters when sending data as application/json in controller

I have the following question:
Currently, I do have an ASP.Net Core Application set up with some generic code in the frontend, which sends all POST requests with a JSON payload and with application/json set as content type.
My controllers accept these calls like this for example:
public async Task<JsonResponse> AddToPortfolio([FromBody] AddPortfolioUiModel addPortfolioUiModel)
{
...
}
So, when my frontend code sends a POST for this controller action, it will properly read the parameter UiModel from the JSON.
Good enough.
However, sometimes I do have controller calls which might only require receiving a single string. As of now, I would have to wrap this in a model and tag it as [FromBody]. This is fairly annoying to do, and I was wondering if there is no easier way to just accept it like this instead:
public async Task<JsonResponse> ControllerMethod(string myParam)
{
...
}
Is this easily possible while still keeping the application/json content type attached to all requests coming from my JS client? I don't really want to juggle around with multiple content types, so this would be great.

Metadata document shows a POST based sample for a GET based DTO

I have the following DTO where the URI should be like api/logs?verbose=, where verbose can be true or false.
[Route("/api/logs", "GET")]
public class GetLogs
{
public bool Verbose { get; set; }
}
My service is:
public object Get(GetLogs getLogs)
{
//For brevity
return getLogs;
}
I see that the sample that generated in metadata document mentions POST instead of GET, which I explicitly set.
POST /json/oneway/GetLogs HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: length
{"Verbose":false}
How can I fix this?
To my knowledge, that is just a sample. Don't take the "POST /json/oneway/GetLogs HTTP/1.1" literally. You should be using the actual Route endpoints shown earlier in the same page, which would likely be:
The following routes are available for this service:
GET /api/logs
In my ServiceStack projects, they are all the same way - the sample is POST, even if the available routes are only: GET, OPTIONS
I did some searching, and this appears to be hardcoded in the class: ServiceStack.Metadata.OperationControl
So you could accept you "can't do it" with the current release. Or figure out how to override or patch OperationControl template rendering.

How to get .NET MVC to bind POST parameters when Content-Type is text/plain

I have an IE8/IE9 CORS request using XDomainRequest of type POST coming into an ASP .NET MVC 3 web application. The linked blog post indicates that:
Only text/plain is supported for the request's Content-Type header
Now, since the Content-Type is text/plain and this is outside my control, the MVC framework will not bind the POST content parameters. It only seems to bind them when the Content-Type is application/x-www-form-urlencoded.
I cannot read the parameters from the Request object either.
I need to find a way for MVC to bind these parameters. I thought about trying to modify the Content-Type of the request on Application_BeginRequest but this didn't work.
How can I get the MVC Framework to bind the POST parameters when the Content-Type is text/plain?
Update
I believe the parameters will be available through the Request.InputStream property. I'm looking for a way to generically bind these parameters using the MVC Framework default binding. I'd prefer not to have to write a model binder for every model in my project.
Check if:
You really are sending a x-www-form-urlencoded. Maybe you are sending a JSON?
The response includes Access-Control-Allow-Origin header
I tried the following and it worked:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_BeginRequest()
{
if (String.IsNullOrEmpty(Context.Request.ContentType) && Context.Request.HttpMethod == "POST")
{
Context.Request.ContentType = "application/x-www-form-urlencoded";
}
}
protected void Application_EndRequest()
{
Context.Response.AddHeader("Access-Control-Allow-Origin", "*");
}
}
Note: For better modularity, you may want to implement Application_BeginRequest as a HTTP module and CORS (the Access-Control-Allow-Origin) as an ActionFilter
No real experience in this subject, but here you can see some approaches thar could help you.
Check this SO Questions and workarounds:
Cors, Web Api, IE8, Post Complex Data
Posting text/plain as a complex object in WebAPI with CORS
Here is how it can be done in ASP.NET WebAPI:
http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

JayData. OData v.3 Action POST parameters received as null

My OData POST Action receive the parameters as null because it gets them from JayData client as part of the body and is expecting them as part of the URI.
I have created an OData service based in WCF Data Services 5.6.0 and Llblgen Pro 4.0 (simply the best .Net ORM out there right now). It has a POST Action:
[WebInvoke(Method = "POST")]
public void DeletePeople(string guidsToDelete) {...}
The OData v.3 standard does not support passing parameters to POST actions as part of the body, but expect them to be in the queryString part of the URI. That is, in System.Data.Services.Client, you have to set them as UriOperationParameter instead of BodyOperationParameter.
Can I configure in JayData's ServiceOperation definition where to send the parameters, or must I assume it does not support POST with parameters right now?
// This works, of course :-)
// JayData client. EntityContext definitions. Changed to GET instead of POST
'DeletePeople': { type: $data.ServiceOperation, method: 'GET', params: [{ name: 'guidsToDelete', type: 'Edm.String' }] }
// Updated server operation (not action any more):
[WebGet]
public void DeletePeople(string guidsToDelete)
TIA,
Raist
JayData expects service operations published via WebGet attribute right now. You are right about the OData standard, it does expecti the params of POST operations in URL parameter, but it's strange because both classic WCF and WebAPI uses params in the body... it's a standard, so it must be followed. In case you cannot use WebGet attribute, feel free to propose the feature that supports POST invoke method:
JayData backlog
JayData Github issue list
I think you are confusing Actions with Service Operations.
Actions may have a side effect, service operations must not have a side effect. Service Operations are marked as a legacy feature in OData v3.0, as Functions can achieve the same result. Note that Functions do not use POST - they must use the GET method and therefore pass any parameters in the query string.
You are best to refer to the protocol specification document which is the complete specification (the online content is not complete).
According to the OData v3.0 specification, any parameters associated with an action are passed in the request body (not request URI), using the POST method. Here's the action example from the specification document:
HTTP Request:
POST /Customers('ALFKI')/SampleEntities.CreateOrder HTTP/1.1 Host: host
Content-Type: application/json;odata=verbose DataServiceVersion: 3.0
MaxDataServiceVersion: 3.0
If-Match: ...ETag...
Content-Length: ####
{
"quantity": 2,
"discountCode": "BLACKFRIDAY"
}
HTTP Response:
HTTP/1.1 204 OK
Date: Fri, 11 Oct 2008 04:23:49 GMT


Web Api not binding to model

I'm trying to post a JSON object to a Web Api URL and it's not binding to the model.
This seems to be the same problem: ASP.Net Web Api not binding model on POST
I tried everything that they did and it still doesn't work. The one difference you may notice is that I'm not using the DataContract attributes, but I don't believe they should be required, and didn't make any difference when I tried them.
public class MyModel
{
public int Id { get; set; }
}
Public class MyController : ApiController
{
public int Save(MyModel myModel)
{
// myModel is always null
return 0;
}
}
You appear to be missing [HttpPost] attribute from your controller method. It appears in the above case this is actually not strictly required, perhaps this is only needed when posting primitives?
Also just as a note I would use a more REST based syntax if you are using WebApi for example use methods Get, Post, Put ect on your controller rather than named methods
EDIT:
You also have one other really subtle issue with your post. A header line cant end with a ; so Content-Type: application/json; charset=utf-8; should be Content-Type: application/json; charset=utf-8
It could be an encoding issue.
I changed my encoding and model binding is done succesfully.
client.Encoding = Encoding.UTF8;

Categories

Resources