ASP.NET Core MVC中实现ActionLink参数以斜杠(/)替代查询字符串问号(?),并支持任意控制器、方法的任意数量参数路由映射
ASP.NET Core MVC中实现ActionLink参数以斜杠(/)替代查询字符串问号(?),并支持任意控制器、方法的任意数量参数路由映射
嘿,这个需求我之前做项目的时候刚好碰到过,要把原来带问号的查询参数改成斜杠分隔的路径格式,还得支持任意控制器、方法和参数数量,其实通过自定义路由配合参数绑定就能搞定,给你两个实用的方案:
方案一:路由参数转查询参数(兼容原有Action参数)
这个方案的思路是先定义一个能捕获所有后续路径段的路由,然后用中间件把这些路径段转换成查询参数,让ASP.NET Core默认的模型绑定器直接处理,这样你不用修改原有Action的参数定义就能直接用。
步骤1:注册自定义路由
在Program.cs里,把这个自定义路由注册在标准路由的前面(路由匹配是按顺序来的,优先匹配自定义路由):
// 支持任意数量参数的自定义Area路由 app.MapControllerRoute( name: "MyAreaWithArbitraryParams", pattern: "{area:exists}/{controller=Home}/{action=Index}/{*routeParams}" ); // 原来的标准Area路由 app.MapControllerRoute( name: "MyArea", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}" );
这里的{*routeParams}是通配符,会把/paraA/1/paraB/2这类后续路径段全部捕获成一个字符串。
步骤2:编写中间件转换参数
创建一个中间件,把捕获到的routeParams拆分成键值对,添加到请求的查询参数里:
public class RouteParamsToQueryMiddleware { private readonly RequestDelegate _next; public RouteParamsToQueryMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { if (context.Request.RouteValues.TryGetValue("routeParams", out var routeParamsObj) && routeParamsObj is string routeParams && !string.IsNullOrEmpty(routeParams)) { var segments = routeParams.Split('/'); // 按"参数名/值"的成对格式拆分 for (int i = 0; i < segments.Length; i += 2) { if (i + 1 >= segments.Length) break; // 处理奇数个路径段的异常情况 context.Request.QueryString = context.Request.QueryString.Add(segments[i], segments[i + 1]); } } await _next(context); } }
步骤3:注册中间件
在Program.cs里,把这个中间件注册在路由中间件之前:
app.UseMiddleware<RouteParamsToQueryMiddleware>(); // 后面是路由相关的中间件 app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute(...);
使用方式
现在你的Action可以直接接收对应的参数,不用做任何修改:
public IActionResult MyAction(string paraA, string paraB) { // paraA会自动拿到1,paraB拿到2 return View(); }
方案二:自定义模型绑定器(直接接收参数字典)
如果你的参数数量不固定,或者想统一用字典接收参数,可以用自定义模型绑定器的方式,直接把路径段转换成字典。
步骤1:注册自定义路由
和方案一的路由注册一样,先把自定义路由放在前面。
步骤2:编写模型绑定器和绑定器提供者
// 模型绑定器提供者,告诉框架什么时候用这个绑定器 public class RouteParamsModelBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { // 当Action参数是Dictionary<string, string>时使用这个绑定器 if (context.Metadata.ModelType == typeof(Dictionary<string, string>)) { return new RouteParamsModelBinder(); } return null; } } // 具体的绑定逻辑实现 public class RouteParamsModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { var routeParams = bindingContext.HttpContext.Request.RouteValues["routeParams"] as string; var paramDict = new Dictionary<string, string>(); if (!string.IsNullOrEmpty(routeParams)) { var segments = routeParams.Split('/'); for (int i = 0; i < segments.Length; i += 2) { if (i + 1 >= segments.Length) break; paramDict[segments[i]] = segments[i + 1]; } } bindingContext.Result = ModelBindingResult.Success(paramDict); return Task.CompletedTask; } }
步骤3:注册模型绑定器
在Program.cs里把绑定器提供者添加到服务中:
builder.Services.AddControllersWithViews(options => { // 把自定义绑定器放在最前面,优先使用 options.ModelBinderProviders.Insert(0, new RouteParamsModelBinderProvider()); });
使用方式
在Action里直接声明Dictionary<string, string>类型的参数:
public IActionResult MyAction(Dictionary<string, string> routeParams) { var paraA = routeParams["paraA"]; // 拿到1 var paraB = routeParams["paraB"]; // 拿到2 return View(); }
额外:生成这种格式的URL(ActionLink扩展)
如果需要在视图或代码里生成/Area/Controller/Action/paraA/1/paraB/2这种格式的URL,可以写一个UrlHelper的扩展方法:
public static class UrlHelperExtensions { public static string ActionWithRouteParams(this IUrlHelper urlHelper, string actionName, string controllerName, object routeValues, string areaName) { // 先拿到基础的Area/Controller/Action URL var baseUrl = urlHelper.Action(actionName, controllerName, new { area = areaName }); if (string.IsNullOrEmpty(baseUrl)) return baseUrl; // 把路由参数转换成键值对列表 var routeDict = new RouteValueDictionary(routeValues); var paramSegments = new List<string>(); foreach (var kvp in routeDict) { paramSegments.Add(kvp.Key); paramSegments.Add(kvp.Value?.ToString() ?? string.Empty); } // 如果有参数,拼接到基础URL后面 if (paramSegments.Any()) { baseUrl += "/" + string.Join("/", paramSegments); } return baseUrl; } }
在视图里使用:
@* 生成/MyArea/MyController/MyAction/paraA/1/paraB/2 *@ <a href="@Url.ActionWithRouteParams("MyAction", "MyController", new { paraA = 1, paraB = 2 }, "MyArea")">点击跳转</a>
备注:内容来源于stack exchange,提问作者WorkInProgress
相关产品推荐
相关产品推荐

