受欢迎的博客标签

URL Matching in Asp .NET Core

Published

.Net 5.x

This post and the next several, we're going to take a deep dive into routing in ASP.NET Core 3.0

To start, let’s define what the internals of a URL looks like:

For our purposes, we care about the scheme, authority, path, query, and fragment. You can think of the scheme as the protocol, i.e., HTTP or HTTPS. The authority is the root or domain, for example, mycompany.com. The path, query, and fragment make up the rest of the URL. The URL spec defines each segment in this specific order. For example, the scheme always comes before the authority. The path comes after the scheme and authority. The query and fragment come after the path if there is one in the URL.

Route Templates Route Constraints
├───Route Constraints(inline)
│       ├───DataType Route Constraints 
│       ├───Data Length\Range Route Constraints  
│       └───Regular expression Route Constraints  
└───Custom Route Constraints

 

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-5.0

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-3.1#route-constraint-reference

 

DynamicRouteValuesTransformer   https://github.com/dotnet/AspNetCore.Docs/issues/12997
├───Route Constraints(inline)
│       ├───DataType Route Constraints 
│       ├───Data Length\Range Route Constraints  
│       └───Regular expression Route Constraints  
└───Custom Route Constraints

 

How Does Routing Work?

Routing uses a pair of middleware, registered by UseRouting and UseEndpoints:

app.UseRouting() adds route matching to the middleware pipeline. This middleware looks at the set of app.UseEndpoints() defined in the app, and selects the best match based on the request.
UseEndpoints adds endpoint execution to the middleware pipeline. It runs the delegate associated with the selected endpoint.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    //Other middleware
    ...
   // Location 1: before app.UseRouting() runs, endpoint is always null 
   // Matches request to an endpoint.
    app.UseRouting();

  // Location 2: after   app.UseRouting() runs, endpoint will be non-null if routing found a match


   // Execute the matched endpoint.
    app.UseEndpoints(endpoints =>
    {
        #Location 3: runs when this endpoint matches
          endpoints.MapControllerRoute(
                    name: "default",
                                pattern: "{controller=Home}/{action=Index}/{id?}");
        
           endpoints.MapModuleRoute3(); //static Routing

                var pattern = "{SeName}";
                 endpoints.MapDynamicControllerRoute<SlugRouteTransformer>(pattern);

       // Map attribute-routed API controllers
       endpoints.MapControllers(); 
       endpoints.MapDefaultControllerRoute(); // Map conventional MVC controllers using the default route
        endpoints.MapRazorPages();
    });
}

Route name and Route  pattern

This route definition has two parameters, a name and a pattern. The name is how we can call the route for use when creating links, and the pattern is the URL pattern the route is looking to match.

Route Templates

如何添加路由约束

在.NET Core中有2种添加路由约束的方法。

  • 行内约束(Inline Constraint)
  • 使用MapRoute方法带Constraint参数的重载

当路由引擎发现当前的请求Url符合某个路由设置之后,就会去触发当前路由设置中的所有路由约束,当所有的约束都返回true, 这个路由对应的action就会被激活。

行内路由及路由约束(Inline Constraint)

所谓的行内约束,即在路由Url模板中直接定义。定义的方式是在参数后面加冒号,并制定约束类型。

例:

"/api/posts/{id:int}"

该方式可以在MapRoute方法中和路由属性中使用。

在MapRoute方法中使用

routes.MapRoute("default", "{controller=Home}/{action=Index}/{id:int}");
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{id:length(24)}/{*article}",
                defaults: new { controller = "Blog", action = "blogpost" });
    
});

在路由属性中使用(Attribute routing )

[Route("Home/Index/{id:int}")]
public string Index(int id)
{
    return "I got " + id.ToString();
}
 

.NET Core内置的路由约束

.NET Core已经提供了很多基础的路由约束,总体上分为3种类型。

  • 检查数据类型的约束
  • 检查数据的值/长度/范围的约束
  • 正则表达式约束

对参数数据类型约束

约束行内Constraint类说明
int<code>{id:int}</code><code>IntRouteConstraint</code>只允许int32整数
alpha<code>{id:alpha}</code><code>AlphaRouteConstraint</code>只能包含大小写字母
bool<code>{id:bool}</code><code>BoolRouteConstraint</code>只允许布尔类型
datetime<code>{id:datetime}</code><code>DateTimeRouteConstraint</code>只允许日期格式
decimal<code>{id:decimal}</code><code>DecimalRouteConstraint</code>只允许decimal类型
double<code>{id:double}</code><code>DoubleRouteConstraint</code>只允许double类型
float<code>{id:float}</code><code>FloatRouteConstraint</code>只允许float类型
guid<code>{id:guid}</code><code>GuidRouteConstraint</code>只允许guid类型

对参数数据的值/长度/范围的约束

约束行内Constraint类说明
length(length)<code>{id:length(12)}</code><code>LengthRouteConstraint</code>字符串长度限制
maxlength(value)<code>{id:maxlength(8)}</code><code>MaxLengthRouteConstraint</code>字符串最大长度限制
minlength(value)<code>{id:minlength(4)}</code><code>MinLengthRouteConstraint</code>字符串最小长度限制
range(min,max)<code>{id:range(18,120)}</code><code>RangeRouteConstraint</code>数值范围限制
min(value)<code>{id:min(18)}</code><code>MinRouteConstraint</code>最小数值限制
max(value)<code>{id:max(120)}</code><code>MaxRouteConstraint</code>最大数值限制

 

/blog/post/{id:int:range(1,1000)}/{**title}
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }

对参数用正则表达式约束

约束行内Constraint类说明
regex(expression)<code>{ssn:regex(^\d{{3}}-\d{{2}}-\d{{4}}$)}/<code><code>RegexRouteConstraint<code>正则表达式约束

对参数用自定义路由约束(Custom Route Constraints)

Need to do more complex routing with ASP.NET Core? Try Custom Route Constraints.

We can create a class that implements IRouteConstraint and adheres to all of these rules, like so:

The segment must be an integer.
The segment must be greater than or equal to 1.
The segment must exist.

The Constraint Class:

public class RequiredPositiveIntRouteConstraint : IRouteConstraint
{
    public bool Match(HttpContext httpContext, 
                        IRouter router, 
                        string parameterName, 
                        RouteValueDictionary values, 
                        RouteDirection routeDirection)
    {
        return new IntRouteConstraint().Match(httpContext, router, parameterName, values, routeDirection)
            && new RequiredRouteConstraint().Match(httpContext, router, parameterName, values, routeDirection)
            && new MinRouteConstraint(1).Match(httpContext, router, parameterName, values, routeDirection);
    }
}

Startup.cs

We also need to attach this constraint to any route that is using it, and we can do so like this:

endpoints.MapControllerRoute(
    name: "userdetails",
    pattern: "user/{id}/{**name}",
    defaults: new {controller = "User", action = "Details"},
    constraints: new { id = new RequiredPositiveIntRouteConstraint() }
);

 

https://developpaper.com/asp-net-core-3-x-endpoint-routing-1-basic-introduction-and-use/

https://www.strathweb.com/2019/08/dynamic-controller-routing-in-asp-net-core-3-0/