3

我想配置以下網址結構的通過路由屬性我控制器操作一個分離複姓毛坯:ASP.NET核心路由屬性 - ID和連字符

/products/12345-purest-green-widgets

這裏是我的路線它代表:

[Route(@"/products/{id:int}-{slug:regex([[\w\-]]+)}")] 
public ContentResult Show(int id, string slug) 

這不符合預期的路線,但它確實匹配:

/products/12345-purest

和如果我添加了其他任何與之不匹配的內容,我都會匹配一個單詞後的尾部連字符。

有趣的是,如果我換出字符串連字符(不是正則表達式的)爲/,它的全部工作正常:

[Route(@"/products/{id:int}/{slug:regex([[\w\-]]+)}")] 
public ContentResult Show(int id, string slug) 

匹配成功:

/products/12345/purest-green-widgets

如此看來跳到字符串文字連字符上。有任何想法嗎?

+0

嘗試'[路線(「products/{id:int} - {* slug:regex([[\ w \ - ]] +)}」)]' – Nkosi

+0

@Nkosi:這應該有效,但不會它會在過時之後做出任何限制和所有事情?不知道有關OPS具體要求 – Tseng

+0

@Tseng我想明白你的'是什麼意思,但不會做出任何限制和淘汰後,隨後的一切嗎?'你能不能改寫呢?我不知道你的意思是什麼 – Nkosi

回答

5

如果您深入研究,您會發現路由中間件即使在應用路由約束之前也會貪婪地分裂複雜的路段,如{id:int}-{name:regex([[\w\-]]+)}。 (在啓動偏偏都使用路由屬性和路由表)

這意味着:

  • 有了這樣products/123-foo URL,路由匹配123 id和foo姓名。然後它將應用約束,找到一個匹配,因爲123是一個有效的int,foo匹配正則表達式。
  • products/123-foo-這樣的網址,路由匹配123作爲id和foo-作爲名稱。然後它將應用約束條件,再次找到匹配。
  • 對於像products/123-foo-bar這樣的網址,路由匹配123-foo作爲id和bar作爲名稱。然後它將應用約束,但是這次它將失敗,因爲123-foo不是有效的int!

你沒有這個問題,如果你在{id:int}/{name:regex([[\w\-]]+)}分割在不同航段的參數,因爲/將分裂正確的參數,你會期望他們。

如果您的路線確實需要這種形狀,那麼我會在路線約束中使用單個參數。該參數將包裹都ID和名稱:

[Route(@"/products/{combined:regex(^[[\d]]+-[[\w\-]]+$)}")] 

的問題是,你會再需要從單一的參數手動提取的ID和名稱。

  • 您可以在控制器操作中手動執行該操作。爲一次性的,這可能是可接受的
  • 可以創建一個ActionFilter並執行動作之前將合併的路線參數分成動作參數(覆蓋OnActionExecuting)。這還是相當哈克,特別是我的快速和骯髒的版本:

    public class SplitProductParametersActionFilter : ActionFilterAttribute 
    { 
        private static Regex combinedRegex = new Regex(@"^([\d]+)-([\w\-]+)$"); 
        public override void OnActionExecuting(ActionExecutingContext context) 
        { 
         var combined = context.RouteData.Values["combined"].ToString(); 
         var match = combinedRegex.Match(combined); 
         if (match.Success) 
         { 
          context.ActionArguments.Add("id", int.Parse(match.Groups[1].Value)); 
          context.ActionArguments.Add("name", match.Groups[2].Value); 
         } 
        } 
    } 
    
    [Route(@"/products/{combined:regex(^[[\d]]+-[[\w\-]]+$)}")] 
    [SplitProductParametersActionFilter] 
    public IActionResult Contact(int id, string name) 
    { 
    } 
    
  • 您可以創建與它的模型綁定提供商,爲您的參數的一些註釋屬性的新模型粘合劑。這可能是最乾淨的,因爲它是類似於上面的方法,但以預期的方式擴展MVC關於模型的結合,但是我還沒有來得及去探索它:

    [Route(@"/products/{combined:regex(^[[\d]]+-[[\w\-]]+$)}")] 
    public IActionResult Contact([FromUrlProduct("combined")]int id, [FromUrlProduct("combined")]string name) 
    { 
    } 
    

爲了調試路線的限制,你可以設置記錄的調試,你應該在控制檯中看到這樣的消息(您可能需要與dotnet run從控制檯運行的應用程序,而不是使用ISS從VS的):

dbug: Microsoft.AspNetCore.Routing.RouteConstraintMatcher[1] 
     => RequestId:0HKVJG96H1RQE RequestPath:/products/1-foo-bar 
     Route value '1-foo' with key 'id' did not match the constraint 'Microsoft.AspNetCore.Routing.Constraints.IntRouteConstraint'. 

您也可以手動services.Configure<RouteOptions>(opts => opts.ConstraintMap.Add("customint", typeof(CustomIntRouteConstraint)))

像一個的方法描述in this blog也可能與調試幫助複製int route constraint並將其註冊在啓動後services.AddMvc()

+0

哇丹尼爾,很多努力已經在這裏進行了,感謝您的調查!這似乎是人們遇到的一個相當普遍的問題,因此它會被認爲是int參數很貪婪的一個bug。我也試圖將它定義爲一個正則表達式,但沒有這樣的運氣。目前,我正在採用組合模式方法並解析出各個值,但有點像你說的那樣拙劣,但似乎並沒有過分乾淨的做法。 – matthewrk

+0

這可能是值得在[路由](https://github.com/aspnet/Routing)回購(實際上,我沒有檢查這是否在那裏討論)開放問題。我也對這種行爲感到驚訝,並且這種情況可能足夠普遍 –

+0

我發現這篇文章是因爲我遇到了構建類似路線的相同問題。我在github上打開了一個問題(https://github.com/aspnet/Routing/issues/412) – aalcutt