简体   繁体   English

如何使ASP.NET Core MVC路由生成相对?

[英]How to make ASP.NET Core MVC routes generation relative?

Context - the application 上下文-应用程序

I'm developing an ASP.NET Core application using netcoreapp2.2 (dotnet core 2.2). 我正在使用netcoreapp2.2 (dotnet core 2.2)开发ASP.NET Core应用程序。 This application is distributed as a Docker image and it's working well. 该应用程序作为Docker映像分发,并且运行良好。 It's an Add-On for HASS.IO , an automated environment for Home Assistant based on docker. 这是一个添加为 HASS.IO的基础上,码头工人的家庭助理自动化环境。 Everything works well. 一切正常。

The missing feature in my app: HASS.IO's ingress 我的应用程序缺少的功能:HASS.IO的入口

But... I want to make use of a HASS.IO feature called Ingress : https://developers.home-assistant.io/docs/en/next/hassio_addon_presentation.html#ingress 但是...我想利用称为IngressHASS.IO功能: https : //developers.home-assistant.io/docs/en/next/hassio_addon_presentation.html#ingress

The goal of this feature is to allow Home Assistant to route the http traffic to the add-on without having to manage the authentication part and without requiring the system owner to setup a port mapping on its firewall for the communication. 此功能的目标是允许Home Assistant将http流量路由到附加组件,而无需管理身份验证部分,而无需系统所有者在其防火墙上设置端口映射以进行通信。 So it's a very nice feature. 因此,这是一个非常不错的功能。

MVC routing paths are absolute MVC路由路径是绝对的

To use HASS.IO ingress, the application needs to provide relative paths for navigation. 要使用HASS.IO入口,应用程序需要提供相对的导航路径。 By example, when the user is loading the url https://my.hass.io/a0a0a0a0_myaddon/ , the add-on container will receive a / http request. 例如,当用户加载URL https://my.hass.io/a0a0a0a0_myaddon/附加容器将收到一个/ http请求。 It means all navigation in the app must be relative. 这意味着该应用程序中的所有导航都必须是相对的。

By example, while on the root page ( https://my.hass.io/a0a0a0a0_myaddon/ translated to a HTTP GET / for the container), we add the following razor code: 例如,在根页面上( https://my.hass.io/a0a0a0a0_myaddon/转换为容器的HTTP GET / ),我们添加以下剃刀代码:

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

We'll get a resulting html like this, which is wrong in this case: 我们将得到这样的结果html,在这种情况下,这是错误的:

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

It's wrong because it's getting translated to https://my.hass.io/Home/myAction/123 by the browser while the correct address would be https://my.hass.io/a0a0a0a0_myaddon/Home/myAction/123 . 这是错误的,因为浏览器已将其翻译为https://my.hass.io/Home/myAction/123 ,而正确的地址为https://my.hass.io/a0a0a0a0_myaddon/Home/myAction/123

To fix this, I need the resulting html to be like that: 要解决此问题,我需要生成的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>

The problem to solve 要解决的问题

[option A] [选项A]

Is there a way to setup the MVC's routing engine to output relative paths instead of absolute ones? 有没有一种方法可以将MVC的路由引擎设置为输出相对路径而不是绝对路径? That would solve my problem. 那会解决我的问题。

It also means when you're on https://my.hass.io/a0a0a0a0_myaddon/Home/myAction/123 and you want to go home , the result should be 这也意味着当您使用https://my.hass.io/a0a0a0a0_myaddon/Home/myAction/123并要回家时 ,结果应该是

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

---OR--- - -要么 - -

[option B] [选项B]

Another approach would be to find a way to discover the actual absolute path and find a way to prepend it in the MVC's routing mechanism. 另一种方法是找到一种方法来发现实际的绝对路径,并找到一种在MVC的路由机制中添加该路径的方法。

I found the solution to my own question. 我找到了自己的问题的解决方案。 I don't know if it's the best way to do it, but it worked! 我不知道这是否是最好的方法,但是它有效!

1. Create a wrapper for the existing IUrlHelper 1.为现有的IUrlHelper创建包装器

This one converts absolute paths to relative ones... 这将绝对路径转换为相对路径...

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. Create a wrapper for IUrlHelperFactory 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. Wrap the IUrlHelper in DI/IoC 3.将IUrlHelper包装在DI / IoC中

Put this in the ConfigureServices() of the Startup.cs file: 将其放在Startup.cs文件的ConfigureServices()中:

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

Finally... 最后...

I posted my solution as a PR there: https://github.com/yllibed/Zigbee2MqttAssistant/pull/2 我在PR处发布了我的解决方案: https : //github.com/yllibed/Zigbee2MqttAssistant/pull/2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM