忽略 ASP.NET MVC 核心路由中的第一段
Ignore first segments in ASP.NET MVC Core routing
我正在寻找能够匹配如下路由的路由定义:
/segment/xxxx/def
/segment/.../xxxx/def
/segment/that/can/span/xxxx/def
并能够 运行 具有参数 `def.
的操作 xxxx
但是这种路线是不允许的:
[Route("/{*segment}/xxx/{myparam}")]
如何做到?
您可以使用自定义 IRouter
结合正则表达式来进行高级 URL 匹配,例如这样。
public class EndsWithRoute : IRouter
{
private readonly Regex urlPattern;
private readonly string controllerName;
private readonly string actionName;
private readonly string parameterName;
private readonly IRouter handler;
public EndsWithRoute(string controllerName, string actionName, string parameterName, IRouter handler)
{
if (string.IsNullOrWhiteSpace(controllerName))
throw new ArgumentException($"'{nameof(controllerName)}' is required.");
if (string.IsNullOrWhiteSpace(actionName))
throw new ArgumentException($"'{nameof(actionName)}' is required.");
if (string.IsNullOrWhiteSpace(parameterName))
throw new ArgumentException($"'{nameof(parameterName)}' is required.");
this.controllerName = controllerName;
this.actionName = actionName;
this.parameterName = parameterName;
this.handler = handler ??
throw new ArgumentNullException(nameof(handler));
this.urlPattern = new Regex($"{actionName}/[^/]+/?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
var controller = context.Values.GetValueOrDefault("controller") as string;
var action = context.Values.GetValueOrDefault("action") as string;
var param = context.Values.GetValueOrDefault(parameterName) as string;
if (controller == controllerName && action == actionName && !string.IsNullOrEmpty(param))
{
return new VirtualPathData(this, $"{actionName}/{param}".ToLowerInvariant());
}
return null;
}
public async Task RouteAsync(RouteContext context)
{
var path = context.HttpContext.Request.Path.ToString();
// Check if the URL pattern matches
if (!urlPattern.IsMatch(path, 1))
return;
// Get the value of the last segment
var param = path.Split('/').Last();
//Invoke MVC controller/action
var routeData = context.RouteData;
routeData.Values["controller"] = controllerName;
routeData.Values["action"] = actionName;
// Putting the myParam value into route values makes it
// available to the model binder and to action method parameters.
routeData.Values[parameterName] = param;
await handler.RouteAsync(context);
}
}
用法
app.UseMvc(routes =>
{
routes.Routes.Add(new EndsWithRoute(
controllerName: "Home",
actionName: "About",
parameterName: "myParam",
handler: routes.DefaultHandler));
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
此路由已参数化,可让您传入与调用的操作方法相对应的控制器、操作和参数名称。
public class HomeController : Controller
{
public IActionResult About(string myParam)
{
ViewData["Message"] = "Your application description page.";
return View();
}
}
要使其与 any 操作方法名称匹配并能够使用该操作方法名称再次构建 URL 还需要做更多的工作。但是这条路线将允许您通过多次注册来添加额外的动作名称。
NOTE: For SEO purposes, it is generally not considered a good practice to put the same content on multiple URLs. If you do this, it is recommended to use a canonical tag to inform the search engines which of the URLs is the authoritative one.
.
我正在寻找能够匹配如下路由的路由定义:
/segment/xxxx/def
/segment/.../xxxx/def
/segment/that/can/span/xxxx/def
并能够 运行 具有参数 `def.
的操作xxxx
但是这种路线是不允许的:
[Route("/{*segment}/xxx/{myparam}")]
如何做到?
您可以使用自定义 IRouter
结合正则表达式来进行高级 URL 匹配,例如这样。
public class EndsWithRoute : IRouter
{
private readonly Regex urlPattern;
private readonly string controllerName;
private readonly string actionName;
private readonly string parameterName;
private readonly IRouter handler;
public EndsWithRoute(string controllerName, string actionName, string parameterName, IRouter handler)
{
if (string.IsNullOrWhiteSpace(controllerName))
throw new ArgumentException($"'{nameof(controllerName)}' is required.");
if (string.IsNullOrWhiteSpace(actionName))
throw new ArgumentException($"'{nameof(actionName)}' is required.");
if (string.IsNullOrWhiteSpace(parameterName))
throw new ArgumentException($"'{nameof(parameterName)}' is required.");
this.controllerName = controllerName;
this.actionName = actionName;
this.parameterName = parameterName;
this.handler = handler ??
throw new ArgumentNullException(nameof(handler));
this.urlPattern = new Regex($"{actionName}/[^/]+/?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
var controller = context.Values.GetValueOrDefault("controller") as string;
var action = context.Values.GetValueOrDefault("action") as string;
var param = context.Values.GetValueOrDefault(parameterName) as string;
if (controller == controllerName && action == actionName && !string.IsNullOrEmpty(param))
{
return new VirtualPathData(this, $"{actionName}/{param}".ToLowerInvariant());
}
return null;
}
public async Task RouteAsync(RouteContext context)
{
var path = context.HttpContext.Request.Path.ToString();
// Check if the URL pattern matches
if (!urlPattern.IsMatch(path, 1))
return;
// Get the value of the last segment
var param = path.Split('/').Last();
//Invoke MVC controller/action
var routeData = context.RouteData;
routeData.Values["controller"] = controllerName;
routeData.Values["action"] = actionName;
// Putting the myParam value into route values makes it
// available to the model binder and to action method parameters.
routeData.Values[parameterName] = param;
await handler.RouteAsync(context);
}
}
用法
app.UseMvc(routes =>
{
routes.Routes.Add(new EndsWithRoute(
controllerName: "Home",
actionName: "About",
parameterName: "myParam",
handler: routes.DefaultHandler));
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
此路由已参数化,可让您传入与调用的操作方法相对应的控制器、操作和参数名称。
public class HomeController : Controller
{
public IActionResult About(string myParam)
{
ViewData["Message"] = "Your application description page.";
return View();
}
}
要使其与 any 操作方法名称匹配并能够使用该操作方法名称再次构建 URL 还需要做更多的工作。但是这条路线将允许您通过多次注册来添加额外的动作名称。
NOTE: For SEO purposes, it is generally not considered a good practice to put the same content on multiple URLs. If you do this, it is recommended to use a canonical tag to inform the search engines which of the URLs is the authoritative one.