首页 > 解决方案 > 如何使 ASP.NET Core MVC 路由生成相对?

问题描述

上下文 - 应用程序

我正在使用netcoreapp2.2(dotnet core 2.2)开发一个 ASP.NET Core 应用程序。此应用程序作为 Docker 映像分发,并且运行良好。它是 HASS.IO 的一个附加组件,一个基于 docker 的Home Assistant自动化环境。一切正常。

我的应用程序中缺少的功能:HASS.IO 的入口

但是......我想使用一个名为Ingress的HASS.IO功能:https ://developers.home-assistant.io/docs/en/next/hassio_addon_presentation.html#ingress

此功能的目标是允许Home Assistant将 http 流量路由到附加组件,而无需管理身份验证部分,也无需系统所有者在其防火墙上设置端口映射以进行通信。所以这是一个非常好的功能。

MVC 路由路径是绝对的

要使用 HASS.IO 入口,应用程序需要提供导航的相对路径。例如,当用户加载 urlhttps://my.hass.io/a0a0a0a0_myaddon/时,插件容器会收到一个/http 请求。这意味着应用程序中的所有导航都必须是相对的。

例如,在根页面上(https://my.hass.io/a0a0a0a0_myaddon/翻译HTTP GET /为容器的 a ),我们添加以下剃须刀代码:

<a asp-action="myAction" asp-route-id="123">this is a link</a>

我们将得到这样的结果 html,在这种情况下这是错误的:

<a href="/Home/myAction/123">this is a link</a> <!-- THIS IS A WRONG LINK! -->

这是错误的,因为它https://my.hass.io/Home/myAction/123被浏览器翻译成正确的地址,而正确的地址是https://my.hass.io/a0a0a0a0_myaddon/Home/myAction/123.

为了解决这个问题,我需要生成的 html 是这样的:

<!-- THIS WOULD BE THE RIGHT LINK [option A] -->
<a href="Home/myAction/123">this is a link</a>

<!-- THIS WOULD BE GOOD TOO [option B] -->
<a href="/a0a0a0a0_myaddon/Home/myAction/123">this is a link</a>

要解决的问题

[选项A]

有没有办法将 MVC 的路由引擎设置为输出相对路径而不是绝对路径?那将解决我的问题。

这也意味着当你在工作https://my.hass.io/a0a0a0a0_myaddon/Home/myAction/123并且你想回家时,结果应该是

<a href="../..">Return home</a>

- -或者 - -

[选项B]

另一种方法是找到一种方法来发现实际的绝对路径,并找到一种方法将其添加到 MVC 的路由机制中。

标签: asp.net-mvcasp.net-corehome-assistant

解决方案


我找到了自己问题的解决方案。我不知道这是否是最好的方法,但它奏效了!

1.为现有的创建一个包装器IUrlHelper

这个将绝对路径转换为相对路径...

private class RelativeUrlHelper : IUrlHelper
{
    private readonly IUrlHelper _inner;
    private readonly HttpContext _contextHttpContext;

    public RelativeUrlHelper(IUrlHelper inner, HttpContext contextHttpContext)
    {
        _inner = inner;
        _contextHttpContext = contextHttpContext;
    }

    private string MakeUrlRelative(string url)
    {
        if (url.Length == 0 || url[0] != '/')
        {
            return url; // that's an url going elsewhere: no need to be relative
        }

        if (url.Length > 2 && url[1] == '/')
        {
            return url; // That's a "//" url, means it's like an absolute one using the same scheme
        }

        // This is not a well-optimized algorithm, but it works!
        // You're welcome to improve it.
        var deepness = _contextHttpContext.Request.Path.Value.Split('/').Length - 2;
        if (deepness == 0)
        {
            return url.Substring(1);
        }
        else
        {
            for (var i = 0; i < deepness; i++)
            {
                url = i == 0 ? ".." + url : "../" + url;
            }
        }

        return url;
    }

    public string Action(UrlActionContext actionContext)
    {
        return MakeUrlRelative(_inner.Action(actionContext));
    }

    public string Content(string contentPath)
    {
        return MakeUrlRelative(_inner.Content(contentPath));
    }

    public bool IsLocalUrl(string url)
    {
        if (url?.StartsWith("../") ?? false)
        {
            return true;
        }

        return _inner.IsLocalUrl(url);
    }

    public string RouteUrl(UrlRouteContext routeContext) => _inner.RouteUrl(routeContext);

    public string Link(string routeName, object values) => _inner.Link(routeName, values);

    public ActionContext ActionContext => _inner.ActionContext;
}

2. 为IUrlHelperFactory

public class RelativeUrlHelperFactory : IUrlHelperFactory
{
    private readonly IUrlHelperFactory _previous;

    public RelativeUrlHelperFactory(IUrlHelperFactory previous)
    {
        _previous = previous;
    }

    public IUrlHelper GetUrlHelper(ActionContext context)
    {
        var inner = _previous.GetUrlHelper(context);

        return new RelativeUrlHelper(inner, context.HttpContext);
    }
}

3. 封装IUrlHelper在 DI/IoC

把它放在ConfigureServices()文件中Startup.cs

services.Decorate<IUrlHelperFactory>((previous, _) => new RelativeUrlHelperFactory(previous));

最后...

我在那里发布了我的解决方案作为 PR:https ://github.com/yllibed/Zigbee2MqttAssistant/pull/2


推荐阅读