I am writing web api method which retrieves list of countries. Name of the method is GetAllCountries. Methods accepts many parameters (like pageno, pagesize, sortorder,other filters etc.). Instead of creating method with many arguments I have created Dto which has properties mentioned above. Since Get method in web api can accept only primitive datatypes or simple dto's, I have chosen GetAllCountries as post method (it is decorated with HttpPost attribute) so that it can accept complex dto.When I am invoking GetAllCountries method from client, I am getting error "StatusCode: 405, ReasonPhrase: 'Method Not Allowed', Version: 1.1, Content: System.Net.Http.StreamContent, Headers".
I know my method name is against web api convention(meaning method name starts with Get but it is actually a post). I have checked everything (like url etc) but still I could not figure why error is coming.
Following is the Code
[HttpPost]
public GetCountryListResponse GetAllCountries(GetAllCountriesRequest request)
{
return DestinationBl.GetAllCountries(request);
}
Sounds like you might have a problem with the method's input.
Try to add [FromBody] before the input parameter:
[HttpPost]
public Countries GetAllConuntries([FromBody]YorObject obj)
{}
Related
I am writing a REST API in .net core. I am trying to test the API using Postman and I am getting an error saying
Failed to load resource: the server responded with a status of 404 ()
I know this error occurs when the route does not match. Not sure, what am I doing wrong with the route. Below is my code with the Route at the top:
namespace RecLoad.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class RecLoadPrimeController : ControllerBase
{
[Route("RecLoadPrime/insertRecLoadData/{RecStartDate}/{RecEndDate}")]
[HttpPost]
public void insertRecLoadData(string RecStartDate, string RecEndDate)
{
RecLoadDataProvider dataProvider = new RecLoadDataProvider();
dataProvider.InsertCardsData(RecStartDate, RecEndDate);
}
}
}
The URL that I am trying to test in Postman is below:
https://localhost:44360/api/RecLoadPrime/insertRecLoadData/?RecStartDate=01/01/2020&RecEndDate=01/02/2020
I am very new to API, this is the first API that I am writing. Below is the image for application structure. Its extremely simple:
Any help will be greatly appreciated.
A 404 error means not found. This means Postman cant find the end point you are trying to hit.
Your [Route] attribute needs to be updated. The root of this endpoint (controller) it's RecLoadPrime. So get rid of that part. If you are just trying to test, update it to [Route("insert")].
Using ? in your URL means you are passing query parameters. Which are usually used on GET requests not on POST requests.
Web API expects you to use Model Binding for passing in parameters. Meaning map the post parameters to a strongly typed .NET object, not to single parameters. Alternatively, you can also accept a FormDataCollection parameter on your API method to get a name value collection of all POSTed values.
For example: Create a small class called Card, with the properties startDate, and endDate. Make them DateTime. Now use that in the method signature public void insertRecLoadData([FromBody]Card card)
In Postman, you are now going to use the Body option and create a JSON representation of this new class we created.
For example: { "startDate": "2020-03-23", "endDate": "2020-03-27" }
In the route, you are going to use: POST | https://localhost:44360/api/insertRecLoadData/insert
Make sure you set breakpoints in your controller. Not sure how you have setup your project but I'd suggest reading up more on how to setup a Web API using ASP.NET Core. Look into RESTful design to also get an idea on how to best setup these end points.
Good luck!
The current route configuration on your controller and on your action will result in duplicated section in your route. Specifically, the route the action will be associated with will be "api/RecLoadPrime/RecLoadPrime/insertRecLoadData/{RecStartDate}/{RecEndDate}".
Consider removing the RecLoadPrim/ prefix from your action route attribute as follows:
[Route("insertRecLoadData/{RecStartDate}/{RecEndDate}")]
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 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.
I have two simple routing methods:
main routing directive:
[RoutePrefix("api/accounts")]
first one
[Route("user/{id:guid}", Name = "GetUserById")]
public async Task<IHttpActionResult> GetUser(string Id)
second one
[Route("user/del/{id:guid}")]
public async Task<IHttpActionResult> DeleteUser(string id)
I wonder why if I test the first method with direct ulr ( GET ) it works:
http://localhost/ReportApp_WebApi/api/accounts/user/1533882b-e1fd-4b52-aeed-4799edbbcda6
And if I try the second link that is just a little different:
http://localhost/ReportApp_WebApi/api/accounts/user/del/1533882b-e1fd-4b52-aeed-4799edbbcda6
I get: The requested resource does not support http method 'GET'.
Can you help me?
The second link is not just a little different. In fact it is pointing to the route that you have defined for your delete method. This method expects DELETE Http verb.
When you write the url directly on the browser it performs a GET request. If you want your delete method to work with a GET verb you have to put the attribte
[HttpGet]
before or just after your Route atttribute. Although this I would not recommend to do it. You can have a HTTP client like Fiddler or Postman to test this
Web Api uses conventions when naming methods in your controllers. Because you named your method DeleteUser, Web Api expects Delete verb.
EDIT>>>Please follow recommendations listed in comments. I have also make bold the part where I do not recommend this
Since a few days I'm trying to create my own web api controller. Duo to the rest conventions I need to use a post request to create an object. To get concrete, Im having this controller with this action:
public class ReservationController : ApiController
{
[HttpPost]
public void Create(int roomId, DateTime arrivalDate)
{
//do something with both parameters
}
}
This code is not working when I fire a post request at it, I'm receiving a 404 exception something like this:
No action was found on the controller 'Some' that matches the request.
The reason for it is that simple types are read from the query string, complex types from the body, according to this aricle. The web api uses the parameters to match the action to a request and can't therefore map my action to the request.
I do know that I can use the [frombody] tag, but you can only apply that to one parameter and I have 2. I also know that I can create a wrapper object which have both the parameters, but I'm not willing to use wrappers for all my calls.
So I do know that I can work around this by these methods. I also think that this is caused by the fact that the body of the post request can only be read once. But my actual question is:
Why is the source of a parameter determined by it's type and not by it's availability, especially when the conventions state that you should make for example a post request for creation? In MVC this is the case, why isn't it in the web api?
Best regards,
BHD
FINAL UPDATE
Since I'm getting some upvotes, problably more people are facing the same question. In the end it comes to this: Web-Api != MVC. It's simply not the same thing and the web api team made different design decisions than the mvc team I guess.
It seems that you have a fundamental misunderstanding of how Web API actually works.
Web API routing is driven off of verbiage, not the method names. "SomeMethod" actually translates to zero useful information for Web API. As a result, if I post
api/some/some?id=1
OR
api/some/somemethod?id=1
OR EVEN
api/some/?id=1
and the SomeMethod endpoint is the ONLY available POST, it will hit that endpoint.
As such, first of all, make sure you have only one POST on that api controller. If you do, POSTing to it from any test client using either of the query strings above will work just fine.
You can use the [FromBody] attribute on the parameter to force it to read from the body of the HTTP POST instead of the Uri. This is opposed to the [FromUri] attribute which does the opposite.
[HttpPost]
public void SomeAction([FromBody] int id)
{
//do something with id
}
Are you sure you're actually putting the id in the body? It could also be a routing issue. If this still doesn't work then maybe you should use Fiddler and copy the RAW output of your HTTP message here.
If you're packing multiple values into the body such as with JSON then you should use a model which should automatically be deserialized to:
public class PostModel
{
public int ID { get; set; }
public int SomeOtherID { get; set; }
}
[HttpPost]
public void SomeAction(PostModel postModel)
{
//do something with postModel.ID and postModel.SomeOtherID
}
You can actually do this straight out of the box in WebAPI, at least in 2.2 (.Net version 4.5.2). Your controller is correct. Using your controller, if you call it with a HTTP POST like this (tested through Fiddler):
http://localhost:58397/api/Reservation?roomId=123&arrivalDate=2015-12-17
You'll get the correct values of roomId = 123 and arrivalDate = 17.12.2015.
I suspect there's something wrong in your call to the WebAPI. Maybe post that call if you're still not getting it to work.