JayData. OData v.3 Action POST parameters received as null - c#

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


Related

Why is [FromBody] needed for complex types in some cases? [duplicate]

I have the following WEB API method, and have a SPA template with Angular:
[HttpPost]
public IActionResult Post([FromBody]MyViewModel model)
I thought, based on this topic, there is no need to use [FromBody] here, since I want to read the value from the message body, so there is no need to override the default behavior, but, if I don't use [FromBody], the model that is coming from Angular is null. I'm really confused, why should I use [FromBody], since I have used the default behavior?
For anyone seeing this issue .net core 3 - you need to add the [ApiController] to the controller where you extend ControllerBase.
The [FromBody] is only needed if you're doing an MVC controller.
This causes the body to get automatically processed in the way you're expecting.
Microsoft documentation for the ApiController attribute
The question you linked to is referring to web-api. You are using core-mvc which has been re-written to merge the pipelines for the previous mvc and web-api versions into one Controller class.
When posting json (as apposed to x-www-form-urlencoded), the [FromBody] attribute is required to instruct the ModelBinder to use the content-type header to determine the IInputFormatter to use for reading the request.
For a detailed explanation of model binding to json in core-mvc, refer Model binding JSON POSTs in ASP.NET Core.
And here's an alternate approach assuming you need to support both [FromForm] and [FromBody] in your Controller API…
Front-End (Angular Code):
forgotPassword(forgotPassword: ForgotPassword): Observable<number> {
const params = new URLSearchParams();
Object.keys(forgotPassword).forEach(key => params.append(key, forgotPassword[key]));
return this.httpClient.post(`${this.apiAuthUrl}/account/forgotpassword`, params.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
}
Back-End (C# Code):
[AllowAnonymous]
[HttpPost("[action]")]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model) { }
Now your signature can remain the same so it can support both.
And another more permanent approach I thought about while addressing.
https://benfoster.io/blog/aspnet-core-customising-model-binding-conventions.
Hope it helps someone!
See my discussion https://stackoverflow.com/a/75263628/5555938 on [FromBody]. It explains everything in great detail!
But in summary, [FromBody] does NOT accept HTML Form field name-value pairs like [FromForm]. It does NOT accept a traditional HTML form submission! It requires the following:
JavaScript POST Request manually sent to the Web API server
JavaScript Http Header with JSON mime-type attached
JavaScript Http Body with form field extracted data, reformatted and submitted as JSON. Traditional HTML POST name-value pairs will not work!

POST HttpRequestMessage is null

I have a Web API in ASP.NET Core, the method has the following signature:
[HttpPost]
public HttpResponseMessage Foo(HttpRequestMessage data)
This is the data I am sending to the action:
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<DTO
xmlns="http://tempuri.org/">
<field1>1</field1>
<field2>2</field2>
</DTO>
</s:Body>
</s:Envelope>
I am making the request with Postman where Data is a soap xml message.
The headers' Content-Type: :"application/xml"
(also tried : application/x-www-form-urlencoded)
For some strange reason, when I am trying to read this data, I can see the following:
Method: Get
Content: null
Request Url: null
Headers: {{}}
In the Start, I also included XmlFormatters (presuming that this is the source of the problem):
services.AddMvc(options =>
{
options.RespectBrowserAcceptHeader = true;
options.InputFormatters.Add(new XmlSerializerInputFormatter());
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
}
);
But it did not help.
I also have another Web Api (for .NET Framework) where I successfully receive HttpRequestMessage parameter.
What is the source of the issue?
ASP.NET Core does not handle SOAP requests out of the box. You need to add some middleware to handle the SOAP message. I have used this project previously and it has worked really well:
https://github.com/DigDes/SoapCore
There is a detailed walkthrough of how to use the library her:
https://stackify.com/soap-net-core/
Model binding in ASP.NET Core doesn't work the same way as in the previous versions.
So first step was right - you registered XML formatters to allow MVC middleware use it for model binding, as by default only JSON formatter is added.
Next step was also right - you added Content-Type: application/xml header to tell a type of content that you send with HTTP POST.
What you should add, is tell the MVC middleware that your data passed in request body. This is done by adding [FromBody] attribute to the model parameter:
[FromBody]: Use the configured formatters to bind data from the request body. The formatter is selected based on content type of the request.
and cause XML formatter will do deserialization internally, you need to update action parameter to type that represents your XML data structure.
Something like this should be in final:
[HttpPost]
public HttpResponseMessage Foo([FromBody] ClassThatRepresentsXMLstructure data)

WebApi POST works without [FromBody]?

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

How can I POST form data to ASP.NET WebAPI 2?

I'm having trouble accessing parameters submitted via POST in my ASP.NET WebAPI 2 application. I hav a very trivial controller and an action, which simply captures the parameter and returns 200.
public IHttpActionResult Post([FromBody] string foo)
{
return Ok();
}
And I'm making the request via cURL
curl http://localhost:24196/api/checkpoints -XPOST -d foo=bar
The parameter ends up being null. The same happens when I try this with jQuery.
$.post("http://localhost:24196/api/checkpoints", { foo: "bar" })
I've found a post that seems to describe this issue, but I'm not sure if that's really the correct way to fix this (using the = encoding).
What is the correct/standard way of talking to a WebAPI service, and how does the parameter binding actually work in these cases?
As documented in the link, FromBody expects data in a certain way =foo - re: there is no "key". So:
Curl:
curl -X POST -d "=bar" http://localhost/controller
Jquery:
var _data = { "": "bar" };
Update:
Reference for this behavior: Sending simple types
By default, Web API tries to get simple types from the request URI. The FromBody attribute tells Web API to read the value from the request body.
...If you need to get multiple values from the request body, define a complex type.
Second, the client needs to send the value with the following format:
=value
Hth...

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

Categories

Resources