1

I have an ASP.NET Core web API controller with (among others) two methods that have the same signature.

Shortened down, this looks as follows:

[Route("my/route")]
public class MyApiController : ApiController
{
    [HttpGet("{*id}", Order = 2)]
    [Route("{*id}", Order = 2)]
    public MyObject Load([FromUri] String id) => new MyObject();
    
    [HttpDelete("{*id}", Order = 1)]
    [Route("{*id}", Order = 1)]
    public void Delete([FromUri] String id)
    {
    }
}

Now, I am issuing a call:

GET my/route/123/456

Shockingly, this call ends up in the Delete method. I literally have a breapoint in the first line of my (in real life, non-empty) Delete method, and the Immediate window in VS tells me HttpContext.Request.Method is "GET", yet I end up in the method explicitly marked as HttpDelete.

What is going on here? Luckily, my call happened from within an automated test to test the web API, but if someone had issued that call to retrieve actual data from the DB, they would have ended up deleting that data instead. Is there any misunderstanding on my side with respect to the [HttpDelete] attribute?

F-H
  • 663
  • 1
  • 10
  • 21
  • Hi @F-H, ASP.NET Core Web Api does not contain `[FromUri]` attribute, please modify the tag. – Rena Feb 14 '22 at 02:54
  • @Rena: The tag is correct, and the `[FromUri]` attribute is indeed there in our code - it is, however, possible, that the attribute doesn't do anything and it is just a remainder of the original .NET Framework code. – F-H Feb 14 '22 at 05:47
  • Hi @F-H, please check your target framework if it is `.NET Core` or not. If it is real .NET Core, please change `FromUri` to `[FromRoute]`. – Rena Feb 14 '22 at 05:53
  • @Rena: Thanks, I now have, though it makes no difference for the issue at hand. (I suspect I can remove the `[FromUri]`/`[FromRoute]` attribute there altogether.) In general, I find it really confusing to find out what needs to be changed for the switch from ASP.NET (Framework) to ASP.NET Core, given that some things (like `[FromUri]`) apparently have to be replaced, whereas it makes no difference for others (e.g. our `[HttpGet]` attributes etc., as well as the `[Route]` attribute, were still the ones from `System.Web.Http`, and I could observe absolutely no difference when using ... – F-H Feb 14 '22 at 08:12
  • ... the ones from `Microsoft.AspNetCore.Mvc` instead). – F-H Feb 14 '22 at 08:12

2 Answers2

3

You don't have to use route attribute and order parameter. It might be cause this situation.

[Route("my/route")]
public class MyApiController : ApiController
{
    [HttpGet("{*id}")]
    public MyObject Load([FromUri] String id) => new MyObject();
    
    [HttpDelete("{*id}")]
    public void Delete([FromUri] String id)
    {
    }
}
  • 1
    Well, the `Order` parameter *is* necessary, I think, to distinguish the endpoints from other endpoints on the same route with the same verb (which I have not shown in the minimal example as they are beside the point). But as I can also place the `Order` parameter on the `HttpGet` and `HttpDelete` attributes, I will try doing it like that and remove the `Route` attributes. – F-H Feb 13 '22 at 16:10
  • Thank you - this worked, and we will place the route directly on the `[Http*]` attribute as a rule of thumb from now on. – F-H Feb 15 '22 at 14:19
-1

If you have an [ApiController] attribute, you have to remove it since you will need full explicit route attribute. Or it is much better to use explicit routes

[Route("my/[action]")]
public class MyApiController : ApiController
{
    [HttpGet("my/load/{id}")]
    public MyObject Load(string id) => new MyObject();
    
     [HttpDelete("my/delete/{id}")]
     [HttpGet("my/delete/{id}")]
    public void Delete(string id)
    {
    }
}
Serge
  • 40,935
  • 4
  • 18
  • 45
  • Our specification requires us to provide the different functionality on the same route (per entity), distinguished only by the HTTP verb. Thus, this is a requirement that cannot be changed. (Also, there used to be no issue with it when the application was still based on .NET Framework with ASP.NET.) – F-H Feb 13 '22 at 15:58
  • @F-H All new net core route mapping doesn't support any Rest. Rest is in old technology that was popular 10 years ago. Now attribute routing is a number one. – Serge Feb 13 '22 at 16:07
  • Would you mind explaining that a bit further, please? We used to do rely entirely on the `[Http...]` and `[Route("...")]` attributes for routing so far, which - unless I'm mistaken - *is* "attribute routing". Also, to provide some context: I'm working on an application of which various versions are already published and running in production systems. For the new version, we have migrated from .NET Framework to .NET Core. However, the web APIs already offered by previous versions must not be broken for the new release, as they are in use in production systems. – F-H Feb 13 '22 at 16:20
  • @F-H I don't know how you can mange to use only 6 actions in one controller that Rest is allowed. The real production API controller can have hundreds actions – Serge Feb 13 '22 at 16:25
  • The application contains many hundreds of controllers with their individual base routes, most of which represent individual logical entities. Each of those entity controllers offers CRUD operations for the respective entity by means of the different HTTP verbs. – F-H Feb 13 '22 at 16:45
  • In your example code, I do not understand why you put both `[HttpDelete]` and `[HttpGet]` on the `Delete` method. The `Delete` method **must not** be called for `DELETE`, that it is is what I'm actually trying to prevent. – F-H Feb 14 '22 at 08:28
  • @F-H your route should like [controller]/[action]/{id} and it doesn't matter what method is called get, post or delete. The problems start when people try to invent something else. – Serge Feb 14 '22 at 12:05
  • Ah, so the design philosophy you're referring to entirely ignores the HTTP verb and, for instance, allows for the creation of items by, say, `DELETE` calls, as long as the route is named `create`. I see now. Unfortunately, that does not align with the design parameters I have to work within, which rely on the standard HTTP methods with their conventional meanings. Thank you for the explanation, anyway. – F-H Feb 14 '22 at 15:34
  • @F-H Yes, you are right. I prefer to use GET, but if my input parameters don't fit for a query string, I use POST. And that's it. I don't see any sense to use anything else. There are 9 methods, but they were invented hundreds years ago, when a byte of memory costed almost the same as the whole computer today. It made some sense that time to save some memory, but it doesn't make any sense nowdays. The longer an action name , the easier to understand what it is doing. – Serge Feb 14 '22 at 15:48