org.sonar.plugins.csharp.S6934.html Maven / Gradle / Ivy
When a route template is defined through an attribute on an action method, conventional routing for that action is disabled. To maintain good
practice, it’s recommended not to combine conventional and attribute-based routing within a single controller to avoid unpredicted behavior. As such,
the controller should exclude itself from conventional routing by applying a [Route]
attribute.
Why is this an issue?
In ASP.NET Core MVC, the routing middleware utilizes a series of rules and conventions to
identify the appropriate controller and action method to handle a specific HTTP request. This process, known as conventional routing, is
generally established using the MapControllerRoute
method. This method is typically configured in one central location for all controllers during the application setup.
Conversely, attribute routing allows routes to be defined at the controller or action method level. It is possible to mix both
mechanisms. Although it’s permissible to employ diverse routing strategies across multiple controllers, combining both mechanisms within one
controller can result in confusion and increased complexity, as illustrated below.
// Conventional mapping definition
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
public class PersonController
{
// Conventional routing:
// Matches e.g. /Person/Index/123
public IActionResult Index(int? id) => View();
// Attribute routing:
// Matches e.g. /Age/Ascending (and model binds "Age" to sortBy and "Ascending" to direction)
// but does not match /Person/List/Age/Ascending
[HttpGet(template: "{sortBy}/{direction}")]
public IActionResult List(string sortBy, SortOrder direction) => View();
}
How to fix it in ASP.NET Core
When any of the controller actions are annotated with a HttpMethodAttribute
with a
route template defined, you should specify a route template on the controller with the RouteAttribute
as well.
Code examples
Noncompliant code example
public class PersonController : Controller
{
// Matches /Person/Index/123
public IActionResult Index(int? id) => View();
// Matches /Age/Ascending
[HttpGet(template: "{sortBy}/{direction}")] // Noncompliant: The "Index" and the "List" actions are
// reachable via different routing mechanisms and routes
public IActionResult List(string sortBy, SortOrder direction) => View();
}
Compliant solution
[Route("[controller]/{action=Index}")]
public class PersonController : Controller
{
// Matches /Person/Index/123
[Route("{id?}")]
public IActionResult Index(int? id) => View();
// Matches Person/List/Age/Ascending
[HttpGet("{sortBy}/{direction}")] // Compliant: The route is relative to the controller
public IActionResult List(string sortBy, SortOrder direction) => View();
}
There are also alternative options to prevent the mixing of conventional and attribute-based routing:
// Option 1. Replace the attribute-based routing with a conventional route
app.MapControllerRoute(
name: "Lists",
pattern: "{controller}/List/{sortBy}/{direction}",
defaults: new { action = "List" } ); // Matches Person/List/Age/Ascending
// Option 2. Use a binding, that does not depend on route templates
public class PersonController : Controller
{
// Matches Person/List?sortBy=Age&direction=Ascending
[HttpGet] // Compliant: Parameters are bound from the query string
public IActionResult List(string sortBy, SortOrder direction) => View();
}
// Option 3. Use an absolute route
public class PersonController : Controller
{
// Matches Person/List/Age/Ascending
[HttpGet("/[controller]/[action]/{sortBy}/{direction}")] // Illustrate the expected route by starting with "/"
public IActionResult List(string sortBy, SortOrder direction) => View();
}
Resources
Documentation
- Microsoft Learn - Overview of ASP.NET Core MVC
- Microsoft Learn - Routing to controller actions in ASP.NET
Core
- Microsoft Learn - Mixed routing:
Attribute routing vs conventional routing
- Microsoft Learn - HttpMethodAttribute Class
- Microsoft Learn - RouteAttribute Class
Articles & blog posts
- Medium - Routing in ASP.NET Core