org.sonar.plugins.csharp.S6931.html Maven / Gradle / Ivy
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 : Controller
{
[HttpGet("/Index1")] // This action is mapped to the root of the web application
public ActionResult Index1() => View();
[Route("/Index2")] // The same applies here
public ActionResult Index2() => View();
}
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 : Controller // Noncompliant
{
// Route is /reviews
[HttpGet("/reviews")]
public ActionResult Index() { /* ... */ }
// Route is /reviews/{reviewId}
[Route("/reviews/{reviewId}")]
public ActionResult Show(int reviewId)() { /* ... */ }
}
Compliant solution
[Route("/")] // Turns on attribute routing
public class ReviewsController : Controller
{
// Route is /reviews
[HttpGet("reviews")]
public ActionResult Index() { /* ... */ }
// Route is /reviews/{reviewId}
[Route("reviews/{reviewId}")]
public ActionResult Show(int reviewId)() { /* ... */ }
}
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