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)
Related
I am trying to create a basic API service in ASP.Net Core 3.1.
Before going to the problem description, I have already gone through these questions-
Post parameter is always null
Asp.net Core Post parameter is always null
ASP.NET Core API POST parameter is always null
Web Api Parameter always null
web-api POST body object always null
but, none of fixing my issue.
What I am trying to do is create a basic API which will take a string from the API then use the string and give a response based on the string. What I am doing is like this in Controller-
[Route("/")]
[ApiController]
[EnableCors]
public class XmlValidatorController : ControllerBase
{
........................
........................
[HttpPost("verify_string")]
public ActionResult<ICollection<Certificate>> VerifyXmlString([FromQuery(Name = "xml")] string xml)
//string xml => Giving Null
//[FromQuery(Name = "xml")] string xml => Giving Null
//[FromBody] string xml => Unsupported Media Type - 415
//[FromBody] dynamic xml => Unsupported Media Type - 415
//HttpRequestMessage msg => Unsupported Media Type - 415
{
...............
...............
}
If I am creating a POST request from POST Man, I am creating like this-
and
In my controller, if I am putting a debugging pointer, I am getting this-
So, I am always getting null in POST request.
If I use others in the function parameter, I am getting this errors-
string xml => Giving Null
[FromQuery(Name = "xml")] string xml => Giving Null
[FromQuery(Name = "xml")] string xml => Giving Null
[FromBody] string xml => Unsupported Media Type - 415
[FromBody] dynamic xml => Unsupported Media Type - 415
HttpRequestMessage msg => Unsupported Media Type - 415
Can anyone please help me find the parameter string from the Controller action paramenter/ function parameter (XML).
Re-
I haven't tried with creating a model for the request because I think it will make the code a little more complex and that become overkill as I need just a string.
Re-re-
My Startup.cs file has no special configuration, it is default code provided during code creation. The code for the file can be found in here. And code for controller can be found in here.
Complete Codebase can be found in this Github Repo.
Thanks in advance for helping.
You have defined FromQuery attribute for your parameter. In fact when you post x-www-form-urlencoded form there's a certain content-type specified that tells model binding system all query parameters are actually form fields. So you have to either define FromForm attribute for xml parameter,
[HttpPost("verify_string")]
public ActionResult<ICollection<Certificate>> VerifyXmlString([FromForm] string xml)
either pass it as a query parameter using Params tab in postman.
You are specifying Content-Type: application/json in Postman, but your payload is not JSON. Hence the "Unsupported Media Type" error.
Change the Content-Type to text/xml or application/xml and try again.
In order to populate [FromQuery] params, You have to provide it from URL and in your case, it could be like:
[POST] https://localhost:44377/verify_string?xml=asd
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!
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
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

I'm new to ASP.NET MVC and it's my first time working with an API.
I'm trying to do a PUT, given an object. However, after starting the application and looking at the available API, it shows my PUT URL as the following, without any option for arguments.
/api/File
Shouldn't it be something like /api/File/{}?
Controller
[HttpPut]
public void PutFile (FileData file)
{
...
}
If I'm doing this completely wrong, please let me know!
That URL is correct since the object you are sending should be passed in the body of the request with the correct content type.... probably multipart/form-data if you are uploading a file. If FileData is not a file and just a complex object then you could use application/x-www-form-urlencoded for forms or application/json for AJAX.
tforester answer is correct, but just to add. You need to use the FromBodyAttribute to tell webapi that the non primitive object (e.g. FileData) is expected and it's in the body of the incoming request. e.g.
[HttpPut]
public void PutFile ([FromBody]FileData file)
{
...
}