ASP .NET Web API 2 FromForm model binder attribute? - c#

Does ASP.NET Web API 2 (not core) contain also something like [FromForm] attribute for binding the action? Or can I add the dll from ASP.NET Core to a normal ASP.NET Web API 2? {
"Message": "The request entity's media type 'multipart/form-data' is not supported for this resource.",
"ExceptionMessage": "No MediaTypeFormatter is available to read an object of type 'FooBindModel' from content with media type
'multipart/form-data'.",
"ExceptionType": "System.Net.Http.UnsupportedMediaTypeException",
"StackTrace": " at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent
content, Type type, IEnumerable1 formatters, IFormatterLogger
formatterLogger, CancellationToken cancellationToken)\r\n at
System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage
request, Type type, IEnumerable1 formatters, IFormatterLogger
formatterLogger, CancellationToken cancellationToken)" }

No, only FromUri and FromBody is available for ASP.NET Web API 2. You can look at the official documentation.
Also, why would you try to implement FromForm to ASP.NET Web API 2. You can simply configure your form action as GET and POST and pass the data to Web Api. In my opinion, it would be over engineering.

You have to use FromUri for your model and also use HttpContext.Current.Request.Files for getting files from the header
this is your model that set in the Params section
this is your file that set in the Body section ==> from-data
and this is final result

I don't think you actually understand these attributes or what they do. [FromBody] means that you are providing a serialization (JSON, XML, etc.) or basically any other content that doesn't have an application/x-www-form-urlencoded mime type (image or other file type). If you're sending a normal form post (application/x-www-form-urlencoded request body) to a param marked as [FromBody], of course it won't bind, because you're explicitly telling it that it should be expecting something other than what you're sending.
Something like [FromForm] is actually pretty useless anyways, as the default is to expect application/x-www-form-urlencoded, which is all this attribute would do. Long and short, just don't decorate your action param with anything.

Related

ASP.Net Core 3.1 - Post parameter is always NULL in Web API

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

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!

How can I get the IActionContextAccessor from the Endpoint.RequestDelegate when using Endpoint Routing and MVC?

I've got a asp .net core 3.1 application and have configured MVC and Endpoint Routing. Assume I have an Endpoint object (it won't always be the Endpoint associated with the current request), I then have it's RequestDelegate. I'd like to get the IActionContextAccessor from this RequestDelegate. In the following example, when I'm in debug mode I can see the _actionContextAccessor so I know it's there.
var endpoint = this.httpContextAccessor.HttpContext.GetEndpoint();
I'm sure you'd like more context of what I'm doing and I can give you more if you like but the gist of this question is to assume I have an Endpoint object and I'm configured to use MVC, how can I get the IActionContextAccessor?
UPDATE
What I'm ultimately trying to get is the actions parameters. Just the type of the input parameters. We follow a one input model convention so actually what I want is that one input model's type.
To determine the type of an action's parameters, there's no need to use IActionContextAccessor or the ActionContext property it exposes. An Endpoint instance contains a set of metadata: for an endpoint that represents an action, this contains an instance of ActionDescriptor, which, unsurprisingly, describes an action. One of its properties is Parameters, which exposes the set of parameters for that action.
Putting that all together, here's an example of how to get to the type of a single action parameter, as requested:
var actionDescriptor = endpoint.Metadata.GetMetadata<ActionDescriptor>();
if (actionDescriptor != null)
{
var actionParameterType = actionDescriptor.Parameters.SingleOrDefault()?.ParameterType;
// ...
}

OData v4 Custom Action for File Upload

I have an OData controller with standard verbs for CRUD. Everything is working fine. Now I need to add a custom action to perform file upload. I try to add a method to my existing controller like this:
[HttpPost]
[Route("UploadFile")]
public async Task<HttpResponseMessage> UploadFile()
{
//handle uploaded content logic here...
}
But when I try to invoke it by doing a POST:
http://localhost/UploadFile
I get this error:
System.InvalidOperationException: No non-OData HTTP route registered.
What should I do for this custom action to allow file upload?
You need to declare the Action as part of the EdmModel, in the following example I am assuming that your Entity Type is Attachment, and your controller class name is AttachmentsController. By convention, your EntitySet name must then be Attachments
var attachments = builder.EntitySet<Attachment>("Attachments");
attachments.Action(nameof(AttachmentsController.UploadFile))
.Returns<System.Net.Http.HttpResponseMessage>();
The important part of the above statement is the return type, if you do not declare the return type correctly in your EdmModel then you will find your endpoints returning 406 errors - Unacceptable, even though your method executes correctly, which is really confusing the first time you run into it. This is because OData will still try to parse your response to match the Accept header from the request before completing the response.
Try to use 'nameof' when mapping functions and actions instead of 'magic strings' or constants so that the compiler can pickup basic issues like wrongly defined route.
With this approach you do not need the Route attribute on the method header and the action will be included in the metadata document and therefore swagger outputs.

WebApi, OData and ConventionErrors

I am struggling with the OData WebApi way to configure routing - mostly by not being able to get any sensible debugging information.
The API in question is part of a server service (as in: windows service) and as such OWIN based.
I have for example the following function:
function = builder.Function("MktSessions").ReturnsCollection<MktSession>();
function.Parameter<string>("Symbol");
function.Parameter<DateTimeOffset>("Begin");
function.Parameter<DateTimeOffset>("End");
and the controller has the following signature function:
[HttpGet]
[ODataRoute("MktSessions(Symbol={symbol},Begin={begin},End={end}")]
public IEnumerable<Reflexo.Api.MktSession> MktSessions (string symbol, DateTime begin, DateTime end) {
SOMETHING is wrong here. As long as the ODataRouter attbribute is presend, any call to $metadata resunlts in:
An error has occurred.
The object has not yet been initialized. Ensure that
HttpConfiguration.EnsureInitialized() is called in the application's
startup code after all other initialization code.
System.InvalidOperationException
at
System.Web.OData.Routing.Conventions.AttributeRoutingConvention.get_AttributeMappings()
at
System.Web.OData.Routing.Conventions.AttributeRoutingConvention.SelectController(ODataPath
odataPath, HttpRequestMessage request) at
System.Web.OData.Routing.ODataPathRouteConstraint.SelectControllerName(ODataPath
path, HttpRequestMessage request) at
System.Web.OData.Routing.ODataPathRouteConstraint.Match(HttpRequestMessage
request, IHttpRoute route, String parameterName, IDictionary`2 values,
HttpRouteDirection routeDirection) at
System.Web.Http.Routing.HttpRoute.ProcessConstraint(HttpRequestMessage
request, Object constraint, String parameterName,
HttpRouteValueDictionary values, HttpRouteDirection routeDirection) at
System.Web.Http.Routing.HttpRoute.ProcessConstraints(HttpRequestMessage
request, HttpRouteValueDictionary values, HttpRouteDirection
routeDirection) at
System.Web.Http.Routing.HttpRoute.GetRouteData(String virtualPathRoot,
HttpRequestMessage request) at
System.Web.Http.HttpRouteCollection.GetRouteData(HttpRequestMessage
request) at
System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage
request, CancellationToken cancellationToken) at
System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage
request, CancellationToken cancellationToken) at
System.Web.Http.HttpServer.d__0.MoveNext()
which is as useless as it gets as an error message. I never have a chance to see original exception and this one jsut tells me the config is not there - which has no direct resemblence of the original error.
If I remove the ODataRoute attribute it works - but I can obviously not call the function.
Is there any way I am overlooking to actually get a meaningfull error message from this? Obviously the ODataRoute is somewhere in error (anyone knows where?) and a sensible "Parameter name blablbla does not match" text somewhere would be really helpfull.
There are two issues with the code your paste,
1. In the ODataRoute, you miss ")" after "{end}"
2. You should have define MktSessions as entity set, so your unbound function route can not be same as an entity set, you can change it to something else like RetrieveMktSessions as EntitySet query is enable by get method in controller but not a unbound function.
Let us know if you have any more issues.
Answering myself.
Now, on the side question there is a missing ")" at the end of the template.
More important, though.
When I add a call to HttpConfiguration.EnsureInitialized() to the end of the Owin configuration then I get the exception thrown there. This exception - while not having an inner exception - contains a meaningfull message that is lost in the web page output. This allows much better debugging.

Categories

Resources