Route templates for ASP.NET controller actions, defined via a `RouteAttribute` or any derivation of `HttpMethodAttribute`, should not start with "/".

## Why is this an issue?

Routing in ASP.NET Core MVC maps controllers and actions to paths in request URIs. Similar routing happens in ASP.NET Framework MVC.

In ASP.NET Core MVC, when an action defines a route template starting with a "/", the route is considered absolute and the action is registered at the root of the web application.

In such a scenario, any route defined at the controller level is disregarded, as shown in the following example:

    <Route("[controller]")> ' This route is ignored for the routing of Index1 and Index2
    Public Class HomeController
        Inherits Controller

        <HttpGet("/Index1")> ' This action is mapped to the root of the web application
        Public Function Index1() As ActionResult
            Return View()
        End Function

        <Route("/Index2")>   ' The same applies here
        Public Function Index2() As ActionResult
            Return View()
        End Function
    End Class

The behavior can be found confusing and surprising because any relative action route is relativized to the controller route.

Therefore, in the vast majority of scenarios, controllers group all related actions not only in the source code, but also at the routing level.

In ASP.NET Framework MVC with attribute routing enabled via `MapMvcAttributeRoutes`, the mere presence of an absolute route at the action level will produce an `InvalidOperationException` at runtime.

It is then a good practice to avoid absolute routing at the action level and move the "/" to the root level, changing the template defined in the `RouteAttribute` of the controller appropriately.

### Exceptions

The rule only applies when all route templates of all actions of the controller start with "/". Sometimes some actions may have both relative and absolute route templates, for example for backward compatibility reasons (i.e. a former route needs to be preserved). In such scenarios, it may make sense to keep the absolute route template at the action level.

## How to fix it

### Code examples

#### Noncompliant code example

    <Route("[controller]")> ' This route is ignored
    Public Class ReviewsController
        Inherits Controller

        ' Route is /reviews
        <HttpGet("/reviews")>
        Public Function Index() As ActionResult
            ' ...
        End Function

        ' Route is /reviews/{reviewId}
        <Route("/reviews/{reviewId}")>
        Public Function Show(reviewId As Integer) As ActionResult
            ' ...
        End Function
    End Class

#### Compliant solution

    <Route("/")> ' Turns on attribute routing
    Public Class ReviewsController
        Inherits Controller

        ' Route is /reviews
        <HttpGet("reviews")>
        Public Function Index() As ActionResult
            ' ...
        End Function

        ' Route is /reviews/{reviewId}
        <Route("reviews/{reviewId}")>
        Public Function Show(reviewId As Integer) As ActionResult
            ' ...
        End Function
    End Class

## Resources

### Documentation

- Microsoft Learn - ASP.NET Core: Routing to controller actions in ASP.NET Core
- Microsoft Learn - ASP.NET Core: Routing to controller actions in ASP.NET Core - Attribute routing for REST APIs
- Microsoft Learn - ASP.NET Core: Handle requests with controllers in ASP.NET Core MVC
- Microsoft Learn - ASP.NET MVC Routing Overview (C#)

### Articles & blog posts

- .NET Blog - Attribute Routing in ASP.NET MVC 5