0

In our classic ASP.NET WebAPI project, we could declare a route and the framework would select the correct action based on the HTTP verb in the request.

However in .NET Core WebAPI, I tried the following route configuration

app.UseEndpoints(endpoints =>
{
   endpoints.MapControllers();
   endpoints.MapControllerRoute(
      name: "DefaultRoute",
      pattern: "{controller}/{id?}"
   );
});

My controller has one method

public class WeatherForecastController : ControllerBase
{
   [HttpGet]
   public WeatherForecast Get()
   {
      //return weather forecast
   }
}

When trying the following URL, I get 404 whereas in a similar classic ASP.NET WebAPI project it would automatically execute the Get method.

https://localhost/weatherforecast

Does that mean for conventional routing we need to add multiple routes with same pattern, with default action and HTTP method constraints for it to work properly?

This question is only about conventional routing, suggesting to switch to attribute routing is not an answer.

stormtrooper
  • 340
  • 2
  • 18

3 Answers3

1

I found a question that tries to simulate this behavior in classic ASP.NET WebAPI in ASP.NET Core: Route action based on HTTP verb?

The example is in .NET Core 2 and MVC, but trying it in .NET Core 3 WebAPI works the same.

Seems the answer is No, in ASP.NET Core WebAPI, if the route doesn't have action in the route pattern and no HTTP method constraints, the framework won't automatically try to match with actions based on HTTP verb in the requests. In order to achieve this, multiple routes with default actions and Verb constraints need to be added.

stormtrooper
  • 340
  • 2
  • 18
0

Routing is responsible for mapping request URL to an endpoint and it comes with two types Conventional and Attributes routing.

And from your question, you are expecting conventional routing with default rout which you can achieve it .NET CORE using below line of code.

app.UseMvc(routes =>
{
    routes.MapRoute("default", "{controller=Search}/{action}/{id?}");
});

Note: But keep in mind that convetional routing will not work if you decorate your controller with [ApiController] attribute.

By default .NET CORE supports attribute routing so you have to prefix the route by placing [Route] attribute on a controller level. Please see below example

    [Route("api/[controller]")]
    [ApiController]
    public class SearchController : ControllerBase
    {

        [HttpGet("{company}")]
        public IActionResult Get(string company) 
        {
            return Ok($"company: {company}");
        }

        [HttpGet("{country}/{program}")]
        public IActionResult Get(string country, string program) 
        {
            return Ok($"country: {country} program: {program}");
        }
    }

The above code will work as you expected (Attribute routing).

If you are decorating your controller by [ApiController] attribute then you have to use Attribute routing and any conventional routing defined in startup class will be overridden. Please see more details here.

Mahesh More
  • 821
  • 1
  • 8
  • 23
0

Does that mean for conventional routing we need to add multiple routes with same pattern, with default action and HTTP method constraints for it to work properly?

Yes, in asp.net core web api, if you want to use conventional routing, you need to remove [ApiController] attribute and [Route] attribute firstly and use the following route with default action

app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=WeatherForecast}/{action=Get}/{id?}");
    });

Refer to Conventional Routing in ASP.NET Core API

Update:Using Url Rewriting

You could always write your own url rewrite rules to meet your requirements.Refer to below demo which deal with url like /weatherforecast:

Create Rewrite Rules:

public class RewriteRuleTest : IRule
    {
        public void ApplyRule(RewriteContext context)
        {
            var request = context.HttpContext.Request;
            var path = request.Path.Value;// path= "/weatherforecast" for example

            if(path !=null)
            {
                context.HttpContext.Request.Path = path + "/" + request.Method; 
                // "/weatherforecast/post"
            }
        }
    }

Startup.cs

app.UseRewriter(new RewriteOptions().Add(new RewriteRuleTest()));
app.UseRouting();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapControllerRoute(
        name: "GetRoute",
        pattern: "{controller=WeatherForecast}/{action=Get}/{id?}"
    );
});
Ryan
  • 19,118
  • 10
  • 37
  • 53
  • So what happens, if the request is a POST or PUT, but the url is /weatherforecast ? In this case it will execute the Get method because that's the default action even though the Http verb is not GET? – stormtrooper Mar 10 '20 at 08:25
  • @stormtrooper No,above configuring is just an example.It also follows the rule `/controller/action` for all request, so it should be `/weatherforecast/post`. – Ryan Mar 10 '20 at 08:33
  • Ok, you mean for that above configuration, a POST request with url /weatherforecast will return 404? That means that in order for a POST request with URL /weatherforecast to work, an additional route with HttpMethod constraint needs to be configured, doesn't it? – stormtrooper Mar 10 '20 at 08:44
  • @stormtrooper Not really,to do a trick is adding `{controller=WeatherForecast}/{action=Post}/{id?}` what above code has done is the same to MVC.Why don't you want the action name in url? – Ryan Mar 10 '20 at 09:43
  • @stormtrooper I try with url rewrite and maybe that is a workaround. See my edited answer. – Ryan Mar 11 '20 at 07:52
  • What I'm trying is not to find a solution to make that route work, but rather to understand and confirm how routing works differently in classic ASP.NET WebAPI vs ASP.NET Core WebAPI. I found a StackOverflow question that tries to simulate that behavior in classic .NET WebAPI, seems adding multiple routes is the only way: https://stackoverflow.com/questions/47995696/route-action-based-on-http-verb – stormtrooper Mar 12 '20 at 03:27