Same url for overloaded controller methods - c#

In my web api application, I want to enable clients to make requests, using the same path, but pass different type of parameters.
For example:
public class MyController : ApiController
{
[HttpDelete]
public IHttpActionResult Delete(int id) {..}
[HttpDelete]
public IHttpActionResult Delete2(Guid id) {..}
[HttpDelete]
public IHttpActionResult Delete3(string id) {..}
}
I want the url for each method to be similar, for example:
api/MyController/1
api/MyController/abc etc..
Is this possible? Iv'e tried alot of combinations with ActionName attribute and Routing configuration, but nothing seemed to work.
Thanks

You can use attribute routing for this. For example:
[RoutePrefix("MyController")]
public class MyController : ApiController
{
[HttpDelete]
[Route("delete/{id:int}")]
public IHttpActionResult Delete(int id) {..}
[HttpDelete]
[Route("delete/{id:guid}")]
public IHttpActionResult Delete2(Guid id) {..}
[HttpDelete]
[Route("delete/{id:alpha}")]
public IHttpActionResult Delete3(string id) {..}
}
If you do this then the request url will be:
http://yoursever/mycontroller/delete/123
http://yoursever/mycontroller/delete/abc
http://yoursever/mycontroller/delete/91c74f8f-d981-4ee1-ba36-3e9416bba202

You need to provide a Route with different parameter types for each of your methods:
[RoutePrefix("api/MyController")]
public class MyController : ApiController
{
[HttpDelete]
[Route("{id:int}", Order = 1)]
public IHttpActionResult Delete(int id) {..}
[HttpDelete]
[Route("{id:guid}", Order = 2)]
public IHttpActionResult Delete2(Guid id) {..}
[HttpDelete]
[Route("{id}", Order = 3)]
public IHttpActionResult Delete3(string id) {..}
}
Of course you have to enable attribute routing if you haven't already.
The Order property for the Route attribute ensures that the route templates are checked in the correct order so that an int value will not match the string route.

Yes, this is possible. Try setting the route as a decoration.
example:
[Route("DeleteThis/{id}")]
[HttpDelete]
public IHttpActionResult DeleteThis(int id)
{
return Ok();
}
[Route("NowDeleteThis/{name}")]
[HttpDelete]
public IHttpActionResult DeleteThis(string name)
{
return Ok();
}

Related

REST url - user by id and name

I have two actions:
[HttpGet]
[Route("Users/{id}")]
public async Task<IActionResult>GetUser(int id)
[HttpGet]
[Route("Users")]
public async Task<IActionResult>GetUser(string name)
When I am trying to call /Users?name=aaa I am getting the following error:
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The
request matched multiple endpoints. Matches:
What am I doing wrong?
-----------
I have noticed I have one more action:
[HttpGet]
public async TaskGetOffers()
This was the reason, sorrry
If you want to retrieve data from query string, you need to use FromQueryAttribute
// e.g. Users/10
[HttpGet]
[Route("Users/{id:int}")]
public async Task<IActionResult>GetUser([FromRoute] int id)
// e.g. Users?name=John
[HttpGet]
[Route("Users")]
public async Task<IActionResult>GetUser([FromQuery] string name)
try with the below code
[HttpGet]
[Route("Users/{id}")]
public async Task<IActionResult>GetUser([FromQuery] int id)
[HttpGet]
[Route("Users")]
public async Task<IActionResult>GetUser([FromQuery]string name)
Can you change from
[Route("Users")]
public async Task<IActionResult>GetUser(string name)
To
[Route("Users/{name}")]
public async Task<IActionResult>GetUser(string name)
And also provide type in your route
[Route("Users/{id:int}")]

Handling multiple IDs in Route

As far as I can understand, the conventional routing in .NET Core MVC is [controller]/[action]/{id?}
However, I have the following POST request I'm trying to catch which looks like this:
myDomain/MyController/MyAction/userID/anotherID/myInfo
I have tried the following, but it doesn't seem to be working:
public class MyController : Controller
{
[HTTPPost]
[Route("MyAction/{userID:minlength(2)}/{anotherID:int}/myInfo")]
public IActionResult MyAction([FromRoute] string userID, [FromRoute] int anotherID, [FromBody] string stuffIWant)
{
return Ok();
}
}
Obviously I'm not handling the routing correctly, but I'm not sure how I would get userID and anotherID from that route. I've published this action to my site, and tried to do a test post with the same URL, but didn't get a response.
Change to:
public class MyController : Controller
{
[HTTPPost]
[Route("MyAction/{userID:minlength(2)}/{anotherID:int}/myInfo")]
public IActionResult MyAction(string userID, int anotherID, [FromBody] string stuffIWant)
{
return Ok();
}
}

How to make valid route for {id}/visit?

I am new at asp.core , so I try to make valid route to {id}/visits
My code:
[Produces("application/json")]
[Route("/Users")]
public class UserController
{
[HttpGet]
[Route("{id}/visits")]
public async Task<IActionResult> GetUser([FromRoute] long id)
{
throw new NotImplementedException()
}
}
But, at route {id} generated method the same:
// GET: /Users/5
[HttpGet("{id}")]
public async Task<IActionResult> GetUser([FromRoute] long id)
{
return Ok(user);
}
How to make route /Users/5/visits nethod?
What parameters at GetUser should I add?
Name the methods differently and use constraints to avoid route conflicts:
[Produces("application/json")]
[RoutePrefix("Users")] // different attribute here and not starting /slash
public class UserController
{
// Gets a specific user
[HttpGet]
[Route("{id:long}")] // Matches GET Users/5
public async Task<IActionResult> GetUser([FromRoute] long id)
{
// do what needs to be done
}
// Gets all visits from a specific user
[HttpGet]
[Route("{id:long}/visits")] // Matches GET Users/5/visits
public async Task<IActionResult> GetUserVisits([FromRoute] long id) // method name different
{
// do what needs to be done
}
}

Web Api how to define 2 methods with same parameters

I'm new to Web API. Reading up on restful made me think it was based upon verbs and as such I was hoping that the logic would be as well.
If I want to create an API for Delete and Get, which have the same signature I'm told off .
[HttpGet]
public HttpResponseMessage Index(int id)
{
return Request.CreateResponse(HttpStatusCode.OK, GetValue());
}
[HttpDelete]
public HttpResponseMessage Index(int id)
{
//logic
return Request.CreateResponse(HttpStatusCode.OK, true);
}
I was hoping by specifying the different verb Web Api 2 would tell. But even if I update the delete to (note the void return type)
[HttpDelete]
public void Index(int id)
{
//logic
}
I am still told off as the member called index with the same parameter types already exist.
According to https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client it shows
Action HTTP method Relative URI
Get a product by ID GET /api/products/id
Create a new product POST /api/products
Update a product PUT /api/products/id
Delete a product DELETE /api/products/id
The Get, Put and Delete have the same URL. Sadly, they don't display the server side code, only the client.
Are my only options to:
1. Overload the method (in this example, seems like it would be hacking as it's not needed to perform the required task)
2. Give the method a different name (eg `Delete` instead of `Index`)
Or is there another way?
You have a syntax issue. You can use attribute routes to maintain same paths but the methods must have different names and structures or you will get compilation error like you already experienced.
Using the example from your question
Action HTTP method Relative URI
Get a product by ID GET /api/products/id
Create a new product POST /api/products
Update a product PUT /api/products/id
Delete a product DELETE /api/products/id
The following would be a controller that matches the above
[RoutePrefix("api/products")]
public class ProductsController : ApiController {
[HttpGet]
[Route("{id:int}")] //Matches GET api/products/1
public IHttpActionResult Get(int id) {
return Ok(GetValueById(id));
}
[HttpPost]
[Route("")] //Matches POST api/products
public IHttpActionResult Post([FromBody]Product model) {
//...code removed for brevity
}
[HttpPut]
[Route("{id:int}")] //Matches PUT api/products/1
public IHttpActionResult Put(int id, [FromBody]Product model) {
//...code removed for brevity
}
[HttpDelete]
[Route("{id:int}")] //Matches DELETE api/products/1
public IHttpActionResult Post(int id) {
//...code removed for brevity
}
}
You can use Route attribute on api methods, check below:
[HttpGet]
[Route("same")]
public IHttpActionResult get(int id)
{
return Ok();
}
[HttpDelete]
[Route("same")]
public IHttpActionResult delete(int id)
{
return Ok();
}
And set http method to get for get request and delete for delete request, similar for post/put.

POST / PUT route constrainst issue on Web API 2

I came across this issue by accident. I have this route
config.Routes.MapHttpRoute(
name: "RecycleCenters",
routeTemplate: "api/cars/{id}",
defaults: new { controller = "rc", id = RouteParameter.Optional }
);
and I have a controller like this
public class CarsController : ApiController
{
public IEnumerable<Car> Get() { ... }
public HttpResponseMessage Get(int id) { ... }
public HttpResponseMessage Post(Car car) { ... }
public HttpResponseMessage Put(int id, Car car) { ... }
public HttpResponseMessage Delete(int id) { ... }
}
Basically what's happening is that my routing is allowing to make a POST request to an endpoint like this /api/cars/id, when it shouldn't because to create a new car the request should be to this endpoint /api/cars
I've found an answer for my issue in this link but I want to know if the newer version of Web API has something already built in to prevent this scenario, if so then how to use it?
Thanks
If you use attribute routing you avoid this problem completely. Instead of defining your routes with config.Routes.MapHttpRoute you use config.MapHttpAttributeRoutes() and then place attributes on your controller methods:
public class CarsController : ApiController
{
[HttpGet]
[Route("api/cars")]
public IEnumerable<Car> Get() { ... }
[HttpGet]
[Route("api/cars/{id}")]
public HttpResponseMessage Get(int id) { ... }
[HttpPost]
[Route("api/cars")]
public HttpResponseMessage Post(Car car) { ... }
[HttpPut]
[Route("api/cars/{id}")]
public HttpResponseMessage Put(int id, Car car) { ... }
[HttpDelete]
[Route("api/cars/{id}")]
public HttpResponseMessage Delete(int id) { ... }
}
You can also add a RoutePrefix attribute at the controller level to avoid duplicating some of the information in the route on each controller. You can also avoid placing the Http verb attributes if you name your methods as you did, with the method name as the verb, but I prefer to use the attribute for each method for consistency.
This is a good article that talks about attribute routing versus convention routing. It talks specifically about MVC, but it applies to web api as well.
Hope that helps.

Categories