ASP.NET Core Web API Introduction - A Brief Discussion on non-RESTful Route Overriding
TLDR
- By inheriting from
BasicControllerand setting[Route("[controller]/[action]")], you can centrally manage API routes in a non-RESTful architecture. - To override the Controller-level route, simply use the
[Route]attribute directly on the Controller. - If an Action needs to completely ignore the Controller-level route configuration, prefix the Action's
[Route]with/or~. - Avoid using
[Route]on both the Controller and the Action simultaneously, as this leads to route concatenation, resulting in unexpected URLs. - If you only need to modify the URL name of an Action, it is recommended to use the
[ActionName]attribute instead of[Route].
Unified Route Management and Basic Configuration
In a non-RESTful style Web API, to avoid redundant route definitions, you can create an abstract BasicController to unify route rules.
When you might encounter this: When the project uses a Controller/Action format and you want to reduce the maintenance cost of repeatedly writing route attributes for every Controller.
[ApiController]
[Route("[controller]/[action]")]
public abstract class BasicController : ControllerBase {
}
public class TestRouteController : BasicController {
// URL: /TestRoute/TestAction
[HttpPost]
public void TestAction() {
}
}Route Overriding and Path Concatenation Issues
When the existing API architecture needs adjustment, or individual Actions require specific paths, it is essential to understand the behavioral differences of the [Route] attribute at different levels.
When you might encounter this: When you need to customize paths for a specific Controller or Action without affecting global settings, or when you accidentally trigger the default route concatenation behavior.
Route Behavior Analysis
- Controller-level override: Using
[Route]on a Controller will directly replace the settings fromBasicController. - Action-level concatenation: If both the Controller and the Action define
[Route], ASP.NET Core will concatenate the two paths. - Forced override (ignoring prefix): Adding
/or~to the prefix of an Action's[Route]forces it to ignore the Controller-level route configuration.
[Route("Override/[action]")]
public class TestRouteController : BasicController {
// Scenario: Controller overrides Route, Action does not handle it
// URL: /Override/OverrideController
[HttpPost]
public void OverrideController() {
}
// Common misuse: Both Controller and Action override, leading to path concatenation
// URL: /Override/OverrideAction/Action/OverrideAction
[HttpPost]
[Route("Action/[action]")]
public void OverrideAction() {
}
// Scenario: Action forces ignoring Controller settings using "/"
// URL: /Action/OnlyAction
[HttpPost]
[Route("/Action/[action]")]
public void OnlyAction() {
}
// Scenario: Action forces ignoring Controller settings using "~"
// URL: /Action/OnlyAction2
[HttpPost]
[Route("~/Action/[action]")]
public void OnlyAction2() {
}
// Scenario: Simply modifying the Action name without affecting the route structure
// URL: /Override/Rename
[HttpPost]
[ActionName("Rename")]
public void RenameAction() {
}
}Conclusion and Recommendations
- To unify API naming styles, prioritize using
BasicControllerfor global route definitions. - If you need to adjust the path for a specific Action, consider using the
[ActionName]attribute first, as this avoids the complexity introduced by route concatenation. - If you must use
[Route]to override an Action path, be sure to add the/or~prefix to ensure the path meets expectations and to avoid generating overly long, nested URLs.
Changelog
- 2024-04-16 Initial version created.
