Interpretation of ASP. NET 5 & MVC6 series tutorial (12): implementation of strong-type Routing Based on Lamda expressions, interpretation of ASP. NET
In the previous in-depth understanding of Routing, we have discussed 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. This chapter describes a type that is strongly typed Based on Lambda expressions.
The basic usage example of this method is as follows:
services.Configure<MvcOptions>(opt =>{ opt.EnableTypedRouting(); opt.GetRoute("homepage", c => c.Action<ProductsController>(x => x.Index())); opt.GetRoute("aboutpage/{name}", c => c.Action<ProductsController>(x => x.About(Param<string>.Any))); opt.PostRoute("sendcontact", c => c.Action<ProductsController>(x => x.Contact()));});
From the example, we can use GetRoute or PostRoute extension methods to define the route, and use Lambda expressions to determine the Controller type and Action method.
Note that the method name for obtaining an Action is implemented by entrusting it to execute this Action (the method is not actually executed, but the MethodInfo of the Action is obtained based on this ).
Implementation Principle
InStratup.cs
OfConfigureServices
When configuring services in the method, we can use the core configuration file for the MVC siteMvcOptions
Configuration.ApplicationModelConventions
Property (List<IApplicationModelConvention
>) You can saveIApplicationModelConvention
Interface set. You can modify the interface to process the pipeline of the MVC program model. The interface is defined as follows:
public interface IApplicationModelConvention{ void Apply(ApplicationModel application);}
InterfaceApply
The parameter type received by the method isApplicationModel
, AndApplicationModel
There are two extremely important items for us to operate. One is the Controller model set and the other is the set of various filters. The definition of this class is as follows:
public class ApplicationModel{ public ApplicationModel(); public IList<ControllerModel> Controllers { get; } public IList<IFilter> Filters { get; }}
The most important thing here isControllerModel
Class, which stores a variety of important and operational information on the instance, such as the routing definition data on the class and related actions, API description information, routing constraints, and so on, this information can be operated.
The new IApplicationModelConvention registration method is as follows:
services.Configure<MvcOptions>(opt =>{ opts.ApplicationModelConventions.Add(new MyApplicationModelConvention());});
Therefore, we can use this method to adjust and modify the response of the entire MVC program model at the right time. This feature is used to implement the strong-type routing in this chapter.
Steps
First, define a strongly typed routing model.TypedRouteModel
Class, which must inherit fromAttributeRouteModel
,AttributeRouteModel
Class is a basic model based on Attribute routing,TypedRouteModel
The class code is as follows:
public class TypedRouteModel : AttributeRouteModel{ public TypedRouteModel(string template) { Template = template; HttpMethods = new string[0]; } public TypeInfo ControllerType { get; private set; } public MethodInfo ActionMember { get; private set; } public IEnumerable<string> HttpMethods { get; private set; } public TypedRouteModel Controller<TController>() { ControllerType = typeof(TController).GetTypeInfo(); return this; } public TypedRouteModel Action<T, U>(Expression<Func<T, U>> expression) { ActionMember = GetMethodInfoInternal(expression); ControllerType = ActionMember.DeclaringType.GetTypeInfo(); return this; } public TypedRouteModel Action<T>(Expression<Action<T>> expression) { ActionMember = GetMethodInfoInternal(expression); ControllerType = ActionMember.DeclaringType.GetTypeInfo(); return this; } private static MethodInfo GetMethodInfoInternal(dynamic expression) { var method = expression.Body as MethodCallExpression; if (method != null) return method.Method; throw new ArgumentException("Expression is incorrect!"); } public TypedRouteModel WithName(string name) { Name = name; return this; } public TypedRouteModel ForHttpMethods(params string[] methods) { HttpMethods = methods; return this; }}
The main functions of this class are: support for incoming Controller type definition and support for chain call.
Then define an inheritanceIApplicationModelConvention
InterfaceTypedRoutingApplicationModelConvention
Class. The Code is as follows:
Public class usage: IApplicationModelConvention {internal static readonly Dictionary <TypeInfo, List <TypedRouteModel> Routes = new Dictionary <TypeInfo, List <TypedRouteModel> (); public void Apply (ApplicationModel application) {foreach (var controller in application. controllers) {if (Routes. containsKey (controller. controllerType) {var typedRoutes = Routes [control Ler. ControllerType]; foreach (var route in typedRoutes) {var action = controller. Actions. FirstOrDefault (x => x. ActionMethod = route. ActionMember); if (action! = Null) {action. attributeRouteModel = route; // note that this is a direct replacement, which will affect the routing foreach (var method in Route. httpMethods) {action. httpMethods. add (method );}}}}}}}
In this class, a static variable Routes is saved to save all the Routes declared in the Lamda expression mode. Then, the existing Controllers set is searched and modified, and then replaced.AttributeRouteModel
And set the Http Method for the response (if not set, all methods are allowed by default ).
Here, we simply replaceaction.AttributeRouteModel
Therefore, it may lead to some defects (for example, an Action can only support one route path, whichever is the last one). You can optimize it based on your own capabilities.
When optimizing, pay attention toRoute
The set is saved incontroller.Attributes
Attribute, the Route set on the Action is saved inaction.Attributes
Attribute, You can optimize it.
Then, on MvcOptions, we add some extension methods for TypeRouteModel for ease of use. The Code is as follows:
public static class MvcOptionsExtensions{ public static TypedRouteModel GetRoute(this MvcOptions opts, string template, Action<TypedRouteModel> configSetup) { return AddRoute(template, configSetup).ForHttpMethods("GET"); } public static TypedRouteModel PostRoute(this MvcOptions opts, string template, Action<TypedRouteModel> configSetup) { return AddRoute(template, configSetup).ForHttpMethods("POST"); } public static TypedRouteModel PutRoute(this MvcOptions opts, string template, Action<TypedRouteModel> configSetup) { return AddRoute(template, configSetup).ForHttpMethods("PUT"); } public static TypedRouteModel DeleteRoute(this MvcOptions opts, string template, Action<TypedRouteModel> configSetup) { return AddRoute(template, configSetup).ForHttpMethods("DELETE"); } public static TypedRouteModel TypedRoute(this MvcOptions opts, string template, Action<TypedRouteModel> configSetup) { return AddRoute(template, configSetup); } private static TypedRouteModel AddRoute(string template, Action<TypedRouteModel> 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<TypedRouteModel> { 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 addedEnableTypedRouting
Extension MethodMvcOptions.ApplicationModelConventions
Add a newTypedRoutingApplicationModelConvention
Type example.
Other extension methods are used to declare related route. Note that in the initial example, we can see that the method to obtain the action information is to call this action method through delegation (but not actually called), but some methods have parameters. What should we do? Therefore, we define a Param class that ignores parameters. The Code is as follows:
public static class Param<TValue>{ public static TValue Any { get { return default(TValue); } }}
In this way, we can define the routing for the About method containing parameters as follows:
opt.GetRoute("aboutpage/{name}", c => c.Action<HomeController>(x => x.About(Param<string>.Any)));
In addition, many methods in TypeRouteModel can be called in a chain, so we can also specify a name for route in this way. The sample code is as follows:
opt.GetRoute("homepage", c => c.Action<HomeController>(x => x.Index())).WithName("foo");
So far, the entire strong-type routing function has been completed. When you are using it, there is another option.
Disadvantages (or bugs)
We can see that the above implementationIApplicationModelConvention
Interface, we just have a simpleaction.AttributeRouteModel
In other words, if you already haveRoute
If it is a feature, it overwrites your information to you, leading to invalid route. For example, if you define a custom route:
public class ProductsController : Controller{ [Route("index")] public IActionResult Index() { return Content("Index"); }}
Then, a strong-type route is defined using the Lamda expression. The Code is as follows:
opt.GetRoute("homepage", c => c.Action<ProductsController>(x => x.Index()));
Then, you can only/homepage
Open access, but not through/index
Because it overwrites your Route.
However, the above Lamda expression does not cover the Route feature definition defined on the Controller. Therefore, if you define the Route feature on the ProductsController, the two will be combined. For example:
[Route("products")]public class ProductsController : Controller{ public IActionResult Index() { return Content("Index"); }}
Then your access URL should be/products/homepage
Instead/homepage
. However, if your code in the Lamda expression method is as follows:
opt.GetRoute("/homepage", c => c.Action<ProductsController>(x => x.Index()));
Then your access URL should be/homepage
Because the route character is an absolute path/homepage
Insteadhomepage
.
Reference: http://www.strathweb.com/2015/03/strongly-typed-routing-asp-net-mvc-6-iapplicationmodelconvention/