You might have faced a scenario where you have a URL, and you need to find out if it maps to an action in your MVC Core app.
Turns out it's not that difficult, we will just have to leverage some framework classes.
Define the route and method to test
First we need to define the URL and HTTP method for the request:
string url = "/Home/Index";
string method = "GET";
Find the IRouteCollection router
The router is key to matching a URL to a controller action.
In this case, we need the IRouteCollection
.
In a controller, we can find it like so:
IRouteCollection router = RouteData.Routers.OfType<IRouteCollection>().First();
Now, we'll need to create an HttpContext
so we
can give the router something to test.
We can do it by injecting IHttpContextFactory
into
our controller:
private readonly IHttpContextFactory _httpContextFactory;
public HomeController(
IHttpContextFactory httpContextFactory)
{
_httpContextFactory = httpContextFactory;
}
And then creating the context with appropriate values:
//Also pass the feature collection of the existing context
HttpContext context = _httpContextFactory.Create(HttpContext.Features);
context.Request.Path = "/Home/Index";
context.Request.Method = "GET";
Now we can check if the route exists:
//Create RouteContext from HttpContext
var routeContext = new RouteContext(context);
//Run it through the router
await router.RouteAsync(routeContext);
//If router assigned a handler, URL matches an action
bool exists = routeContext.Handler != null;
If the router assigned a handler for the call, then a match was found! This means there is an action that would match if a request with that URL and HTTP method was received.
Here is the example as a whole:
public class HomeController : Controller
{
private readonly IHttpContextFactory _httpContextFactory;
public HomeController(
IHttpContextFactory httpContextFactory)
{
_httpContextFactory = httpContextFactory;
}
public async Task<IActionResult> Index()
{
IRouteCollection router = RouteData.Routers.OfType<IRouteCollection>().First();
HttpContext context = _httpContextFactory.Create(HttpContext.Features);
context.Request.Path = "/Home/TestPostAction";
context.Request.Method = "POST";
var routeContext = new RouteContext(context);
await router.RouteAsync(routeContext);
bool exists = routeContext.Handler != null;
return View();
}
[HttpPost]
public IActionResult TestPostAction()
{
return Ok();
}
}
This works for attribute routing as well.
Default values are also taken into consideration,
so "/"
, "/Home"
, and "/Home/Index"
all work, given
the default route template.
It will not match an action if:
- The HTTP method is wrong
- An inline route constraint is not satisfied (e.g.
[HttpGet("{id:int}")]
)