Как сделать генерацию маршрутов ASP.NET Core MVC относительной?
Контекст - приложение
Я разрабатываю приложение ASP.NET Core, используя netcoreapp2.2
(дотнет ядро 2.2). Это приложение распространяется в виде образа Docker, и оно работает хорошо. Это дополнение для HASS.IO, автоматизированной среды для Home Assistant на основе докера. Все работает хорошо
Отсутствующая функция в моем приложении: вход HASS.IO
Но... я хочу использовать функцию HASS.IO, которая называется Ingress: https://developers.home-assistant.io/docs/en/next/hassio_addon_presentation.html
Цель этой функции - позволить Home Assistant направлять http-трафик к надстройке, не управляя частью аутентификации и не требуя от владельца системы настройки сопоставления портов на своем брандмауэре для связи. Так что это очень хорошая особенность.
Маршруты MVC являются абсолютными
Чтобы использовать вход HASS.IO, приложению необходимо предоставить относительные пути для навигации. Например, когда пользователь загружает URL https://my.hass.io/a0a0a0a0_myaddon/
, дополнительный контейнер получит /
http запрос. Это означает, что вся навигация в приложении должна быть относительной.
Например, пока на корневой странице (https://my.hass.io/a0a0a0a0_myaddon/
переведено на HTTP GET /
для контейнера) добавляем следующий код бритвы:
<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.
1 ответ
Я нашел решение своего собственного вопроса. Я не знаю, если это лучший способ сделать это, но это сработало!
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));
- ВАЖНО: Вам необходимо установить пакет nuget
Scrutor
для этого https://www.nuget.org/packages/Scrutor/.
В заключение...
Я разместил свое решение в качестве PR там: https://github.com/yllibed/Zigbee2MqttAssistant/pull/2