Configure the global routing prefix in ASP. NET Core MVC and coremvc
Configure the global routing prefix in ASP. NET Core MVC
Preface
Hello everyone, today we will introduce a new feature of ASP. NET Core MVC and add a unified prefix to global routes. Strictly speaking, it is not a new feature, but a unique feature of Core MVC.
Application background
I don't know if you have encountered such a scenario when using Web Api applications, that is, all interfaces start with/api, that is, our api 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 implement this kind of requirement, we could add a feature routing Attribute like [Route ("/api/order")] to the Controller, then, the MVC Framework scans your route table to match requests such as/api/order.
However, the second requirement with a version number is that the original Controller's Route is defined as [Route ("/api/v1/order")]. Now there are hundreds of interfaces to upgrade to v2, this requires one modification, and may be forced.
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 at it.
IApplicationModelConvention Interface
First, we need to use the IApplicationModelConvention interface, which is located in the Microsoft. AspNetCore. Mvc. ApplicationModels namespace. Let's take a look at the interface definition.
public interface IApplicationModelConvention{ void Apply(ApplicationModel application);}
We know that the MVC framework has some common things, so this interface is mainly used to customize some things of MVC conventions, we can add or modify some conventions by specifying the ApplicationModel object. We can see that the interface provides an Apply method, which has an ApplicationModel object. We can use this object to modify what we need, the MVC framework itself injects this interface into Services at startup, so we only need to implement this interface and then configure it slightly.
Let's 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 attributes such as ApiExplorer, Controllers, Filters, and Properties.
- Apicyclermodel: Mainly used to configure the default MVC Api Explorer, including the Api description, group information, and visibility.
- ControllerModel: it is mainly related to the default Comtroller conventions. There are more things in it, so we will not introduce them one by one. We will wait for a while to configure one thing in it.
- IFilterMetadata: Empty interface, mainly used for marking.
Another thing to tell you is that the Controllers attribute above is an IList <ControllerModel>, that is, the list records information of all Controllers in your program, you can set a part or a Controller through traversal, including the Actions information in the Controller, we can use this feature to flexibly modify the MVC Framework. Isn't it cool.
Next, we will use this feature to implement our theme today. Thank you for your thumbs up ~ :)
Add unified global route prefix
There is not so much nonsense. You can directly add the code. All you have to say is in the Code:
// Define a class RouteConvention to implement the invoke interface public class RouteConvention: Rule {private readonly AttributeRouteModel _ centralPrefix; public RouteConvention (condition) {_ centralPrefix = new AttributeRouteModel (condition );} // Apply method of the interface public void Apply (ApplicationModel application) {// traverse all controllers foreach (var Controller in application. Controllers) {// Controller var matchedSelectors = controller. Selectors. Where (x => x. AttributeRouteModel! = Null ). toList (); if (matchedSelectors. any () {foreach (var selectorModel in matchedSelectors) {// Add a route prefix selectorModel to the current route. attributeRouteModel = AttributeRouteModel. combineAttributeRouteModel (_ centralPrefix, selectorModel. attributeRouteModel); }}// Controller var unmatchedSelectors = controller without RouteAttribute. selectors. where (x => x. attributeRouteModel = null ). toList (); if (unmatchedSelectors. any () {foreach (var selectorModel in unmatchedSelectors) {// Add a route prefix selectorModel. attributeRouteModel = _ centralPrefix ;}}}}}
Then, we can start to use the class we have defined.
Public static class MvcOptionsExtensions {public static void UseCentralRoutePrefix (this MvcOptions opts, IRouteTemplateProvider routeAttribute) {// Add RouteConvention opts for implementing IApplicationModelConvention. conventions. insert (0, new RouteConvention (routeAttribute ));}}
Finally, add the above Extension Method in the Startup. cs file.
Public class Startup {public Startup (IHostingEnvironment env ){//...} public void ConfigureServices (IServiceCollection services ){//... services. addMvc (opt => {// The route parameter is still valid here, for example, add a version number opt. useCentralRoutePrefix (new RouteAttribute ("api/v {version}");} public void Configure (IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory ){//... app. useMvc ();}}
Here, opt. UseCentralRoutePrefix is the extension method defined above, and the routing parameters are still usable. For example, you can specify a version number for your interface. In this way, the RoteAttribute of all your controllers will be added with this prefix, which perfectly solves the needs of the first version number. They look like this:
[Route ("order")] public class OrderController: Controller {// Route address: /api/v {version}/order/details/{id} [Route ("details/{id}")] public string GetById (int id, int version) {// the version number can be received, and the returned version and id return $ "other resource: {id}, version: {version}" ;}} public class ItemController: controller {// Route address:/api/v {version}/item/{id} [Route ("item/{id}")] public string GetById (int id, int version) {// the version number can be received above. The returned version and id return $ "item: {id}, version: {version }";}}
Summary
I hope you can understand and use the above in bold Chinese. This example is only a small scenario in actual needs. There will be various normal or abnormal requirements in specific projects, we need to think more about a function. In fact, the MVC framework still has many things to learn, including its design ideas and scalability. If you are interested in ASP. NET Core, please pay attention to me. I will share some of my learning achievements on a regular basis in my blog.
I hope this article will help you. Thank you for your support for this site!