ASP.NET Core MVC configures global routing prefix
Preface
Hello everyone, today I will introduce to you a new feature of ASP.NET Core MVC, adding global routing Unified prefix. Strictly speaking, it is not a new feature, but it is unique to Core MVC.
Application background
I don’t know if you have ever encountered this scenario when you are making Web Api applications, that is, all interfaces start with /api, that is, we The api interface request address is like this:
http://www.example.com/api/order/333
or such a requirement
http://www.example.com/api/v2/order/333
In the past, if we wanted to achieve this requirement, we could add a [Route("/api /order")], then the MVC framework will scan your routing table to match requests like /api/order.
But for the second requirement with a version number, the original Controller’s Route definition was [Route("/api/v1/order")]. Now it needs to be upgraded to v2, and there are hundreds more. The interface needs to be modified one by one, which may cause confusion.
Now, there is a simpler and more elegant way to do this. You can add a global prefix routing tag in a unified manner. Let’s take a look below.
IApplicationModelConvention interface
First, we need to use the IApplicationModelConvention interface, located under the Microsoft.AspNetCore.Mvc.ApplicationModels namespace. Let’s take a look at the definition of the interface.
public interface IApplicationModelConvention { void Apply(ApplicationModel application); }
We know that the MVC framework has some conventions, so this interface is mainly used to customize some of the MVC conventions. We can add or Modify some conventions. You can see that the interface provides an Apply method. This method has an ApplicationModel object. We can use this object to modify what we need. The MVC framework itself will inject this interface into Services when it starts, so we only need to implement This interface can be configured slightly.
Then let us take a look at what the ApplicationModel object has:
public class ApplicationModel : IPropertyModel, IFilterModel, IApiExplorerModel { public ApiExplorerModel ApiExplorer { get; set; } public IList<ControllerModel> Controllers { get; } public IList<IFilterMetadata> Filters { get; } public IDictionary<object, object> Properties { get; } }
You can see that there are ApiExplorer, Controllers, Filters, Properties and other properties.
ApiExplorerModel: Mainly configures some things of the default MVC Api Explorer, including Api description information, group information, visibility, etc.
ControllerModel: Mainly related to the default convention of Comtroller. There are many things in this, so I won’t introduce them one by one. We will configure one of the things in it later.
IFilterMetadata: Empty interface, mainly used as a marker.
One more thing I need to tell you is that you can see that the above Controllers property is an IList
Next, we will use this feature to implement our theme today. Thank you for your thumbs up~ :)
Add global route unified prefix
No more nonsense, go directly to the code, everything you want to say is in the code:
//定义个类RouteConvention,来实现 IApplicationModelConvention 接口 public class RouteConvention : IApplicationModelConvention { private readonly AttributeRouteModel _centralPrefix; public RouteConvention(IRouteTemplateProvider routeTemplateProvider) { _centralPrefix = new AttributeRouteModel(routeTemplateProvider); } //接口的Apply方法 public void Apply(ApplicationModel application) { //遍历所有的 Controller foreach (var controller in application.Controllers) { // 已经标记了 RouteAttribute 的 Controller var matchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel != null).ToList(); if (matchedSelectors.Any()) { foreach (var selectorModel in matchedSelectors) { // 在 当前路由上 再 添加一个 路由前缀 selectorModel.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_centralPrefix, selectorModel.AttributeRouteModel); } } // 没有标记 RouteAttribute 的 Controller var unmatchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel == null).ToList(); if (unmatchedSelectors.Any()) { foreach (var selectorModel in unmatchedSelectors) { // 添加一个 路由前缀 selectorModel.AttributeRouteModel = _centralPrefix; } } } } }
Then, we can start using the class we defined.
public static class MvcOptionsExtensions { public static void UseCentralRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute) { // 添加我们自定义 实现IApplicationModelConvention的RouteConvention opts.Conventions.Insert(0, new RouteConvention(routeAttribute)); } }
Finally, in the Startup.cs file, just add the above extension method.
public class Startup { public Startup(IHostingEnvironment env) { //... } public void ConfigureServices(IServiceCollection services) { //... services.AddMvc(opt => { // 路由参数在此处仍然是有效的,比如添加一个版本号 opt.UseCentralRoutePrefix(new RouteAttribute("api/v{version}")); }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //... app.UseMvc(); } }
Among them, opt.UseCentralRoutePrefix is the extension method defined above. The routing parameters here can still be used, so for example, you can give your interface Specify something like a version number. After this, the RoteAttribute of all your Controllers will be added with this prefix, which perfectly solves the original version number requirement. They look like this:
[Route("order")] public class OrderController : Controller { // 路由地址 : /api/v{version}/order/details/{id} [Route("details/{id}")] public string GetById(int id, int version) { //上面是可以接收到版本号的,返回 version 和 id return $"other resource: {id}, version: {version}"; } } public class ItemController : Controller { // 路由地址: /api/v{version}/item/{id} [Route("item/{id}")] public string GetById(int id, int version) { //上面是可以接收到版本号的,返回 version 和 id return $"item: {id}, version: {version}"; } }
Summary
The bold words above, I hope everyone can understand and use them, this example is just a practical example In a very small scenario of demand, there will be various normal or abnormal demands in specific projects. We need to think more when making a function. In fact, there are many things to learn about the MVC framework, including Its design ideas, scalability and other things need to be understood slowly. If you are interested in ASP.NET Core, you can follow me. I will regularly share some of my learning results in my blog.
I hope this article can help everyone, thank you for your support of this site!
For more articles related to ASP.NET Core MVC configuring global routing prefix, please pay attention to the PHP Chinese website!