.NET Core中的路由約束


背景介紹

上周給大家分享了Nancy in .NET Core學習筆記 - 路由之后, 就一直在考慮.NET Core能否實現和Nancy中一樣的路由約束, 最近查閱了一下MSDN及一些國外博客, 發現.NET Core中已經實現了相同的功能,所以這里給大家分享一下。

路由約束

路由約束是路由中的一種設置,可以幫助我們限制Url中的參數類型,只有當參數符合約束條件的時候,action才會被激活並觸發。

比如我們現在有以下2個Url

[GET] /api/posts/{id}
[GET] /api/posts/{name}

我們希望當Posts后面的參數是int類型的時候觸發第一個Url所指向action, 參數是string類型的時候觸發第二個Url所指向的action。

在這種場景下,我們就需要使用路由約束。

如何添加路由約束

在.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}");

在路由屬性中使用

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

使用MapRoute方法帶Constraint參數的重載

除了行內約束,我們還可以在Startup.cs的中通過app.UseMvc()方法來添加約束。

例:

using Microsoft.AspNetCore.Routing.Constraints;
 
app.UseMvc(routes =>
{
    routes.MapRoute("default",
                    "{controller}/{action}/{id}",
                     new { controller = "Home", action = "Index" },
                     new { id = new IntRouteConstraint() });
});

.NET Core內置的路由約束

.NET Core已經提供了很多基礎的路由約束,總體上分為3種類型。

  • 檢查數據類型的約束
  • 檢查數據的值/長度/范圍的約束
  • 正則表達式約束

檢查數據類型的約束

約束 行內 Constraint類 說明
int {id:int} IntRouteConstraint 只允許int32整數
alpha {id:alpha} AlphaRouteConstraint 只能包含大小寫字母
bool {id:bool} BoolRouteConstraint 只允許布爾類型
datetime {id:datetime} DateTimeRouteConstraint 只允許日期格式
decimal {id:decimal} DecimalRouteConstraint 只允許decimal類型
double {id:double} DoubleRouteConstraint 只允許double類型
float {id:float} FloatRouteConstraint 只允許float類型
guid {id:guid} GuidRouteConstraint 只允許guid類型

檢查數據的值/長度/范圍的約束

約束 行內 Constraint類 說明
length(length) {id:length(12)} LengthRouteConstraint 字符串長度限制
maxlength(value) {id:maxlength(8)} MaxLengthRouteConstraint 字符串最大長度限制
minlength(value) {id:minlength(4)} MinLengthRouteConstraint 字符串最小長度限制
range(min,max) {id:range(18,120)} RangeRouteConstraint 數值范圍限制
min(value) {id:min(18)} MinRouteConstraint 最小數值限制
max(value) {id:max(120)} MaxRouteConstraint 最大數值限制

正則表達式約束

約束 行內 Constraint類 說明
regex(expression) {ssn:regex(^\d{{3}}-\d{{2}}-\d{{4}}$)}/ RegexRouteConstraint 正則表達式約束

自定義路由約束

和Nancy一樣,.NET Core也支持自定義路由約束,我們可以通過實現IRouteConstraint接口的Match方法來自定義路由約束。

我們舉一個和之前Nancy in .NET Core學習筆記 - 路由中的類似的例子。

當前我們有一個PostController類,代碼如下:

    [ApiController]
    public class PostController : ControllerBase
    {
        [HttpGet]
        [Route("~/api/posts/{id:int}")]
        public IActionResult GetPostById(int id)
        {
            return Content("Coming from GetPostById");
        }

        [HttpGet]
        [Route("~/api/posts/{name:alpha}")]
        public IActionResult GetPostByName(string name)
        {
            return Content("Coming from GetPostByName");
        }
    }

這時候我們添加新的action方法GetPostByEmail, 並追加一個email約束,方法如下:

    [HttpGet]
    [Route("~/api/posts/{email:email}")]
    public IActionResult GetPostByEmail(string email)
    {
        return Content("Coming from GetPostByEmail");
    }

我們希望當posts后面的參數是email格式的時候,顯示"Coming from GetPostByEmail"。

這里我們首先添加一個EmailConstraint類,並實現IRouteConstraint接口的Match方法

    public class EmailConstraint : IRouteConstraint
    {
        public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
        {
            if (httpContext == null)
                throw new ArgumentNullException(nameof(httpContext));

            if (route == null)
                throw new ArgumentNullException(nameof(route));

            if (routeKey == null)
                throw new ArgumentNullException(nameof(routeKey));

            if (values == null)
                throw new ArgumentNullException(nameof(values));

            object routeValue;

            if (values.TryGetValue(routeKey, out routeValue))
            {
                var parameterValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);

                return parameterValueString.Contains("@");
            }

            return false;
        }
    }

其中values.TryGetValue(routeKey, out routeValue)是嘗試從路由參數列表中,取出當前參數的值, 如果當前值中包含@, 我們就簡單的認為這個Email約束通過, 並返回true。


上述代碼完成之后,我們打開Startup.cs文件, 在ConfigureServices方法中, 我們將這個自定義的路由約束添加到約束列表中,並指定當前的約束名稱是email。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.Configure<RouteOptions>(routeOptions =>
    {
        routeOptions.ConstraintMap.Add("email", typeof(EmailConstraint));
    });
}

最后我們看一下效果, 頁面中正確顯示除了"Coming from GetPostByEmail"。

本篇源代碼

參考文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM