如何在.NET Core中为所有Action的URL自动追加指定查询字符串?
实现方案
问题排查
- 你之前的过滤器方案通过重定向实现,会产生多余请求,不符合需求
- 你之前的中间件方案失效是因为
QueryString是值类型,Add()方法仅返回新实例,不会修改原请求的QueryString,必须重新赋值给context.Request.QueryString才能生效
完整实现步骤
1. 入站请求自动重写QueryString(无跳转,后台可正常读取参数)
编写自定义中间件,注意要放在Session中间件之后、路由中间件之前:
app.UseSession(); // 如果你用Session存preview值,必须先启用Session app.Use(async (context, next) => { // 调整为你自己的preview获取逻辑,这里示例从Session取 if (context.Session.TryGetValue("Preview", out var previewBytes) && int.TryParse(System.Text.Encoding.UTF8.GetString(previewBytes), out int preview) && preview > 0 && !context.Request.Query.ContainsKey("preview")) { // 必须把Add返回的新QueryString赋值回请求 context.Request.QueryString = context.Request.QueryString.Add("preview", preview.ToString()); } await next(); }); app.UseRouting(); // 其他中间件...
2. 全局生成Action URL自动追加preview参数
该方案会对所有Url.Action()、Url.RouteUrl()、asp-action/asp-controller标签生成的链接生效,无需手动加参数:
第一步:自定义UrlHelper
public class AppendPreviewUrlHelper : UrlHelper { private readonly IHttpContextAccessor _httpContextAccessor; public AppendPreviewUrlHelper(ActionContext actionContext, IHttpContextAccessor httpContextAccessor) : base(actionContext) { _httpContextAccessor = httpContextAccessor; } public override string Action(UrlActionContext actionContext) { var context = _httpContextAccessor.HttpContext; if (context != null && context.Request.Query.TryGetValue("preview", out var previewVal) && !string.IsNullOrEmpty(previewVal)) { actionContext.Values ??= new RouteValueDictionary(); if (!actionContext.Values.ContainsKey("preview")) { actionContext.Values.Add("preview", previewVal); } } return base.Action(actionContext); } public override string RouteUrl(UrlRouteContext routeContext) { var context = _httpContextAccessor.HttpContext; if (context != null && context.Request.Query.TryGetValue("preview", out var previewVal) && !string.IsNullOrEmpty(previewVal)) { routeContext.Values ??= new RouteValueDictionary(); if (!routeContext.Values.ContainsKey("preview")) { routeContext.Values.Add("preview", previewVal); } } return base.RouteUrl(routeContext); } }
第二步:实现自定义UrlHelper工厂
public class AppendPreviewUrlHelperFactory : IUrlHelperFactory { private readonly IHttpContextAccessor _httpContextAccessor; public AppendPreviewUrlHelperFactory(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public IUrlHelper GetUrlHelper(ActionContext context) { return new AppendPreviewUrlHelper(context, _httpContextAccessor); } }
第三步:注册服务
在Program.cs(.NET 6+)或者Startup.cs的ConfigureServices方法中注册:
// 注册HttpContextAccessor builder.Services.AddHttpContextAccessor(); // 替换默认的UrlHelper工厂 builder.Services.AddSingleton<IUrlHelperFactory, AppendPreviewUrlHelperFactory>(); // 注册Session(如果用Session存preview) builder.Services.AddSession(opt => { opt.Cookie.HttpOnly = true; opt.IdleTimeout = TimeSpan.FromMinutes(30); }); // 如果需要自动把请求里的preview存到Session,注册全局过滤器 builder.Services.AddControllersWithViews(opt => { opt.Filters.Add<SavePreviewFilter>(); });
可选:新增自动存preview的全局过滤器
public class SavePreviewFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { if (context.HttpContext.Request.Query.TryGetValue("preview", out var previewVal) && int.TryParse(previewVal, out int preview)) { context.HttpContext.Session.SetInt32("Preview", preview); } } public void OnActionExecuted(ActionExecutedContext context) { } }
内容的提问来源于stack exchange,提问作者user11469175
相关产品推荐
相关产品推荐

