A library enabling strongly typed routing in ASP.NET Core MVC projects.
Everything is on Nuget.
nuget install Strathweb.TypedRouting.AspNetCore
or via the .NET Core CLI:
dotnet add package Strathweb.TypedRouting.AspNetCore
In your Startup
class, after adding MVC, call AddTypedRouting();
and then configure your routes:
services.AddMvc().AddTypedRouting(opt =>
{
opt.Get("homepage", c => c.Action<HomeController>(x => x.Index()));
opt.Get("aboutpage/{name}", c => c.Action<HomeController>(x => x.About(Param<string>.Any)));
opt.Post("sendcontact", c => c.Action<HomeController>(x => x.Contact()));
});
This creates:
- a GET route to
/homepage
- a GET route to
/aboutpage/{name}
- a POST route to
/sendcontact
All of which will route to the relevant methods on our HomeController
.
Since the API is fluent, you can also give the routes names so that you can use them with i.e. link generation.
opt.Get("api/values/{id}", c => c.Action<ValuesController>(x => x.Get(Param<int>.Any))).WithName("GetValueById");
Now you can use it with IUrlHelper
(it's a Url
property on every controller):
var link = Url.Link("GetValueById", new { id = 1 });
IUrlHelper
can also be obtained from HttpContext
, anywhere in the pipeline (i.e. in a filter):
var services = context.HttpContext.RequestServices;
var urlHelper = services.GetRequiredService<IUrlHelperFactory>().GetUrlHelper(context);
var link = urlHelper.Link("GetValueById", new { id = 1 });
Finally, you can also use this link generation technique with the built-in action results, such as for example CreatedAtRouteResult
:
public IActionResult Post([FromBody]string value)
{
var result = CreatedAtRoute("GetValueById", new { id = 1 }, "foo");
return result;
}
The route definitions can also be done along with filters that should be executed for a given route. This is equivalent to defining a controller action, and annotating it with a relevant attribute such as action filter or authorization filter.
services.AddMvc().AddTypedRouting(opt =>
{
opt.Get("api/items", c => c.Action<ItemsController>(x => x.Get())).WithFilters(new AnnotationFilter());
});
Filters can also be resolved from ASP.NET Core DI system - as long as they are registered there before.
services.AddSingleton<TimerFilter>();
services.AddMvc().AddTypedRouting(opt =>
{
opt.Get("api/items", c => c.Action<ItemsController>(x => x.Get())).WithFilter<TimerFilter>();
});
The route definitions can also have ASP.NET Core authorization policies attached to them.
You can pass in a policy instance:
services.AddMvc().AddTypedRouting(opt =>
{
opt.Get("api/secure", c => c.Action<OtherController>(x => x.Foo()).
WithAuthorizationPolicy(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()));
});
You can also define a policy as string - then a corresponding policy must be previously registerd in ASP.NET Core DI system.
services.AddAuthorization(o =>
{
o.AddPolicy("MyPolicy", b => b.RequireAuthenticatedUser());
});
services.AddMvc().AddTypedRouting(opt =>
{
opt.Get("api/secure", c => c.Action<OtherController>(x => x.Foo()).
WithAuthorizationPolicy("MyPolicy"));
});
The library supports two ways of specifying MVC action constraints:
- inline in the template
- via fluent API
The inline constraints are the same as you can use with attribute routing. For example:
opt.Get("api/other/{id:int}", c => c.Action<OtherController>(x => x.Action2(Param<int>.Any)));
You can also specify constraints via the fluent API, by chaining IActionConstraintMetadata
implementations. Consider the following sample constraint class:
public class MandatoryHeaderConstraint : IActionConstraint, IActionConstraintMetadata
{
private string _header;
public MandatoryHeaderConstraint(string header)
{
_header = header;
}
public int Order
{
get
{
return 0;
}
}
public bool Accept(ActionConstraintContext context)
{
// only allow route to be hit if the predefined header is present
if (context.RouteContext.HttpContext.Request.Headers.ContainsKey(_header))
{
return true;
}
return false;
}
}
You can now use this class in the route declaration:
opt.Get("api/other", c => c.Action<OtherController>(x => x.Action1())).WithConstraints(new MandatoryHeaderConstraint("CustomHeader"));