升级到 asp.net 核心 2.2 后为空 href

Empty href after upgrading to asp.net core 2.2

我们建立了一个 ASP.NET 核心 2.1 网站,其中 URL 像 www.example.org/uk and www.example.org/de 确定要显示的 resx 文件和内容。升级到 ASP.NET Core 2.2 后,页面加载但生成的所有 link 生成 blank/empty href。

比如一个link这个:

<a asp-controller="Home" asp-action="Contact">@Res.ContactUs</a>

将在 2.2 中生成一个空的 href,如下所示:

<a href="">Contact us</a>

但是在 2.1 中我们得到正确的 href:

<a href="/uk/contact">Contact us</a>

我们正在使用 Constraint Map 来管理基于 URL 的语言功能 - 这是代码:

Startup.cs

// configure route options {lang}, e.g. /uk, /de, /es etc
services.Configure<RouteOptions>(options =>
{
    options.LowercaseUrls = true;
    options.AppendTrailingSlash = false;
    options.ConstraintMap.Add("lang", typeof(LanguageRouteConstraint));
 });

 ...

app.UseMvc(routes =>
{
    routes.MapRoute(
       name: "LocalizedDefault",
       template: "{lang:lang}/{controller=Home}/{action=Index}/{id?}");
}

LanguageRouteConstraint.cs

public class LanguageRouteConstraint : IRouteConstraint
{
    private readonly AppLanguages _languageSettings;

    public LanguageRouteConstraint(IHostingEnvironment hostingEnvironment)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(hostingEnvironment.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);

        IConfigurationRoot configuration = builder.Build();

        _languageSettings = new AppLanguages();
        configuration.GetSection("AppLanguages").Bind(_languageSettings);
    }

    public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (!values.ContainsKey("lang"))
        {
            return false;
        }

        var lang = values["lang"].ToString();
        foreach (Language lang_in_app in _languageSettings.Dict.Values)
        {
            if (lang == lang_in_app.Icc)
            {
                return true;
            }
        }
        return false;
    }
}

我缩小了问题范围但找不到解决方法; 基本上在2.2。以上IRouteConstraint Match方法中有些参数没有设置,例如

httpContext = null
route = {Microsoft.AspNetCore.Routing.NullRouter)

在 2.1 中

 httpContext = {Microsoft.AspNetCore.Http.DefaultHttpContext}
 route = {{lang:lang}/{controller=Home}/{action=Index}/{id?}}

我在 2.1 和 2.2 之间所做的唯一区别是我改变了

var builder = new ConfigurationBuilder()
   .SetBasePath(Directory.GetCurrentDirectory())
   .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);

以下内容(由于 https://github.com/aspnet/AspNetCore/issues/4206

 var builder = new ConfigurationBuilder()
    .SetBasePath(hostingEnvironment.ContentRootPath) // using IHostingEnvironment 
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);

有什么想法吗?

更新 根据 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.2#parameter-transformer-reference ASP.NET Core 2.2 使用 EndpointRouting 而 2.1 使用 IRouter 基本逻辑。这解释了我的问题。现在,我的问题是 2.2 使用新 EndpointRouting 的代码是什么样的?

Differences from earlier versions of routing 解释这里发生了什么(强调我的):

The link generation ambient value invalidation algorithm behaves differently when used with endpoint routing.

Ambient value invalidation is the algorithm that decides which route values from the currently executing request (the ambient values) can be used in link generation operations. Conventional routing always invalidated extra route values when linking to a different action. Attribute routing didn't have this behavior prior to the release of ASP.NET Core 2.2. In earlier versions of ASP.NET Core, links to another action that use the same route parameter names resulted in link generation errors. In ASP.NET Core 2.2 or later, both forms of routing invalidate values when linking to another action.

...

Ambient values aren't reused when the linked destination is a different action or page.

在您的示例中,lang 是一个 环境值 ,因此在从 Home/IndexHome/About 时它不会被重用(不同的动作)。如果没有为 lang 指定值,则没有匹配的操作,因此会生成一个空的 href。这也在文档中描述为端点路由差异:

However, endpoint routing produces an empty string if the action doesn't exist. Conceptually, endpoint routing doesn't assume that the endpoint exists if the action doesn't exist.

如果您想继续使用端点路由,您似乎需要将 lang 值从您的控制器传递到您的视图,然后显式设置它。这是一个例子:

public class HomeController : Controller
{
    public IActionResult Index(string lang)
    {
        ViewData["lang"] = lang; // Using ViewData just for demonstration purposes.
        return View();
    }
}
<a asp-controller="Home" asp-action="Contact"
    asp-route-lang="@ViewData["lang"]">@Res.ContactUs</a>

您可以使用例如一个动作过滤器,但概念仍然相同。我看不出有另一种方法来处理这个问题(例如,能够将特定值标记为环境值),但也许其他人能够对此提出意见。

// Use the routing logic of ASP.NET Core 2.1 or earlier:
services.AddMvc(options => options.EnableEndpointRouting = false)
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

您需要显式传递路由数据中的值:

@using Microsoft.AspNetCore.Routing;
<a ... asp-route-storeId="@this.Context.GetRouteValue("storeId")">Pay Button</a>