.NET Core中的路由約束

背景介紹

上周給大家分享了Nancy in .NET Core學(xué)習(xí)筆記 - 路由之后, 就一直在考慮.NET Core能否實(shí)現(xiàn)和Nancy中一樣的路由約束, 最近查閱了一下MSDN及一些國(guó)外博客, 發(fā)現(xiàn).NET Core中已經(jīng)實(shí)現(xiàn)了相同的功能,所以這里給大家分享一下。

路由約束

路由約束是路由中的一種設(shè)置,可以幫助我們限制Url中的參數(shù)類型,只有當(dāng)參數(shù)符合約束條件的時(shí)候,action才會(huì)被激活并觸發(fā)。

比如我們現(xiàn)在有以下2個(gè)Url

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

我們希望當(dāng)Posts后面的參數(shù)是int類型的時(shí)候觸發(fā)第一個(gè)Url所指向action, 參數(shù)是string類型的時(shí)候觸發(fā)第二個(gè)Url所指向的action。

在這種場(chǎng)景下,我們就需要使用路由約束。

如何添加路由約束

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

  • 行內(nèi)約束(Inline Constraint)
  • 使用MapRoute方法帶Constraint參數(shù)的重載

當(dāng)路由引擎發(fā)現(xiàn)當(dāng)前的請(qǐng)求Url符合某個(gè)路由設(shè)置之后,就會(huì)去觸發(fā)當(dāng)前路由設(shè)置中的所有路由約束,當(dāng)所有的約束都返回true, 這個(gè)路由對(duì)應(yīng)的action就會(huì)被激活。

行內(nèi)約束(Inline Constraint)

所謂的行內(nèi)約束,即在路由Url模板中直接定義。定義的方式是在參數(shù)后面加冒號(hào),并制定約束類型。

例:

"/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參數(shù)的重載

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

例:

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內(nèi)置的路由約束

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

  • 檢查數(shù)據(jù)類型的約束
  • 檢查數(shù)據(jù)的值/長(zhǎng)度/范圍的約束
  • 正則表達(dá)式約束

檢查數(shù)據(jù)類型的約束

約束 行內(nèi) Constraint類 說(shuō)明
int <code>{id:int}</code> <code>IntRouteConstraint</code> 只允許int32整數(shù)
alpha <code>{id:alpha}</code> <code>AlphaRouteConstraint</code> 只能包含大小寫(xiě)字母
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類型

檢查數(shù)據(jù)的值/長(zhǎng)度/范圍的約束

約束 行內(nèi) Constraint類 說(shuō)明
length(length) <code>{id:length(12)}</code> <code>LengthRouteConstraint</code> 字符串長(zhǎng)度限制
maxlength(value) <code>{id:maxlength(8)}</code> <code>MaxLengthRouteConstraint</code> 字符串最大長(zhǎng)度限制
minlength(value) <code>{id:minlength(4)}</code> <code>MinLengthRouteConstraint</code> 字符串最小長(zhǎng)度限制
range(min,max) <code>{id:range(18,120)}</code> <code>RangeRouteConstraint</code> 數(shù)值范圍限制
min(value) <code>{id:min(18)}</code> <code>MinRouteConstraint</code> 最小數(shù)值限制
max(value) <code>{id:max(120)}</code> <code>MaxRouteConstraint</code> 最大數(shù)值限制

正則表達(dá)式約束

約束 行內(nèi) Constraint類 說(shuō)明
regex(expression) <code>{ssn:regex(^\d{{3}}-\d{{2}}-\d{{4}}$)}/<code> <code>RegexRouteConstraint<code> 正則表達(dá)式約束

自定義路由約束

和Nancy一樣,.NET Core也支持自定義路由約束,我們可以通過(guò)實(shí)現(xiàn)<code>IRouteConstraint</code>接口的<code>Match</code>方法來(lái)自定義路由約束。

我們舉一個(gè)和之前Nancy in .NET Core學(xué)習(xí)筆記 - 路由中的類似的例子。

當(dāng)前我們有一個(gè)<code>PostController</code>類,代碼如下:

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

這時(shí)候我們添加新的action方法GetPostByEmail, 并追加一個(gè)email約束,方法如下:

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

我們希望當(dāng)posts后面的參數(shù)是email格式的時(shí)候,顯示"Coming from GetPostByEmail"。

這里我們首先添加一個(gè)<code>EmailConstraint</code>類,并實(shí)現(xiàn)<code>IRouteConstraint</code>接口的<code>Match</code>方法

    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;
        }
    }

其中<code>values.TryGetValue(routeKey, out routeValue)</code>是嘗試從路由參數(shù)列表中,取出當(dāng)前參數(shù)的值, 如果當(dāng)前值中包含@, 我們就簡(jiǎn)單的認(rèn)為這個(gè)Email約束通過(guò), 并返回true。


上述代碼完成之后,我們打開(kāi)Startup.cs文件, 在<code>ConfigureServices</code>方法中, 我們將這個(gè)自定義的路由約束添加到約束列表中,并指定當(dāng)前的約束名稱是email。

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

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

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

image

本篇源代碼

參考文獻(xiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容