Interpreting ASP.NET 5 & MVC6 Series (12): Strongly Typed Routing Implementation Based on Lamda Expressions

Interpreting ASP.NET 5 & MVC6 Series (12): Strongly Typed Routing Implementation Based on Lamda Expressions

In the previous chapter, Understanding Routing, we talked about how, in MVC, in addition to using the default ASP.NET 5 routing registration method, you can also use Attribute-based features (Route and HttpXXX series methods) to define. In this chapter, we will talk about a strongly typed type based on Lambda expressions.

The following is a basic example of how to use this approach:

  1. services.Configure(opt => { opt.EnableTypedRouting(); opt.GetRoute( "homepage" , c => c.Action(x => x.Index())); opt.GetRoute( "aboutpage/{name}" , c => c.Action(x => x.About(Param.Any))); opt.PostRoute( "sendcontact" , c => c.Action(x => x.Contact())); });

As can be seen from the example, we can define the route through extension methods such as GetRoute or PostRoute, and then use Lambda expressions to define the type of Controller and the method of Action.

Note that obtaining the method name of the Action here is achieved by delegating the execution of the Action method (it is not actually executed, but the MethodInfo of the Action is obtained based on this).

Implementation principle

When configuring services in the ConfigureServices method of Stratup.cs, we can configure the core configuration file MvcOptions used by the MVC site, where this class has an ApplicationModelConventions property (List ) can save a collection of IApplicationModelConvention interfaces, which can pipeline the program model of the MVC program. The definition of this interface is as follows:

  1. public   interface IApplicationModelConvention
  2. {
  3. void Apply(ApplicationModel application);
  4. }

The parameter type received by the Apply method in the interface is ApplicationModel, and ApplicationModel has two extremely important contents for us to operate, one is the Controller model collection, and the other is the collection of various Filters. The definition of this class is as follows:

  1. public   class ApplicationModel
  2. {
  3. public ApplicationModel();
  4.  
  5. public IList Controllers { get; } public IList Filters { get; } }

The most important thing here is the ControllerModel class. The instance of this class stores a variety of important and operational information, such as the route definition data on this class and related Actions, API description information, route constraints, etc. All this information can be operated.

The new IApplicationModelConvention registration method is as follows:

  1. services.Configure(opt => { opts.ApplicationModelConventions.Add( new MyApplicationModelConvention()); });

Therefore, we can use this method to make responsive adjustments and modifications to the entire MVC program model at the appropriate time. The strongly typed routing in this chapter is implemented using this feature.

Implementation steps

First, define a strongly typed routing model TypedRouteModel class, which inherits from AttributeRouteModel. The AttributeRouteModel class is a basic model based on Attribute routing. The code of the TypedRouteModel class is as follows:

  1. public   class TypedRouteModel : AttributeRouteModel
  2. {
  3. public TypedRouteModel(string template)
  4. {
  5. Template = template;
  6. HttpMethods = new string[ 0 ];
  7. }
  8.  
  9. public TypeInfo ControllerType { get; private set; }
  10.  
  11. public MethodInfo ActionMember { get; private set; }
  12.  
  13. public IEnumerable HttpMethods { get; private set; } public TypedRouteModel Controller() { ControllerType = typeof(TController).GetTypeInfo(); return   this ; } public TypedRouteModel Action

The main functions of this class are: defining supported incoming Controller types and supporting chain calls.

Then define a TypedRoutingApplicationModelConvention class that inherits the IApplicationModelConvention interface. The code is as follows:

  1. public   class TypedRoutingApplicationModelConvention : IApplicationModelConvention
  2. {
  3. internal static readonly Dictionary

In this class, a static variable Routes is saved to save all routes declared in Lamda expression mode, and then search and modify them in the existing Controllers collection, replace the AttributeRouteModel property, and set the corresponding Http Method (if not set, all methods are allowed by default).

Here, we simply replace action.AttributeRouteModel, which may lead to some defects (for example, an Action can only support one routing path, the last one shall prevail). You can optimize it according to your own ability.

When optimizing, please note that the Route collection on the Controller is saved in the controller.Attributes property, and the Route collection on the Action is saved in the action.Attributes property, which can be optimized. Then, on MvcOptions, we add some extension methods to TypeRouteModel for easy use. The code is as follows:

  1. public   static   class MvcOptionsExtensions
  2. {
  3. public   static TypedRouteModel GetRoute( this MvcOptions opts, string template, Action configSetup) { return AddRoute(template, configSetup).ForHttpMethods( "GET" ); } public   static TypedRouteModel PostRoute( this MvcOptions opts, string template, Action configSetup) { return AddRoute(template, configSetup).ForHttpMethods( "POST" ); } public   static TypedRouteModel PutRoute( this MvcOptions opts, string template, Action configSetup) { return AddRoute(template, configSetup).ForHttpMethods( "PUT" ); } public   static TypedRouteModel DeleteRoute( this MvcOptions opts, string template, Action configSetup) { return AddRoute(template, configSetup).ForHttpMethods( "DELETE" ); } public   static TypedRouteModel TypedRoute( this MvcOptions opts, string template, Action configSetup) { return AddRoute(template, configSetup); } private   static TypedRouteModel AddRoute(string template, Action configSetup) { var route = new TypedRouteModel(template); configSetup(route); if (TypedRoutingApplicationModelConvention.Routes.ContainsKey(route.ControllerType)) { var controllerActions = TypedRoutingApplicationModelConvention.Routes[route.ControllerType]; controllerActions.Add(route); } else { var controllerActions = new List { route }; TypedRoutingApplicationModelConvention.Routes.Add(route.ControllerType, controllerActions); } return route; } public   static   void EnableTypedRouting( this MvcOptions opts) { opts.ApplicationModelConventions.Add( new TypedRoutingApplicationModelConvention()); } }

In the above code, we added an EnableTypedRouting extension method to add a new TypedRoutingApplicationModelConvention type instance to the MvcOptions.ApplicationModelConventions property.

Other extension methods are used to declare related routes. Please note that in the first example, we see that the method to obtain action information is to call the action method through delegation (but not actually call it), but some methods have parameters, what should we do? For this reason, we define a Param class that ignores parameters. The code is as follows:

  1. public   static   class Param { public   static TValue Any { get { return   default (TValue); } } }

In this way, when we define the route for the About method containing parameters, we can define it like this. The code is as follows:

opt.GetRoute("aboutpage/{name}", c => c.Action (x => x.About(Param .Any)));

In addition, since many methods in TypeRouteModel can be chained, we can also specify a name for the route in this way. The sample code is as follows:

opt.GetRoute("homepage", c => c.Action(x => x.Index())).WithName("foo");

At this point, the entire strongly typed routing function has been implemented, and everyone has one more choice when using it.

Disadvantages (or Bugs)

We can see that when implementing the IApplicationModelConvention interface above, we simply replace action.AttributeRouteModel. That is, if you have already set the Route attribute on the Action, it will overwrite your information, causing your route to fail. For example, if you define a custom route like this:

  1. public   class ProductsController : Controller
  2. {
  3. [Route( "index" )]
  4. public IActionResult Index()
  5. {
  6. return Content( "Index" );
  7. }
  8. }

Then, a strongly typed route is defined through Lamda expression. The code is as follows:

  1. opt.GetRoute( "homepage" , c => c.Action(x => x.Index()));

Then, you can only access it through /homepage, but not through /index, because it overwrites your Route.

However, the above Lamda expression method does not override the Route attribute definition defined on the Controller, so if you define the Route attribute on ProductsController, the two will be combined together, for example:

  1. [Route( "products" )]
  2. public   class ProductsController : Controller
  3. {
  4. public IActionResult Index()
  5. {
  6. return Content( "Index" );
  7. }
  8. }

Then your access URL should be /products/homepage, not /homepage. However, if your code in Lamda expression mode is as follows:

  1. opt.GetRoute( "/homepage" , c => c.Action(x => x.Index()));

Then your access URL should be /homepage, because the routing string is the absolute path /homepage, not homepage.

Reference: http://www.strathweb.com/2015/03/strongly-typed-routing-asp-net-mvc-6-iapplicationmodelconvention/

<<:  Interpreting ASP.NET 5 & MVC6 Series (11): Routing

>>:  Interpreting ASP.NET 5 & MVC6 Series (14): View Component

Recommend

What are the operational strategies behind free and paid products?

Between 2013 and 2014, when Xiaomi CEO Lei was pr...

How to conduct a fission activity correctly?

This article analyzes the disadvantages of blindl...

Cook's coming out on homosexuality: Fireworks of different colors

Cook finally came out. After years of suspicion, ...

Umbrella Google: The birth of a technology empire that will last for generations

[[144870]] Recently, Google announced that it wil...

Tencent's operational path over the years

I would like to share with you four short stories...

14 tips to improve the conversion rate of advertising landing pages!

This article is a summary of the book "The B...

Take every step carefully and build a user growth system from scratch!

Recently, I have received a lot of inquiries abou...

Nankang SEO training: How to solve the slow snapshot update problem

When we get a new site, we can't rush to upda...