我正在尝试通过 Route 属性为我的 Controller 操作之一配置以下 url 结构:
/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
所以它似乎被字符串文字连字符绊倒了。有什么想法吗?
最佳答案
如果深入挖掘,您会发现路由中间件正在贪婪地分割复杂的路由段,例如 {id:int}-{name:regex([[\w\-]]+)}
甚至在应用路线约束之前。 (在启动中使用路由属性和路由表都会发生)
这意味着:
- 网址如
products/123-foo
,路由匹配123
作为 id 和foo
作为名字。然后它将应用约束,找到123
的匹配项。是一个有效的 int 且foo
与正则表达式匹配。 - 网址如
products/123-foo-
,路由匹配123
作为 id 和foo-
作为名字。然后它将应用约束,再次找到匹配项。 - 网址如
products/123-foo-bar
,路由匹配123-foo
作为 id 和bar
作为名字。然后它将应用约束,但这一次它将失败,显示为123-foo
不是有效的整数!
如果您将参数拆分为不同的路线段(如 {id:int}/{name:regex([[\w\-]]+)}
所示),则不会出现此问题。 ,如/
将按照您的预期正确分割参数。
如果您的路线确实需要具有该形状,那么我将在路线约束中使用单个参数。此参数将包装 id 和名称:
[Route(@"/products/{combined:regex(^[[\d]]+-[[\w\-]]+$)}")]
问题是您需要从该单个参数中手动提取 ID 和名称。
- 您可以在 Controller 操作中手动执行此操作。对于一次性来说,这可能是可以接受的
您可以创建一个 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) { }
您可以使用其模型绑定(bind)程序提供程序和参数的一些注释属性创建一个新的模型绑定(bind)程序。这可能是最干净的,因为它与上面的方法类似,但以模型绑定(bind)的预期方式扩展了 MVC,但是我还没有时间探索它:
[Route(@"/products/{combined:regex(^[[\d]]+-[[\w\-]]+$)}")] public IActionResult Contact([FromUrlProduct("combined")]int id, [FromUrlProduct("combined")]string name) { }
为了调试路由约束,您可以将日志记录设置为调试,并且您应该在控制台中看到类似这样的消息(您可能需要使用 dotnet run
从控制台运行应用程序,而不是使用 VS 中的 ISS ):
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'.
您还可以手动复制 int route constraint并在services.AddMvc()
之后在启动中注册它与 services.Configure<RouteOptions>(opts => opts.ConstraintMap.Add("customint", typeof(CustomIntRouteConstraint)))
类似于 in this blog 所描述的方法也可能有助于调试。
关于ASP.NET Core 路由属性 - ID 和连字符 slug 以连字符分隔,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40009935/