You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:44:54