溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ASP.NET5和MVC6中Routing路由的示例分析

發(fā)布時間:2021-09-16 17:47:01 來源:億速云 閱讀:114 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下ASP.NET5和MVC6中Routing路由的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

新版Routing功能介紹

在ASP.NET 5和MVC6中,Routing功能被全部重寫了,雖然用法有些類似,但和之前的Routing原理完全不太一樣了,該Routing框架不僅可以支持MVC和Web API,還支持一般的ASP.NET5程序。新版的改變有如下幾個部分。

首先,Routing系統(tǒng)是基于ASP.NET 5的,是一個獨(dú)立于MVC的路由框架,而不是基于MVC的。MVC只是在上面擴(kuò)展了一個快捷方式而已。

其次,在ASP.NET 5中,MVC和Web API控制器沒有區(qū)別了,即合二為一了。兩者派生于同一個Controller基類。也就是說該Routing框架是適用于兩者的,適用于MVC則意味著也適用于Web API。

最后,不管在基于約定的Route聲明還是基于Attribute的Route聲明,都可以使用內(nèi)聯(lián)約束和參數(shù)選項(xiàng)。例如,你可以約定路由中某個參數(shù)的數(shù)據(jù)類型,也可以讓一個參數(shù)標(biāo)記為可選類型,再或者給其提供一個默認(rèn)值。

Routing框架的主要流程

基本的Routing框架是基于Middleware來實(shí)現(xiàn)的,這樣就可以將其添加到HTTP的請求Pipeline中了,它可以喝其它任意Middleware一起進(jìn)行組合使用,如靜態(tài)文件處理程序、錯誤頁、或者SignalR服務(wù)器

在使用Routing框架之前,首要要了解Routing的作用,作用很簡單:

對于HTTP請求,Routing系統(tǒng)負(fù)責(zé)找出與之匹配的route,創(chuàng)建route數(shù)據(jù),并將該請求派送到該route對于的處理程序(Handler)上。Controller和Action的選擇,只是MVC的Handler的一個具體實(shí)現(xiàn),該實(shí)現(xiàn)使用route數(shù)據(jù)和HTTP請求中的其它信息來選擇要執(zhí)行的Controller和Action。在新版的MVC6中,該處理程序的名稱為MvcRouteHandler。

路由系統(tǒng)的執(zhí)行流程如下:

ASP.NET 5監(jiān)聽到一個HTTP請求。然后Routing Middleware就會嘗試將route集合中的route匹配該請求。一旦成功匹配一個請求,就找出該route對應(yīng)的handler。調(diào)用該handler上的RouteAsync方法(因?yàn)樗械膆andler都要實(shí)現(xiàn)該接口方法)。RoutingContext有一個IsHandled標(biāo)記,如果該標(biāo)記設(shè)置為true,則意味著該請求已經(jīng)被這個handler成功處理了;如果設(shè)置為false,則意味著該handler無法處理該請求,系統(tǒng)會再為此匹配一個route。

和之前的Routing系統(tǒng)有點(diǎn)不同的是,老版的Routing系統(tǒng)一旦成功匹配一個路由,就將其交由其對應(yīng)的Handler,不管對應(yīng)的Handler能不能處理該請求,所以就會出現(xiàn)route匹配成功了,但是找不到對應(yīng)的action,此時就會出現(xiàn)404錯誤,而新版對此作出了上述第4步驟的改進(jìn)(重新將控制權(quán)交回給Routing系統(tǒng),進(jìn)行重新匹配),看起來還是非常不錯的。

Route參數(shù)和約束條件的改進(jìn)

在之前的route設(shè)置中,要約束一個參數(shù)的數(shù)據(jù)類型的話,我們需要使用類型如下代碼:

routes.MapRoute(
 "Product", 
 "Product/{productId}", 
 defaults: new { controller = "Product", action = "Details" },
 constraints: new { productId = @"\d+" });

而在新版route中,就可以直接設(shè)置Product/{productId:int}了,約束條件遵守如下約定:

{parameter:constraint}

目前支持的約束如下:

約束示例說明
required"Product/{ProductName:required}"參數(shù)必選
alpha"Product/{ProductName:alpha}"匹配字母,大小寫不限
int"Product/{ProductId:int}"匹配int類型
long"Product/{ProductId:long}"匹配long類型
bool"Product/{ProductId:bool}"匹配bool類型
double"Product/{ProductId:double}"匹配double類型
float"Product/{ProductId:float}"匹配float類型
guid"Product/{ProductId:guid}"匹配guid類型
decimal"Product/{ProductId:decimal}"匹配decimal類型
datetime"Search/{datetime:datetime}"匹配datetime類型
composite"Product/{ProductId:composite}"匹配composite類型
length"Product/{ProductName:length(5)}"長度必須是5個字符
length"Product/{ProductName:length(5, 10)}"長度在5-10個之間
maxlength"Product/{productId:maxlength(10)}"最大長度為10
minlength"Product/{productId:minlength(3)}"最小長度為3
min"Product/{ProductID:min(3)}"大于等于3
max"Product/{ProductID:max(10)}"小于等于10
range"Product/{ProductID:range(5, 10)}"對應(yīng)的數(shù)組在5-10之間
Regex"Product/{productId:regex(^\d{4}$)}"符合指定的正則表達(dá)式

而對于可選參數(shù),則值需要在約束類型后面加一個問號即可,示例如下:

routes.MapRoute(
 "Product", 
 "Product/{productId:long?}", 
 new { controller = "Product", action = "Details" });

如果參數(shù)是必填的,需要保留一個默認(rèn)值的話,則可以按照如下示例進(jìn)行設(shè)置:

routes.MapRoute(
 "Product", 
 "Product/{productId:long=1000}",
 new { controller = "Product", action = "Details" });

通用Routing

關(guān)于示例使用,我們先不從MVC開始,而是先從普通的Routing使用方式開始,新版route添加的時候默認(rèn)添加的是TemplateRoute實(shí)例,并且在該實(shí)例實(shí)例化的時候要設(shè)置一個Handler。

舉例來說,我們先創(chuàng)建一個空的ASP.NET 5項(xiàng)目,并在project.json文件的dependencies節(jié)點(diǎn)中添加程序集"Microsoft.AspNet.Routing": "1.0.0-beta3",,在Startup.csConfigure方法里添加如下代碼:

public void Configure(IApplicationBuilder app)
{
 RouteCollection routes = new RouteCollection();
 routes.Add(new TemplateRoute(new DebuggerRouteHandler("RouteHandlerA"), "", null));
 routes.Add(new TemplateRoute(new DebuggerRouteHandler("RouteHandlerB"), "test/{a}/{b:int}", null));
 routes.Add(new TemplateRoute(new DebuggerRouteHandler("RouteHandlerC"), "test2", null));

 app.UseRouter(routes); // 開啟Routing功能
}

在這里,我們設(shè)置HTTP請求處理的的Handler為DebuggerRouteHandler,該類繼承于IRouter,實(shí)例代碼如下:

public class DebuggerRouteHandler : IRouter
{
 private string _name;

 public DebuggerRouteHandler(string name)
 {
 _name = name;
 }

 public string GetVirtualPath(VirtualPathContext context)
 {
 throw new NotImplementedException();
 }

 public async Task RouteAsync(RouteContext context)
 {
 var routeValues = string.Join("", context.RouteData.Values);
 var message = String.Format("{0} Values={1} ", _name, routeValues);
 await context.HttpContext.Response.WriteAsync(message);
 context.IsHandled = true;
 }
}

上述類,繼承IRouter以后,必須實(shí)現(xiàn)一個RouteAsync的方法,并且如果處理成功,則將IsHandled設(shè)置為true

訪問如下網(wǎng)址即可查看相應(yīng)的結(jié)果:

正常:`http://localhost:5000/`
正常:`http://localhost:5000/test/yyy/12`
404 :`http://localhost:5000/test/yyy/s`
正常:`http://localhost:5000/test2`
404 :`http://localhost:5000/test3`

注意:TemplateRouteDebuggerRouteHandler都繼承于IRouter,是實(shí)現(xiàn)前面所述的不出現(xiàn)404錯誤(繼續(xù)匹配下一個路由)的核心。

MVC中的Routing

在MVC示例程序中,我們只需要配置在調(diào)用app.UseMVC方法的時候,使用委托中的MapRoute方法來定義各種route就可以了。在這里我們以空白項(xiàng)目為例,來看看MVC的route如何使用。

第一步:在project.json文件的dependencies節(jié)點(diǎn)中引用程序集"Microsoft.AspNet.Mvc": "6.0.0-beta3",
第二部:添加MVC的Middleware,并使用MVC,然后添加一條默認(rèn)的路由,代碼如下:

public void ConfigureServices(IServiceCollection services)
{
 services.AddMvc();
}

public void Configure(IApplicationBuilder app)
{
 app.UseMvc(routeBuilder =>
 {
 routeBuilder.MapRoute(
  name: "default",
  template: "{controller}/{action}/{id?}",
  defaults: new { controller = "Home", action = "Index" });
 });
}

第三步:分別創(chuàng)建如下如下三種Controller,其中ProductsController繼承于Microsoft.AspNet.Mvc下的Controller。

public class ProductsController : Controller
{
 public IActionResult Index()
 {
 return Content("It Works with Controller Base Class!");
 }
}

public class DemoController
{
 public IActionResult Index()
 {
 return new ObjectResult("It Works without Controller Base Class!");
 }
}

public class APIController
{
 public object Index()
 {
 return new { Code = 100000, Data = "OK" };
 }
}

訪問http://localhost:5000/productshttp://localhost:5000/demo,均能顯示正常的輸出結(jié)果;而訪問http://localhost:5000/api的時候返回的則是json數(shù)據(jù)。

這就是我們在前面ASP.NET5新特性中所講的MVC和API合二為一了,并且也可以不繼承于Controller基類(但類名要以Controller結(jié)尾)。這種技術(shù)的核心是Controller的查找機(jī)制,關(guān)于如何在一個項(xiàng)目中查找合適的程序集,請參考《Controller與Action》章節(jié)。

新版MVC在判定Controller的時候,有2個條件:要么繼承于Controller,要么是引用MVC程序集并且類名以Controller結(jié)尾。

所以,在創(chuàng)建MVC Controller和Web API Controller的時候,如果你不需要相關(guān)的上下文(如HTTPContext、ActionContext等)的話,則可以不必繼承于Controller基類;但推薦都繼承于Controller,因?yàn)榭梢远喽嗬没惖姆椒ê蛯傩?,因?yàn)椴还芾^承不繼承,你定義的所有Controller類都要走M(jìn)VC的各個生命周期,我們通過ActionFilter來驗(yàn)證一下:

第一步:在project.json文件的dependencies節(jié)點(diǎn)中引用程序集"Microsoft.AspNet.Server.WebListener": "1.0.0-beta3"。
第二步:創(chuàng)建一個Aciton Filter,分別在Action執(zhí)行前和執(zhí)行后輸出一行文字,代碼如下:

public class ActionFilterTest : IActionFilter
{
 public void OnActionExecuting(ActionExecutingContext context)
 {
  var typeName = context.Controller.GetType().FullName;
  Console.WriteLine(typeName + "." + context.ActionDescriptor.Name + ":Start");
 }

 public void OnActionExecuted(ActionExecutedContext context)
 {
  var typeName = context.Controller.GetType().FullName;
  Console.WriteLine(typeName + "." + context.ActionDescriptor.Name + ":END");
 }
}

第三步:在ConfigureServices方法里注冊該Action Filter。

services.Configure<MvcOptions>(options =>
{
 options.Filters.Add(typeof(ActionFilterTest));
});

運(yùn)行程序,并訪問響應(yīng)的路徑,三種類型的代碼均會按計(jì)劃輸出內(nèi)容,輸出內(nèi)容如下:

RouterTest.ProductsController.Index:Start
RouterTest.ProductsController.Index:End
RouterTest.DemoController.Index:Start
RouterTest.DemoController.Index:End
RouterTest.APIController.Index:Start
RouterTest.APIController.Index:End

普通的ASP.NET5程序和MVC程序是可以在一起混合使用Routing功能的。

自定義Route

ASP.NET 5和MVC6都提供了豐富的Route自定義功能,關(guān)于普通Route的自定義,可以參考前面小節(jié)的DebuggerRouteHandler,這種方式需要實(shí)現(xiàn)自己的HTTP輸出,相當(dāng)于原來輕量級的IHttpHandler一樣。本節(jié),我們將這種在基于MVC的Route自定義功能,即定義的Route的Handler處理程序都是MvcRouteHandler。

在之前版本的MVC中,要自定義Route,一般都是繼承于RouteBase基類或Route類;而在新版的MVC6中,要實(shí)現(xiàn)自定義Route,有三種方式,分別如下:

繼承于TemplateRoute實(shí)現(xiàn)IRouter實(shí)現(xiàn)INamedRouter(注:INamedRouter和IRouter的唯一區(qū)別是多了一個名稱)

本例中,我們以繼承繼承于TemplateRoute為例,首先創(chuàng)建一個繼承于該類的子類PromoTemplateRoute,該類只匹配/promo目錄下的路徑。

public class PromoTemplateRoute : TemplateRoute
{
 public PromoTemplateRoute(IRouter target, string routeTemplate, IInlineConstraintResolver inlineConstraintResolver)
  : base(target, routeTemplate, inlineConstraintResolver: inlineConstraintResolver)
 {
 }

 public PromoTemplateRoute(IRouter target,
       string routeTemplate,
       IDictionary<string, object> defaults,
       IDictionary<string, object> constraints,
       IDictionary<string, object> dataTokens,
       IInlineConstraintResolver inlineConstraintResolver)
  : base(target, routeTemplate, defaults, constraints, dataTokens, inlineConstraintResolver)
 {
 }

 public PromoTemplateRoute(IRouter target,
       string routeName,
       string routeTemplate,
       IDictionary<string, object> defaults,
       IDictionary<string, object> constraints,
       IDictionary<string, object> dataTokens,
       IInlineConstraintResolver inlineConstraintResolver)
  : base(target, routeName, routeTemplate, defaults, constraints, dataTokens, inlineConstraintResolver)
 { }

 public async override Task RouteAsync(RouteContext context)
 {
  var requestPath = context.HttpContext.Request.Path.Value ?? string.Empty;
  if (!requestPath.StartsWith("/promo", StringComparison.OrdinalIgnoreCase))
  {
   return;
  }
  await base.RouteAsync(context);
 }
}

為了方便使用,我們也比葫蘆畫瓢,創(chuàng)建一些擴(kuò)展方法,示例如下:

public static class RouteBuilderExtensions
{
 public static IRouteBuilder MapPromoRoute(this IRouteBuilder routeCollectionBuilder, string name, string template)
 {
  MapPromoRoute(routeCollectionBuilder, name, template, defaults: null);
  return routeCollectionBuilder;
 }

 public static IRouteBuilder MapPromoRoute(this IRouteBuilder routeCollectionBuilder, string name, string template, object defaults)
 {
  return MapPromoRoute(routeCollectionBuilder, name, template, defaults, constraints: null, dataTokens: null);
 }

 public static IRouteBuilder MapPromoRoute(this IRouteBuilder routeCollectionBuilder, string name, string template, object defaults, object constraints, object dataTokens)
 {
  var inlineConstraintResolver = routeCollectionBuilder.ServiceProvider.GetService<IInlineConstraintResolver>();
  routeCollectionBuilder.Routes.Add(
   new PromoTemplateRoute(
    routeCollectionBuilder.DefaultHandler,
    name,
    template,
    ObjectToDictionary(defaults),
    ObjectToDictionary(constraints),
    ObjectToDictionary(dataTokens),
    inlineConstraintResolver));

  return routeCollectionBuilder;
 }

 private static IDictionary<string, object> ObjectToDictionary(object value)
 {
  var dictionary = value as IDictionary<string, object>;
  if (dictionary != null)
  {
   return dictionary;
  }

  return new RouteValueDictionary(value);
 }
}

使用的時候,則很簡單,和之前的方式非常類似,示例如下:

routes.MapPromoRoute(
 name: "default2",
 template: "promo/{controller}/{action}/{id?}",
 defaults: new { controller = "Home", action = "Index" });

通過這種方式,我們可以在符合路由匹配條件的時候,使用PromoTemplateRoute類來處理一些自定義邏輯,比如添加一些額外的文件頭信息等等。

基于Attribute的Routing

基于Attribute的Routing功能一直是MVC所期待的功能,在Web API已經(jīng)通過RoutePrefix(Controller上使用)和Route(Action上使用)來實(shí)現(xiàn)了。該特性在MVC 6中進(jìn)行了重寫和增強(qiáng),并且由于MVC和Web API合二而一了,所以在這兩種Controller上都可以使用該特性。

舉例來說:

[Route("bookhome")]
public class HomeController : Controller
{
 public IActionResult Index()
 {
  return View();
 }

 [Route("about")]
 public IActionResult About()
 {
  ViewBag.Message = "Your application description page.";
  return View();
 }

 [Route("contactus")]
 public IActionResult Contact()
 {
  ViewBag.Message = "Your contact page.";
  return View();
 }
}

在上述Controller上定義一個bookhome前綴,并且在About和Contact上又分別定義了action名稱,所以上述3個Action的訪問地址則是如下這種形式:

/bookhome
/bookhome/about
/bookhome/contactus

在這里,我們需要注意,Controller和Action使用的Attribute都是Route,同時,在這些路由模板字符串中,依然可以使用內(nèi)聯(lián)參數(shù),比如,我們可以定義類似這樣的路由:

[Route("products/{productId:int}")]

Controller和Action標(biāo)記位

另外,針對Route的模板字符串,不僅支持內(nèi)聯(lián)參數(shù),還支持Controller和Action的標(biāo)記位,即不用寫死該Controller或Action的名稱,使用一個[controller][action]的字符即可表示該Controller或Action的名稱。比如,我們可以在Controller上定義這樣的一個路由(Action上什么都不定義):

[Route("book/[controller]/[action]")]

這樣訪問首頁的地址就變成了:/book/Home/Index。

Web API的等價Route定義

在Web API中,我們一般還要定義GET、POST這樣的請求方式,為了方便,新版的HTTPGET等一系列方法都集成了Route功能,直接在構(gòu)造函數(shù)傳入Route模板即可,示例如下:

[HttpGet("products/{productId:int}")]

上述Route的定義,即表明,既要符合products/{productId:int}的路由規(guī)則,又要是GET請求。

其實(shí)HTTPGET這一系列Attribute也可以在普通的MVC Controller上使用,因?yàn)樵贛VC6中,MVC Controller和Web API Controller本身就是同一個東西,只不過MVC的返回類型都是IActionResult而已。Route定義,不僅僅支持GET請求,還支持POST等其它類型的請求,即不限制請求方式。在HttpXXX系列特性中,也是支持內(nèi)聯(lián)參數(shù)和[controller]、[action]標(biāo)記位的,大可放心使用。目前可用的特性類有:HttpGet、HttpPost、HttpPut、HttpDelete、HttpPatch。

非要重要Route定義規(guī)則

基于Attribute的Route定義很方便,但也很危險,具體規(guī)則和危險性如下。

規(guī)則1:Controller上定義了Route特性很危險

一旦在Controller上定義了Route特性,該Controller下的所有路由規(guī)則都不受其它規(guī)則控制了,比如,如果你定義了類似這樣的

[Route("book")]
public class HomeController : Controller
{
 public IActionResult Index()
 {
  return View();
 }

 public IActionResult About()
 {
  ViewBag.Message = "Your application description page.";
  return View();
 }
}

那么,上述2個Action你都再也沒辦法訪問了,因?yàn)槟J(rèn)的action的名稱根本就不會起作用,即/book/index/book/about這兩個路徑無法路由到對應(yīng)的Action方法上。而且/book也訪問不了,因?yàn)橛袃蓚€以上的Action,系統(tǒng)無法定位到其中一個Action上。

所以要讓上述Action能訪問,必須要在其中一個Action上定義再Route,例如:

[Route("book")]
public class HomeController : Controller
{
 public IActionResult Index()
 {
  return View();
 }

 [Route("about")]
 public IActionResult About()
 {
  ViewBag.Message = "Your application description page.";
  return View();
 }
}

這樣,就可以通過/book/about來訪問About方法了,而訪問/book則可以訪問默認(rèn)的index方法了,因?yàn)樵搃ndex方法是默認(rèn)唯一一個沒有定義路由的方法,所以他就是/book路由規(guī)則的默認(rèn)Action。如果,有3個Action的話,則必須要至少給兩個Action定義Route,示例如下:

[Route("book")]
public class HomeController : Controller
{
 [Route("index")]
 public IActionResult Index()
 {
  return View();
 }

 [Route("about")]
 public IActionResult About()
 {
  ViewBag.Message = "Your application description page.";
  return View();
 }
 public IActionResult Contact()
 {
  ViewBag.Message = "Your contact page.";
  return View();
 }
}

此時,Contact方法就是默認(rèn)/book路由的Action了,訪問/book路徑的話,就會顯示Contact對應(yīng)的頁面。

規(guī)則2:Route和HttpGet可以一起使用,但也很危險

我們前面提到,在Action上即可以使用Route特性,也可以使用HttpGet特性,兩者之間的不同,就是多了一個Http Method。很多同學(xué)可以要問兩個特性在一起使用的時候會有問題么?

其實(shí),這兩個特性是可以在一起使用的,示例如下:

[Route("book")]
public class HomeController : Controller
{
 [Route("Contact")]
 [HttpGet("home/Contact2")]
 public IActionResult Contact()
 {
  ViewBag.Message = "Your contact page.";

  return View();
 }
}

這樣/book/contact/book/home/contact2這兩個網(wǎng)址,都可以訪問了。但如果這里定義HttpGet,情況就不一樣了,示例如下:

[Route("Contact")]
[HttpPost("home/Contact2")]

此時,訪問該Action的方式,要么是以GET的方式訪問/book/contact地址,要么是以POST的方式訪問/book/home/contact2。所以為了避免出錯,建議使用的時候不要講兩者混用,即便是要同時支持GET和POST,那也是建議用同類型的HttpXXX來定義這些路由,例如:

[HttpGet("Contact")]
[HttpPost("home/Contact2")]

這樣,看起來就清晰多了。

規(guī)則3:多個Route和多個HttpXXX也可以一起使用,但也很危險

在如下示例中,我們?yōu)镠omeController定義了2個Route特性,而Contact定義了2個Route特性和1個HttpPost特性。

[Route("book")]
[Route("tom")]
public class HomeController : Controller
{
 [Route("Contact")]
 [Route("ContactUS")]
 [HttpPost("home/Contact2")]
 public IActionResult Contact()
 {
  ViewBag.Message = "Your contact page.";
  return View();
 }
}

那么,在上述代碼生效后,我們將有六種訪問來訪問該Action,這六種方式分布如下:

GET:/book/contact
GET:/book/contactus
GET:/tom/contact
GET:/tom/contactus
POST:/book/home/contact2
POST:/tom/home/contact2

但是,在視圖文件中,通過@Html.ActionLink("Contact", "Contact", "Home")生成鏈接地址的話,則默認(rèn)會使用第一個定義的Route,如果要強(qiáng)制指定順序,則可以使用Order屬性來定義排序值,默認(rèn)會優(yōu)先使用最小的值。示例如下:

[Route("book", Order = 1)]
[Route("tom", Order = 0)]
public class HomeController : Controller
{
 [Route("Contact", Order = 1)]
 [Route("ContactUS", Order = 0)]
 [HttpPost("home/Contact2", Order = 2)]
 public IActionResult Contact()
 {
  ViewBag.Message = "Your contact page.";
  return View();
 }
}

自定義內(nèi)聯(lián)參數(shù)約束

在前面的介紹中,我們知道任意類型的路由在定義的時候都支持不同的內(nèi)聯(lián)參數(shù)約束,因?yàn)檫@些約束是基于ASP.NET 5的,而不是基于MVC6的,并且這些約束還是可以擴(kuò)展的,本節(jié)我們就來看看如何自定義一些擴(kuò)展。

無參數(shù)約束

首先,我們來看一個比較簡單的約束,即無參數(shù)約束,類似于{productId:int}這樣的類型約束,假設(shè)我們要實(shí)現(xiàn)一個AABBCC字符串限定的約束,示例如下:

[Route("index/{productId:aabbcc}")]

為了確保/index/112233和/index/aabbcc是符合約束的,而/index/aabbccdd是不符合約束的,我們首先要自定義一個約束類AABBCCRouteConstraint,并實(shí)現(xiàn)IRouteConstraint接口,示例如下:

public class AABBCCRouteConstraint : IRouteConstraint
{
 public bool Match(HttpContext httpContext, IRouter route, string routeKey, IDictionary<string, object> values, RouteDirection routeDirection)
 {
  bool b = false;

  object value;
  if (values.TryGetValue(routeKey, out value) && value != null)
  {
   if (value is string) // 獲取傳入的值,比如aabbcc或112233
   {
    string aabbcc = value.ToString();
    b = !string.IsNullOrWhiteSpace(aabbcc) && aabbcc.Length == 6 && aabbcc[0] == aabbcc[1] && aabbcc[2] == aabbcc[3] && aabbcc[4] == aabbcc[5];

   }
  }

  return b;
 }
}

在該實(shí)現(xiàn)類中,要實(shí)現(xiàn)Match方法,根據(jù)傳入的各種參數(shù),判斷是否符合定義的約束,并返回true或false,Match方法的參數(shù)中,其中routeKey是約束{productId:aabbcc}對應(yīng)的參數(shù)名稱(本例中是productId),values集合中會有該productId所對應(yīng)的數(shù)字(如112233),在該方法通過響應(yīng)的判斷返回true和false。

下一步,就是要將該約束類注冊到Routing系統(tǒng)的約束集合中,在Startup.csConfigureServices方法中,執(zhí)行如下語句:

services.Configure<RouteOptions>(opt =>
{
 opt.ConstraintMap.Add("aabbcc", typeof(AABBCCRouteConstraint));
});

注意,這里注冊的aabbcc就是前面我們所指定約束名稱,完成上述步驟以后,即可實(shí)現(xiàn)類似{productId:int}的功能了。

有參數(shù)約束

一般情況下,有些時候可能需要定義一些約束的值,比如Length(1,10)來表示1-10之間的字符串長度,舉例來說,加入我們要定義一個4個參數(shù)的約束規(guī)則,如abcd(1,10,20,30)來表示一個特殊的驗(yàn)證項(xiàng),則需要聲明有4個參數(shù)的構(gòu)造函數(shù),示例如下:

public class ABCDRouteConstraint : IRouteConstraint
{
 public int A { get; private set; }
 public int B { get; private set; }
 public int C { get; private set; }
 public int D { get; private set; }
 public ABCDRouteConstraint(int a, int b, int c, int d)
 {
  A = a;B = b;C = c;D = d;
 }

 public bool Match(HttpContext httpContext, IRouter route, string routeKey, IDictionary<string, object> values, RouteDirection routeDirection)
 {
  bool b = false;

  object value;
  if (values.TryGetValue(routeKey, out value) && value != null)
  {
   var valueString = value.ToString();//這里需要進(jìn)行進(jìn)一步的驗(yàn)證工作
   return true;
  }

  return b;
 }
}

假如你在Action上了定義了如下約束:

[Route("index/{productId:abcd(1,20,30,40)}")]

那么,在注冊該約束類型以后,系統(tǒng)啟動厚掃描所有的Route進(jìn)行注冊的時候,會分析你定義的這4個值,然后會將這4個值賦值給該路由對應(yīng)的約束實(shí)例上的A、B、C、D四個屬性上,以便在HTTP請求過來的時候,分析URL上的值,看是否符合Match里定義的規(guī)則(在驗(yàn)證的時候就可以使用這4個屬性值)。

默認(rèn)約束的所有代碼可以參考: https://github.com/aspnet/Routing/tree/dev/src/Microsoft.AspNet.Routing/Constraints

另外,如果定義了4個參數(shù)的約束,那么在action上定義路由的時候則必須符合參數(shù)的數(shù)據(jù)類型,如果不符合,系統(tǒng)啟動的時候就會出錯,示例錯誤如下:

[Route("index/{productId:abcd}")] //沒有為該對象定義無參數(shù)的構(gòu)造函數(shù)

[Route("index/{productId:abcd(a)}")]
[Route("index/{productId:abcd('a')}")] //輸入字符串的格式不正確

[Route("index/{productId:abcd(1,2,3)}")] //構(gòu)造函數(shù)的參數(shù)個數(shù)和定義的參數(shù)個數(shù)不一致。

如果你定義的參數(shù)類型是字符串類型,則下面2種形式的定義都是合法的:

[Route("index/{productId:abcd(a,b,c,d)}")]
[Route("index/{productId:abcd('a','b','c','d')}")]

雖然ASP.NET 5 和MVC6的路由使用方式很簡單,但是相關(guān)的使用規(guī)則卻很復(fù)雜,大家使用的時候需要多加注意。

以上是“ASP.NET5和MVC6中Routing路由的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI