I have a weird situation... I have this api methods:
[Route("api/something")]
public class MyController : Controller
{
[HttpGet("test-{id}")]
public Task<T> method1() { ... }
[HttpGet("test-something-{id}")]
public Task<T> method2() { ... }
}
I want to call api/something/test-something-1 but my api calls api/something/test-1
why?
The route "test-{id}" matches api/something/test-something-1 where template parameter would end up as id = something-1.
That is the reason why when you call api/something/test-something-1 that it calls method1 with route template test-{id}
When there are route conflicts like this then you should use route constraints to better differentiate routes.
[Route("api/something")]
public class MyController : Controller {
[HttpGet("test-{id:int}")]//Matches GET api/something/test-1
public Task<IActionResult> method1(int id) {
//...
}
[HttpGet("test-something-{id}")]//Matches GET api/something/test-something-any_id_here
public Task<IActionResult> method2(string id) {
//...
}
}
You can apply route constraints to the second one as well if the id is suppose to be an int as well.
[HttpGet("test-something-{id:int}")]//Matches GET api/something/test-something-1
public Task<IActionResult> method2(int id) {
//...
}
Reference: Routing in ASP.NET Core : Route Constraint Reference
Class declaration is incorrect
[RoutePrifix("api/something")]
public Class blabla : Controller
{
[HttpGet("test-{id}")]
public Task<T> method1{}
[HttpGet("test-something-{id}")]
public Task<T> method1{}
}
In route config file you have to enable attribute routing.
routes.MapMvcAttributeRoutes();
Related
I have an API controller say TestController.
I have the following route definition: api/[controller] on class level and I can call the default get method as follows: .../api/test
I want to have another method in the same controller, I also want to call it with Get and the link should be as follows: .../api/test-abc
What is the best way to differentiate this second method within the same controller class.
For test-abc, you can configure the Route attribute as ~/api/test-abc to override the controller base route. See this link for details.
[Route("api/[controller]")]
public class TestController : ControllerBase
{
public IActionResult Get()
{
// ...
}
[HttpGet]
[Route("~/api/test-abc")]
public IActionResult GetAbc()
{
// ...
}
}
This approach changes only the URL of the "test-abc"-action; all other actions of the controller use the base URL configured on class level.
In .Net 6 by following Minimal API's
app.MapGet("api/test", () =>
{
return "";
});
app.MapGet("api/test-abc", () =>
{
return "";
});
By following Traditional Method
[Route("api")]
public class TestController : ControllerBase
{
[HttpGet("test")]
public IActionResult Get()
{
// ...
return Ok();
}
[HttpGet("test-abc")]
public IActionResult GetAbc()
{
// ...
return Ok();
}
}
You can by defining the controllers route template like as below:
[Route("api/[controller]")]
public class TestController : Controller
{
...
}
So you can define a method like:
[HttpGet]
[Route("~/api/Test/Index")]
public IActionResult Index()
{
return Foo.Bar();
}
[HttpGet]
[Route("~/api/Test/Index2")]
public IActionResult Index2()
{
return Foo.Bar2();
}
[HttpGet]
public IActionResult Index3()
{
return Foo.Bar3();
}
You can call it by ../api/test/index2 to use the method Index2 or ../api/test to use Index3.
This is for the standardized way to do, in your case you would use - instead of / at the Route("...") attribute.
If you use - you need to call ../api/test-index2
For further information take a look at documentation.
I was wondering that if we use RoutePrefix attribute in our web api controller with a different name from controller's actual name. So would it work or not?
As far as i did
[RouterPrefix("quotation")]
public class SaleOrderController : ApiController { ... }
if we define RoutePrefix like above we can't access it via /quotation but we can access it using saleorder.
So what is RoutePrefix for or am i doing something wrong ?
To use default route use Route("")
[RoutePrefix("quotation")]
public class SaleOrderController : ApiController {
//GET quotation
[Route("")]
[HttpGet]
public IHttpActionResult GetAll() { ... }
}
Source: Attribute Routing in ASP.NET Web API 2 : Route Prefix
In order for it to work, you need to call the code below inside your WebApiConfig.Register() method:
config.MapHttpAttributeRoutes();
So your RoutePrefix works as exptected:
[RoutePrefix("quotation")]
public class SaleOrderController : ApiController
{
[Route("example")]
[HttpGet]
public IHttpActionResult Example()
{
return Ok();
}
[Route("another")]
[HttpGet]
public IHttpActionResult Another()
{
return Ok();
}
}
So your could access your apis like this:
quotation/example
quotation/another
In the documentation of ODATA's WebAPI there is a page about Attribute Routing.
In this page, there is an example about using ODataRoutePrefixAttribute when all requests to a particular controller have the same prefix, and this prefix can include a parameter. In the sample, all action methods declare the same parameter. From their sample:
[ODataRoutePrefix("Customers({id})")]
public class MyController : ODataController
{
[ODataRoute("Address")]
public IHttpActionResult GetAddress(int id)
{
......
}
[ODataRoute("Address/City")]
public IHttpActionResult GetCity(int id)
{
......
}
[ODataRoute("/Order")]
public IHttpActionResult GetOrder(int id)
{
......
}
}
I would like to avoid repeating the parameter in each and every method and just have it be a property of the class, like this:
[ODataRoutePrefix("Customers({id})")]
public class MyController : ODataController
{
public int Id
{
get { ... }
}
[ODataRoute("Address")]
public IHttpActionResult GetAddress()
{
......
}
}
How to get the value of the id parameter from the URL when it is not passed as parameter to the action method?
I found out that I could implement the property getter by reading the value from the RequestContext.RouteData.Values:
public string Id => (string)this.RequestContext.RouteData.Values["id"];
One drawback of this solution is that route data values do not seem to be available during the controller's Initialize method, so one needs to be cautious not to depend on such properties in there.
I have two Controllers as follows:
[RoutePrefix("v1/user/something")]
public class SomethingsController : ApiController
{
[Route("{id}")]
[HttpGet]
[ResponseType(typeof(SomethingsViewModel))]
public async Task<IHttpActionResult> GetAsync([FromUri]int id)
{
}
}
[RoutePrefix("v1/user")]
public class UserController : ApiController
{
[Route("{id}")]
[HttpGet]
[Authorize(Roles = "Super Admin")]
public async Task<IHttpActionResult> GetByIdAsync([FromUri]int id)
{
}
}
Now by looking at the code above, I'd think that the following two routes are being created:
v1/user/something/{id}
v1/user/{id}
But unfortunately, for some reason, that is not the case. I keep getting the following exception message when trying to access one of the above routes:
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types: MyProject.Api.Controllers.UserController, MyProject.Api.Controllers.SomethingsController
Please help me out in figuring what I might be doing wrong or which small detail am I missing out here.
Though their route prefix are different their resolved routes match. for example v1/user/{id} will match v1/user/something/{id} where id parameter arg in the first route will take something/{id}.
Route prefix and Route attributes combine to create a full route that is added to the route table.
In a case like this you will need to use constraints in order to better differentiate the routes.
[RoutePrefix("v1/user/something")]
public class SomethingsController : ApiController {
[Route("{id:int}")]
[HttpGet]
[ResponseType(typeof(SomethingsViewModel))]
public async Task<IHttpActionResult> GetAsync([FromUri]int id) { ... }
}
[RoutePrefix("v1/user")]
public class UserController : ApiController {
[Route("{id:int}")]
[HttpGet]
[Authorize(Roles = "Super Admin")]
public async Task<IHttpActionResult> GetByIdAsync([FromUri]int id) { ... }
}
So now with the int constraint something wont be mistaken for valid parameter for the UserController.GetByIdAsync action
Reference Attribute Routing in ASP.NET Web API 2: Route Constraints
Route Constraints
Route constraints let you restrict how the parameters in the route
template are matched. The general syntax is "{parameter:constraint}".
For example:
[Route("users/{id:int}"]
public User GetUserById(int id) { ... }
[Route("users/{name}"]
public User GetUserByName(string name) { ... }
Here, the first route will only be selected if the "id" segment of the
URI is an integer. Otherwise, the second route will be chosen.
I am working on an asp.net 5 mvc api, and I am currently working on the Accounts Controller.
since I saw in many different places that there is a convention of using /api/Tokenrouting to a login in a web api. I would like to route to that specific method without the accounts prefix, I would prefer not using a different controller, and I would prefer using Attributes over routing in Startup.cs to avoid confusion in the future.
this is what I have currently
[Route("api/[controller]")]
public class AccountsController : Controller
{
[HttpPost("login")]
public async Task<JwtToken> Token([FromBody]Credentials credentials)
{
...
}
[HttpPost]
public async Task CreateUser([FromBody] userDto)
{
...
}
}
With attribute routing you can use a tilde (~) on the Action's route attribute to override the default route of the Controller if needed:
[Route("api/[controller]")]
public class AccountsController : Controller {
[HttpPost]
[Route("~/api/token")] //routes to `/api/token`
public async Task<JwtToken> Token([FromBody]Credentials credentials) {
...
}
[HttpPost]
[Route("users")] // routes to `/api/accounts/users`
public async Task CreateUser([FromBody] userDto) {
...
}
}
For ASP.NET Core it seems that the tilde ~ symbol (see accepted answer) is not needed anymore to override the controller's route prefix – instead, the following rule applies:
Route templates applied to an action that begin with a / don't get combined with route templates applied to the controller. This example matches a set of URL paths similar to the default route.
Here is an example:
[Route("foo")]
public class FooController : Controller
{
[Route("bar")] // combined with "foo" to map to route "/foo/bar"
public IActionResult Bar()
{
// ...
}
[Route("/hello/world")] // not combined; maps to route "/hello/world"
public IActionResult HelloWorld()
{
}
}
from https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing
[Route("[controller]/[action]")]
public class HomeController : Controller
{
[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
In the preceding code, the Index method templates must prepend / or ~/ to the route templates. Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.