使用RouteDataRequestCultureProvider时无法访问/{culture}/Error页面
问题:Net8本地化Web应用中错误页面路由的文化信息处理
我有一个启用请求本地化的Net8 Web应用。抛出错误时,系统无法导航到Error页面,因为它将URL中的Error视为Culture,尝试跳转到http://localhost:5245/Error/Index。
若指定app.UseExceptionHandler("/en/Error")可成功跳转,但无法提供其他语言的页面。我尝试过编写自定义错误处理器,但希望重定向到Error页面(保留页面导航),而非返回纯文本或手动编写HTML。
现有代码
Program.cs
using System.Globalization; using LocalizationWithErrors.Conventions; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization.Routing; using Microsoft.Extensions.Options; var builder = WebApplication.CreateBuilder(args); var services = builder.Services; services.Configure<RequestLocalizationOptions>(options => { var supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("fr"), }; options.DefaultRequestCulture = new RequestCulture(supportedCultures[0]); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; options.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider() { Options = options }); }); services.AddRazorPages(options => { options.Conventions.Add(new GlobalTemplatePageRouteModelConvention()); }); var app = builder.Build(); /* It works if I include the language in the path to the ExceptionHandler page, but I'm not able to add the path for multiple supported languages */ // app.UseExceptionHandler("/en/Error"); app.UseExceptionHandler("/Error"); var localizationOptions = app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value; app.UseRouting(); app.UseStaticFiles(); app.UseRequestLocalization(localizationOptions); app.MapRazorPages(); app.Run();
GlobalTemplatePageRouteModelConvention.cs
using Microsoft.AspNetCore.Mvc.ApplicationModels; namespace LocalizationWithErrors.Conventions; public class GlobalTemplatePageRouteModelConvention : IPageRouteModelConvention { public void Apply(PageRouteModel model) { var selectorCount = model.Selectors.Count(); if (!model.RelativePath.Contains("Error")) { for (int i = 0; i < selectorCount; i++) { SelectorModel selector = model.Selectors[i]; model.Selectors.Add(new SelectorModel { AttributeRouteModel = new AttributeRouteModel { Order = -1, Template = AttributeRouteModel.CombineTemplates("{culture}", selector.AttributeRouteModel!.Template ), } }); } } } }
Pages/Index.cshtml
@page @using System.Globalization @using Microsoft.AspNetCore.Localization @model IndexModel @{ ViewData["Title"] = "Home page"; var requestCultureFeature = HttpContext.Features.Get<IRequestCultureFeature>(); } <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>CultureInfo: @requestCultureFeature.RequestCulture.Culture</p> <p>CurrentUICulture: @requestCultureFeature.RequestCulture.UICulture</p> </div> <form method="POST"> <button type="submit">Cause an error</button> </form>
Pages/Index.cshtml.cs
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace LocalizationWithErrors.Pages; public class IndexModel : PageModel { /* Throw an exception to trigger the Error handler */ public async Task<IActionResult> OnPost() => throw new FileNotFoundException("Whoops"); }
Pages/Error.cshtml
@page @model ErrorModel @{ ViewData["Title"] = "Error"; } <h1 class="text-danger">Error.</h1> <h2 class="text-danger">An error occurred while processing your request.</h2> @if (Model.ShowRequestId) { <p> <strong>Request ID:</strong> <code>@Model.RequestId</code> </p> }
Pages/Error.cshtml.cs
using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace LocalizationWithErrors.Pages; [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] [IgnoreAntiforgeryToken] public class ErrorModel : PageModel { public string? RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); public void OnGet() => RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; }
解决方案
要让错误页面路由自动包含当前请求的文化信息,需完成以下修改:
1. 为Error页面添加本地化路由
修改GlobalTemplatePageRouteModelConvention.cs,取消对Error页面的路由排除,让它也生成带文化参数的路由:
using Microsoft.AspNetCore.Mvc.ApplicationModels; namespace LocalizationWithErrors.Conventions; public class GlobalTemplatePageRouteModelConvention : IPageRouteModelConvention { public void Apply(PageRouteModel model) { var selectorCount = model.Selectors.Count(); // 移除Error页面的排除逻辑,所有页面生成带culture的路由 for (int i = 0; i < selectorCount; i++) { SelectorModel selector = model.Selectors[i]; model.Selectors.Add(new SelectorModel { AttributeRouteModel = new AttributeRouteModel { Order = -1, Template = AttributeRouteModel.CombineTemplates("{culture}", selector.AttributeRouteModel!.Template ), } }); } } }
2. 动态构建异常处理路径
在Program.cs中替换固定路径的异常处理配置,改为动态获取当前请求的文化信息并构建错误页面路径:
// 替换原有的app.UseExceptionHandler("/Error"); app.UseExceptionHandler(options => { options.Run(async context => { var requestCulture = context.Features.Get<IRequestCultureFeature>()?.RequestCulture; var culture = requestCulture?.Culture.Name ?? "en"; // 默认使用英文 var errorPath = $"/{culture}/Error"; context.Response.Redirect(errorPath); await Task.CompletedTask; }); });
3. 调整中间件顺序
确保UseRequestLocalization在UseExceptionHandler之前执行,这样异常处理逻辑能正确获取当前请求的文化信息:
var localizationOptions = app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value; // 移到异常处理中间件之前 app.UseRequestLocalization(localizationOptions); app.UseExceptionHandler(options => { options.Run(async context => { var requestCulture = context.Features.Get<IRequestCultureFeature>()?.RequestCulture; var culture = requestCulture?.Culture.Name ?? "en"; var errorPath = $"/{culture}/Error"; context.Response.Redirect(errorPath); await Task.CompletedTask; }); }); app.UseRouting(); app.UseStaticFiles(); app.MapRazorPages(); app.Run();
效果验证
现在抛出异常时,系统会根据当前请求的文化自动跳转到对应错误页面:
- 英文环境下跳转到
/en/Error - 法文环境下跳转到
/fr/Error
后续可添加多语言资源文件,实现错误页面的本地化文本展示。
内容的提问来源于stack exchange,提问作者Steven Bitaxi
相关产品推荐
相关产品推荐

