ASP. NET Core MVC Configure Global Routing Prefix

  • 2021-08-21 20:09:55
  • OfStack

ASP. NET Core MVC Configure Global Routing Prefix

Preface

Hello, everyone. Today, I will introduce a new feature of ASP. NET Core MVC, which adds a unified 1 prefix to global routing. Strictly speaking, it is not a new feature, but it is unique to Core MVC.

Application background

I don't know if you have encountered this scenario when you are doing Web Api application, that is, all interfaces begin with/api, that is, our api interface request address is like this:

http://www.example.com/api/order/333

Or is it such a demand

http://www.example.com/api/v2/order/333

In the past, if we wanted to fulfill this requirement, we could add a feature [Route ("/api/order")] to Controller to route Attribute, and then the MVC framework would scan your routing table to match requests such as/api/order.

However, for the second requirement with version number, the original definition of Route of Controller is [Route ("/api/v1/order")], and now it is necessary to upgrade to v2, and there are hundreds of interfaces, which 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 1. Let's take a look at 1.

IApplicationModelConvention interface

First of all, we need to use the interface IApplicationModelConvention, which is located in the namespace Microsoft. AspNetCore. Mvc. ApplicationModels. Let's look at the definition of the interface under 1.


public interface IApplicationModelConvention
{
 void Apply(ApplicationModel application);
}
 

As we know, the MVC framework has some conventional things, so this interface is mainly used to customize some MVC conventions. We can add or modify some conventions by specifying ApplicationModel objects. It can be seen 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 will inject this interface into Services at startup, so we only need to implement this interface and then configure it a little.

Then let's 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 attributes.

ApiExplorerModel: Mainly configure the default MVC Api Explorer, including Api description information, group information, visibility and so on. ControllerModel: It is mainly related to the default agreement of Comtroller. There are more things in this, so we will not introduce it in 11. We will configure one thing in it later. IFilterMetadata: Empty interface, which mainly serves as a tag.

One more thing to tell you is that you can see the Controllers attribute above, which is an IList < ControllerModel > That is to say, this list records all the information of Controller in your program. You can set it for a certain part or an Controller by traversing, including the information of Actions in Controller. We can use this feature to modify the MVC framework very flexibly. Is it cool?

Next, we will use this feature to realize our topic today. Thank you for your compliment ~:)

Add Global Routing System 1 Prefix

There is not so much nonsense, just go to the code, and everything to say is in the code:


// Define a class RouteConvention , to achieve  IApplicationModelConvention  Interface 
public class RouteConvention : IApplicationModelConvention
{
 private readonly AttributeRouteModel _centralPrefix;

 public RouteConvention(IRouteTemplateProvider routeTemplateProvider)
 {
  _centralPrefix = new AttributeRouteModel(routeTemplateProvider);
 }

 // Interface's Apply Method 
 public void Apply(ApplicationModel application)
 {
  // Traverse all  Controller
  foreach (var controller in application.Controllers)
  {
   //  It has been marked  RouteAttribute  Adj.  Controller
   var matchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel != null).ToList();
   if (matchedSelectors.Any())
   {
    foreach (var selectorModel in matchedSelectors)
    {
     //  In   On the current route   Again   Add 1 A   Routing prefix 
     selectorModel.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_centralPrefix,
      selectorModel.AttributeRouteModel);
    }
   }

   //  Not marked  RouteAttribute  Adj.  Controller
   var unmatchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel == null).ToList();
   if (unmatchedSelectors.Any())
   {
    foreach (var selectorModel in unmatchedSelectors)
    {
     //  Add 1 A   Routing prefix 
     selectorModel.AttributeRouteModel = _centralPrefix;
    }
   }
  }
 }
}

Then, we can start using the class we defined ourselves.


public static class MvcOptionsExtensions
{
 public static void UseCentralRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute)
 {
  //  Add our custom   Realization IApplicationModelConvention Adj. 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 =>
  {
   //  Routing parameters are still valid here, such as adding 1 Version number 
   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, where routing parameters are still available, so for example, you can specify a version number for your interface. After that, all your Controller RoteAttribute will be added with this prefix, which perfectly solves the requirement of the original version number. They look something like this:


[Route("order")]
public class OrderController : Controller
{
 //  Routing address  : /api/v{version}/order/details/{id}
 [Route("details/{id}")]
 public string GetById(int id, int version)
 {
  // The version number can be received above, and it returns  version  And  id
  return $"other resource: {id}, version: {version}";
 }
}

public class ItemController : Controller
{
 //  Routing address:  /api/v{version}/item/{id}
 [Route("item/{id}")]
 public string GetById(int id, int version)
 {
  // The version number can be received above, and it returns  version  And  id
  return $"item: {id}, version: {version}";
 }
}

Summarize

The bold type on it, I hope everyone can understand and apply it, This example is only a small scenario in the actual requirements. There will be various normal or abnormal requirements in specific projects. We should think more when doing a function. In fact, there are still many things to learn about MVC framework, including its design ideas, extensibility and other things, which need to be understood slowly. If you are interested in ASP. NET Core, you can pay attention to me. I will regularly share my learning achievements in my blog.

Through this article, I hope to help you. Thank you for your support to this site!


Related articles: