Implementation of Custom Routing Constraints in ASP. NET Core

  • 2021-11-02 00:32:09
  • OfStack

Routing constraint

In ASP. NET Core, you can pass variables on Url by defining routing templates, and you can provide default values, options, and constraints for variables.

The constraint is used by adding the specified constraint name to the property route as follows:


//  Single use 
[Route("users/{id:int}")]
public User GetUserById(int id) { }
//  Combined use 
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }

There are already 1 constraints provided inside the framework, as follows:

约束 示例 匹配项示例 说明
int {id:int} 123456789, -123456789 匹配任何整数
bool {active:bool} true, FALSE 匹配 true或 false(区分大小写)
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm 匹配有效的 DateTime 值(位于固定区域性中 - 查看警告)
decimal {price:decimal} 49.99, -1,000.01 匹配有效的 decimal 值(位于固定区域性中 - 查看警告)
double {weight:double} 1.234, -1,001.01e8 匹配有效的 double 值(位于固定区域性中 - 查看警告)
float {weight:float} 1.234, -1,001.01e8 匹配有效的 float 值(位于固定区域性中 - 查看警告)
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638, {CD2C1638-1638-72D5-1638-DEADBEEF1638} 匹配有效的 Guid 值
long {ticks:long} 123456789, -123456789 匹配有效的 long 值
minlength(value) {username:minlength(4)} Rick 字符串必须至少为 4 个字符
maxlength(value) {filename:maxlength(8)} Richard 字符串不得超过 8 个字符
length(length) {filename:length(12)} somefile.txt 字符串必须正好为 12 个字符
length(min,max) {filename:length(8,16)} somefile.txt 字符串必须至少为 8 个字符,且不得超过 16 个字符
min(value) {age:min(18)} 19 整数值必须至少为 18
max(value) {age:max(120)} 91 整数值不得超过 120
range(min,max) {age:range(18,120)} 91 整数值必须至少为 18,且不得超过 120
alpha {name:alpha} Rick 字符串必须由1个或多个字母字符(a-z,区分大小写)组成
regex(expression) {ssn:regex(^\d{{3}}-\d{{2}}-\d{{4}}$)} 123-45-6789 字符串必须匹配正则表达式(参见有关定义正则表达式的提示)
required {name:required} Rick 用于强制在 URL 生成过程中存在非参数值

Built-in constraints can be applied to most common application scenarios, but sometimes we still need to customize the effects we want.

Custom routing constraints

The custom constraint is to implement the IRouteConstraint interface and then overload the Match method, which takes four parameters.

The first parameter httpContext is the context of the current request

The second parameter route is the route to which the current constraint belongs

The third parameter, routeKey, is the name of the variable being checked, such as id in the example at the beginning of this article

The fourth parameter, values, is the dictionary value that the current Url matches, such as the route in the example at the beginning of the article. If Url is users/1, then there is a dictionary with key = id and value = 1. Of course, there are other variable values, such as controller, action and so on.

The fifth parameter, routeDirection, is an enumerated value that represents whether Url is requested by web or generated by Url. Action, etc.

As an example, we want to define a constraint that specifies that the parameters passed by the route must be the specified enumeration values.

Let's first define an enumeration:


public enum BoolEnum
{
  True,
  False
}

Then define constraints:


public class EnumConstraint : IRouteConstraint
{
  private Type _enumType;

  public EnumConstraint(string enumTypeName)
  {
    _enumType = Type.GetType(enumTypeName);
  }

  public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
  {
    var value = values[routeKey];
    if (value == null)
    {
      return false;
    }

    if (Enum.TryParse(_enumType, value.ToString(), out object result))
    {
      if (Enum.IsDefined(_enumType, result))
      {
        return true;
      }
    }

    return false;
  }
}

Add a custom constraint to the ConfigureServices method of Startup. cs:


services.Configure<RouteOptions>(options =>
{
  options.ConstraintMap.Add("enum", typeof(EnumConstraint));
});

Use constraints on routes:

(WebApplicationTest is the current namespace)


[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
  // GET: api/Test
  [HttpGet("{bool:enum(" + nameof(WebApplicationTest) + "." + nameof(BoolEnum) + ")}")]
  public string Get(BoolEnum @bool)
  {
    return "bool: " + @bool;
  }

  [HttpGet("{id:int:min(2)}", Name = "Get")]
  public string Get(int id)
  {
    return "id: " + id;
  }

  [HttpGet("{name}")]
  public string Get(string name)
  {
    return "name: " + name;
  }
}

{id: int: min (2)} Routing must use min (2), otherwise there will be a collision for id = 0 or id = 1.

Run the program to match our custom constraints when the routes are api/Test/0, api/Test/1, api/Test/True, and api/Test/False.

Matches the second route when the route is api/Test/{integer greater than 2}.

Other conditions match the third route.

Conclusion

Routing constraint is a very useful function in some scenarios, which can reduce the verification parameters in controller, realize some parameter verification functions by declarative attruibute, and some repeated verification can be extracted into constraints for public use.

constraint constructor can use injection, so can expand 10 points strong, you can query the database to do 1 parameter check.

Official website for the routing constraints are simply mentioned under 1, this article on the use of routing constraints to provide a specific example.


Related articles: