This question suddenly pops up in my mind.
In Startup.cs I have:
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
app.UseWebApi(config);
When I have a method like this one:
[RoutePrefix("api/Order")]
public class OrderController : ApiController
{
// don't use any attribute here
public IHttpActionResult HelloWorld()
{
...
return Ok();
}
}
Is it possible to access HelloWorld()?
Should a GET or POST or whatever action be sent?
You can access to HttpWorld() using GET if you rename your method as: GetHelloWorld().
The same with POST renaming to PostHelloWorld().
But I prefer using [HttpGet], [HttpPost], ... attributes, even if my action methods has the "Get" or "Post" characters in the name, to avoid possible errors.
Updated
After making some tests, I realised that my comments about that is not possible to call the HelloWorld are not correct.
Indeed it is possible to call your HelloWorld() method if you make a POST call to http://<YourProjectUrl>/order.
So, the default method will be POST and, as you haven't configured any Route for your action method (take in account that RoutePrefix is just a prefix, so needs a Route attribute to be considered), it will get your controller name without "Controller" (OrderController -> Order).
Related
When using only attribute routing in an ASP.NET Core 5.0 app, for example, with the following in Startup:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
A controller class with a single public method that has no route attribute will still be matched to a GET request that otherwise matches that controller.
For example GET api/test will match the Index method below (it doesn't matter what the method is called) even though the method doesn't have a route attribute:
[ApiController]
[Route("api/[controller]")]
public class TestController
{
public IActionResult Index()
{
return Ok();
}
}
If, however, another public method is added to that controller class, also without an attribute, an AmbiguousMatchException is thrown for GET api/test because routing can't otherwise figure out which method to use.
If a [HttpGet] attribute is added to one of those methods, the issue is resolved.
I understand that one solution is to just ensure that all methods have appropriate route attributes, or that only one method on the controller is public.
Is there a way to prevent this default/fallback behaviour where methods that don't have explicit attributes are still matched?
Can attribute routing be configured to only match methods that have explicit routing attributes on them?
Setting the route to:
[Route("api/{controller}/{action}")]
Will indicate that action has to be specified, it will not default to anything unless you specify default action like this:
[Route("api/{controller}/{action=Index}")]
Edit:
If you want to be forced to be explicit about the routes, then you can remove the [Route()] attribute from controller level and add it above every action like this
[ApiController]
public class TestController
{
[Route("api")]
[Route("api/[controller]")]
[Route("api/[controller]/index")]
public IActionResult Index()
{
return Ok();
}
[Route("api/[controller]/asdf")]
[Route("api/[controller]/asdf/{id?}")]
public IActionResult Index2(int? id)
{
return Ok();
}
}
The code will then not compile unless every action has a Route() attribute or other similar attributes like HttpGet(), HTTpPost() etc.
Currently I have the following in my Startup.cs class:
app.UseMvc(routes =>
{
routes.MapRoute("default", "api/{controller}/{action=Get}");
});
All my MVC controllers have a method Get on them, and a number of them have a Post as well. Something like this:
public class ExampleController : Controller
{
[HttpGet]
public MyType Get()
{
return GetMyTypeFromSomewhere();
}
[HttpPost("api/[controller]")]
public void Post([FromBody]MyType updated)
{
//do some stuff with the new instance
}
}
Currently I need to have ("api/[controller]") on the Post method so that post requests will reach it.
I wish to be able to remove this and redirect POST requests to controller Post methods by default. Problem is, if I do so in the current state, HTTP POST requests will end up posting to /api/Example/Get.
I have looked into MapGet and MapPost, but the only implementations of these I can find short circuit the pipeline, handling and responding within the RequestDelegate passed. My controller methods are never reached this way.
How can I achieve this?
EDIT
It would probably be worth adding, some controllers have extra GET and POST methods on them which are reached at api/controller/action, meaning I need to have a route to {action} specified somewhere. However doing so means that my default Get and Post methods are no longer accessible by just the controller name, hence adding =Get as a default action in MapRoute
Of course every request will use the Get method because you told it to default it to:
routes.MapRoute("default", "api/{controller}/{action=Get}");
If you specify the literal segment (action token), i.e., /api/example/get, MVC sees that as the top-most level in the pecking order. That's why it always pick Get action inside your controller because you have specified the action token as well in the url.
Don't do that with web api!
Instead, web api uses HttpVerb to map the route. So you shouldn't specify the HttpVerb along with the Url. You supply the HttpVerbas the method type along with the request instead.
Think about api routes as identifying a resource / resource collection. It's different than how you think with MVC (RPC style).
Change to these and give it a try:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvcWithDefaultRoute();
}
Customer resource
[Route("api/[controller]")]
public class CustomersController : Controller
{
[HttpGet]
public IActionResult Get()
{ ... }
[HttpPost]
public IActionResult Post()
{ ... }
}
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]
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.
Hi I am using wep api 2 to create this route. I have
in my WebConfigApi defined
config.MapHttpAttributeRoutes();
// In Globa.asax
GlobalConfiguration.Configure(WebApiConfig.Register);
My method is decorated as
[Route("api/pO")]
[AcceptVerbs("Getpo")]
[HttpGet]
public IEnumerable<pOver> Getpo()
{
------
}
When I run this using
../api/pO/Getpo
I keep getting error
"<Error><Message>No HTTP resource was found that matches the request URI 'http://localhost:60482/api/pO/Getp0'.</Message><MessageDetail>No action was found on the controller 'prO' that matches the request.</MessageDetail></Error>"
Please let me know how to decorate it properly to run it.
Thanks
I think you want it to look like this:
[Route("api/pO/Getpo")]
[HttpGet]
public IEnumerable<pOver> Getpo()
{
------
}
The AcceptVerbs attribute is meant to do something similar to the HttpGet attribute that you have put on your Action. you could use AcceptVerbs("GET") in place of the HttpGet attribute.