I've come to some strange behaviour using web api. I'm using attribute routing, and making post on server.
Controller:
[Route("names")]
public ResultDTO SaveData(SomeDTO dto)
{
//somecode
...
}
and i'm calling it
$http.post('/api/mycontroller/names', $scope.model.dto).success(function
(data) { ...
It's working. However, if I rename my method
[Route("names")]
public ResultDTO GetData(SomeDTO dto)
{
//somecode
...
}
it's not working and I get HTTP 405 error The page you are looking for cannot be displayed because an invalid method (HTTP verb) was used to attempt access,
However it's working if I change calling from $http.post to $http.get
Obviously, I won't name my method starting with GetSomeMethod if I'm posting data, but I'm curious, shouldn't defined route
[Route("names")]
work with $http.post, no matter how I actually call method that will handle that post? More specific, why $http.post won't work if I named my method GetSomething, but it will if I change method name to, for example, GotSomething or SaveSomething?
Try to add route attribute
[HttpPost]
and then you can name your action as you wish.
Web API looks at the HTTP method, and then looks for an action whose name begins with that HTTP method name. For example, with a GET request, Web API looks for an action that starts with Get..., such as GetContact or GetAllContacts. This convention applies only to GET, POST, PUT, and DELETE methods.
See more here
Use proper verbs for $http.post(***) - [HttpPost] and for $http.get(***) - [HttpGet]
Related
I have this controller
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
Console.WriteLine("GET Index");
throw new Exception();
}
// POST api/values
[HttpPost]
public void Post()
{
Console.WriteLine("POST Index");
throw new Exception();
}
}
Both the GET and POST request returns a statuscode of 500. This is the expected behavior.
But when I add app.UseExceptionHandler("/api/debug/error");
In the Startup.cs file, the POST request does no longer return a statuscode of 500, instead it returns 404. The GET request is still working as it should by returning a statuscode of 500.
DebugController
[Route("api/[controller]")]
public class DebugController : Controller
{
[HttpGet("error")]
public IActionResult Index()
{
return StatusCode(500,"Hello, World! From debug controller");
}
}
Any idé why adding app.UseExceptionHandler("/api/debug/error"); would make the POST request behave this way?
A repo to reproduce this behavior can be found Here.
When using ExceptionHandlerMiddleware with an ExceptionHandlingPath, as in your example, the main outcome is that the request path gets rewritten to instead use your new path (/api/debug/error in your case). You can see how this works in the source:
if (_options.ExceptionHandlingPath.HasValue)
{
context.Request.Path = _options.ExceptionHandlingPath;
}
If you continue to browse through the source, you'll see that the StatusCode gets set to 500, but the original request is left mostly intact.
In practical terms, this means you are being sent to a POST action on your DebugController, but you have only provided a GET action.
A simple way to fix this would be to make your Index action support both GET and POST, like so:
[HttpGet("error")]
[HttpPost("error")]
public IActionResult Index()
{
return StatusCode(500,"Hello, World! From debug controller");
}
If you want to do something different for POST errors, you can just create a new action, decorated with [HttpPost("error")].
Update: Some of this explanation now exists in the docs:
The exception handling middleware re-executes the request using the original HTTP method. If an error handler endpoint is restricted to a specific set of HTTP methods, it runs only for those HTTP methods. For example, an MVC controller action that uses the [HttpGet] attribute runs only for GET requests. To ensure that all requests reach the custom error handling page, don't restrict them to a specific set of HTTP methods.
To handle exceptions differently based on the original HTTP method:
For Razor Pages, create multiple handler methods. For example, use OnGet to handle GET exceptions and use OnPost to handle POST exceptions.
For MVC, apply HTTP verb attributes to multiple actions. For example, use [HttpGet] to handle GET exceptions and use [HttpPost] to handle POST exceptions.
I think I've been having a similar problem. I made an API controller callable from JavaScript. Everything works fine on development. I have noticed for a while that the API on the live site often returns a 404 error, even though the route is obviously there.
I think I have tracked down the issue which was 500 errors caused by subtle differences on the live site, most notably using a different version of the SQL database on live and dev. So basically my API's GET method was returning a 404 when it should have been returning 500. It sounds as if the solution is to ensure my API methods have both [HttpGet] and [HttpPost] attributes.
One other issue I have noticed is that API calls are sometimes cached if you use:
app.UseResponseCaching();
in Startup.cs. This very much depends on your hosting environment. I append a random number to my API route that resolves this.
I am building an API. But I receive the following error.
The requested resource does not support http method 'GET'
My request is a HTTP PUT and I'm trying to update my database. I have searched for many days but I can't find an answer. Below is my request code. Please help me. Thanks in advance.
request
Code in Controller
[HttpPut]
public void CGNATObjUpdate(int ID)
{
Library.Instances.Value.CGNATObjUpdate(ID);
}
You need to send PUT request aswell. [HttpPut] Allows your method to be triggered for PutRequests only, but in your example you just put link into browser and this simple GET request. Try to use something like Postman to send correct request.
Another way is to check that your controller alows to trigger action by name, for this you need to configure routing for your controller. Just set
[Route("api/[controller]/[action]")]
public class CGNATUpdateApiController : Controller
{
}
Now you can call Action by name, otherwise you will not be able to call Controller action by it`s name, and you need to call Controller:
/api/CGNATUpdateApi?ID=3
just with correct method (PUT) and it will call first PUT method in controller.
I've got a front end WebAPI written with angular and TypeScript that looks like this.
removeSubset(id: number): ng.IPromise<any> {
return this.$http.post(this.api + '/DeleteStudySubset', id)
.then(this.returnData);
}
returnData = (response: any) => {
return response.data;
};
And the back end version it calls out to is written like this
[HttpPost]
[ResponseType(typeof(IHttpActionResult))]
public async Task<IHttpActionResult> DeleteStudySubset(int id)
{
await _subsetRepo.DeleteStudySubset(id);
return Ok();
}
At first I was getting a URI 404 Error and I couldn't figure it out for the life of me. Then I stumbled across the parameter binding [FromBody] attribute and using it fixed the 404 issue.
So the back end API was rewritten to include the attribute and looked like so
[HttpPost]
[ResponseType(typeof(IHttpActionResult))]
public async Task<IHttpActionResult> DeleteStudySubset([FromBody] int id)
{
await _subsetRepo.DeleteStudySubset(id);
return Ok();
}
From what I think I vaguely understand [FromBody] is telling my back end to not reference the url that is pointing to the back end but search the body of what's being sent to it? Is that right? I don't think I'm explaining my hazy understanding well.
My more, to-the-point question is more or less outlined in the title and is: Is using the [FromBody] attribute on my back end to fix my 404 good practice, or is it more of a hack solution?
It feels like it was too easy of a solution and I'm worried if overall it is a non-elegant solution, and I just slapped a quick-fix on top of a more serious issue. Such as not configuring my front or back end API correctly to work in sync with one another.
Your type script is doing a post, and send the id in the body this is why you've got 404, you should change the typescript to do a get and leave the back-end as is, you do not need a body for the request so get should be just fine (send the id in the url of the get call).
Or best practice it would be that the Delete actions to be actually DELETE http requests, to be more REST compliant, so you need to mark the back-end method with HttpDelete and change the type script to do a delete http request instead.
I would recommend second option.
In the interest making your WebApi RESTful, you should treat your routes as addresses for entities and your http verbs as actions. FromBody should only be used with the verb HttpPost when creating an entity.
Decorate your controller with a route prefix using attribute routing [RoutePrefix()]. Attribute routing should be enabled if you are using WebApi 2. If not just search for enabling attribute routing.
[RoutePrefix("api/study-subset")]
Set the route on the method using attribute routing [Route()]. No need to include [HttpDelete] attribute, WebApi will pick the Delete keyword from the method name. Id will be injected from the route into your method. If you didn't include id in the route, it would be treated as a query string.
[Route("{id}")]
public async Task<IHttpActionResult> DeleteStudySubset(int id)
{
await _subsetRepo.DeleteStudySubset(id);
return Ok();
}
Then update your type script to
return this.$http.delete(this.api + 'api/study-subset/' + id)
I have a web api controller and two GET methods:
public class ImagesController : ApiController {
[HttpGet]
public HttpResponseMessage GetImages() { }
[HttpGet]
public HttpResponseMessage Download([FromUri]int[] ids) { }
}
Why I'm getting multiple actions found error, when trying to reach /api/Images, why both actions are the same?
When you created controller, you have assigned HttpGet to two different methods. Making that you have confused web server when it tries to process your request. Since you are sending GET verb to the controller it self, instead directly to the method, web server can not determinate what method should be invoked.
You can try with /api/Images/GetImages in order to directly hit a method, or remove one of listed.
If you see the Web API feature it work for the selected httm methods like GET,PUT,POST,DELETE.
So if you create two action method with same name it will give error. To avoid this error you have to redefine the DefaultAPI path in route.config file.
Change it to
API/{controller}.....
After changing this acces your API from browser like
Or
Mark as a answer if this resolve your issue.
In the event that a Controller specifies a route:
[Route("api/platypus/getPlatypi")]
public List<Platypus> GetAllPlatypi()
{
return _platypusRepository.GetPlatypi();
}
...is there any advantage to annotating it with a "[HttpGet]" like so:
[HttpGet]
[Route("api/platypus/getPlatypi")]
public List<Platypus> GetAllPlatypi()
{
return _platypusRepository.GetPlatypi();
}
?
For the example you have given there is not any advantage to adding the HTTP method attribute. By convention Web API will try to match a controller method that starts with the HTTP request method (GET, POST, PUT etc.).
In your example the method GetAllPlatypi will be considered for a match for all GET requests to that controller.
If however your method was named FindAllPlatypi you would need to add the [HttpGet] attribute to make it clear that this method is meant for GET requests.