Получение абсолютных URL-адресов с помощью ASP.NET Core
В MVC 5 у меня были следующие методы расширения для генерации абсолютных URL-адресов вместо относительных:
public static class UrlHelperExtensions
{
public static string AbsoluteAction(
this UrlHelper url,
string actionName,
string controllerName,
object routeValues = null)
{
string scheme = url.RequestContext.HttpContext.Request.Url.Scheme;
return url.Action(actionName, controllerName, routeValues, scheme);
}
public static string AbsoluteContent(
this UrlHelper url,
string contentPath)
{
return new Uri(url.RequestContext.HttpContext.Request.Url, url.Content(contentPath)).ToString();
}
public static string AbsoluteRouteUrl(
this UrlHelper url,
string routeName,
object routeValues = null)
{
string scheme = url.RequestContext.HttpContext.Request.Url.Scheme;
return url.RouteUrl(routeName, routeValues, scheme);
}
}
Что будет эквивалент в ASP.NET Core?
-
UrlHelper.RequestContext
больше не существует. - Вы не можете получить
HttpContext
как больше нет статического свойства HttpContext.Current
.
Насколько я могу видеть, теперь вам потребуется также HttpContext
объекты HttpContext
или HttpRequest
. Я прав? Есть ли способ получить текущий запрос?
Я даже на правильном пути, должен ли домен теперь быть переменной среды, которая просто добавляется к относительному URL? Будет ли это лучший подход?
Ответы
Ответ 1
Для ASP.NET Core 1.0 и выше
Вы можете использовать приведенный ниже код или пакет Boxed.AspNetCore NuGet или просмотреть код в репозитории Dotnet-Boxed/Framework GitHub.
/// <summary>
/// <see cref="IUrlHelper"/> extension methods.
/// </summary>
public static class UrlHelperExtensions
{
/// <summary>
/// Generates a fully qualified URL to an action method by using the specified action name, controller name and
/// route values.
/// </summary>
/// <param name="url">The URL helper.</param>
/// <param name="actionName">The name of the action method.</param>
/// <param name="controllerName">The name of the controller.</param>
/// <param name="routeValues">The route values.</param>
/// <returns>The absolute URL.</returns>
public static string AbsoluteAction(
this IUrlHelper url,
string actionName,
string controllerName,
object routeValues = null)
{
return url.Action(actionName, controllerName, routeValues, url.ActionContext.HttpContext.Request.Scheme);
}
/// <summary>
/// Generates a fully qualified URL to the specified content by using the specified content path. Converts a
/// virtual (relative) path to an application absolute path.
/// </summary>
/// <param name="url">The URL helper.</param>
/// <param name="contentPath">The content path.</param>
/// <returns>The absolute URL.</returns>
public static string AbsoluteContent(
this IUrlHelper url,
string contentPath)
{
HttpRequest request = url.ActionContext.HttpContext.Request;
return new Uri(new Uri(request.Scheme + "://" + request.Host.Value), url.Content(contentPath)).ToString();
}
/// <summary>
/// Generates a fully qualified URL to the specified route by using the route name and route values.
/// </summary>
/// <param name="url">The URL helper.</param>
/// <param name="routeName">Name of the route.</param>
/// <param name="routeValues">The route values.</param>
/// <returns>The absolute URL.</returns>
public static string AbsoluteRouteUrl(
this IUrlHelper url,
string routeName,
object routeValues = null)
{
return url.RouteUrl(routeName, routeValues, url.ActionContext.HttpContext.Request.Scheme);
}
}
Бонусный совет
Вы не можете напрямую зарегистрировать IUrlHelper
в контейнере DI. Для разрешения экземпляра IUrlHelper
необходимо использовать IUrlHelperFactory
и IActionContextAccessor
. Тем не менее, вы можете сделать следующее в качестве ярлыка:
services
.AddSingleton<IActionContextAccessor, ActionContextAccessor>()
.AddScoped<IUrlHelper>(x => x
.GetRequiredService<IUrlHelperFactory>()
.GetUrlHelper(x.GetRequiredService<IActionContextAccessor>().ActionContext));
Ответ 2
После RC2 и 1.0 вам больше не нужно вводить IHttpContextAccessor
в класс расширения. Он сразу доступен в IUrlHelper
через urlhelper.ActionContext.HttpContext.Request
. Затем вы создадите класс расширения, следуя той же идее, но проще, поскольку в него не будет задействована инъекция.
public static string AbsoluteAction(
this IUrlHelper url,
string actionName,
string controllerName,
object routeValues = null)
{
string scheme = url.ActionContext.HttpContext.Request.Scheme;
return url.Action(actionName, controllerName, routeValues, scheme);
}
Оставлять детали о том, как его создать, впрыскивая accesor в случае, если они полезны кому-то. Вы также можете просто заинтересоваться абсолютным URL-адресом текущего запроса, и в этом случае взгляните на конец ответа.
Вы можете изменить свой класс расширения, чтобы использовать IHttpContextAccessor
интерфейс, чтобы получить HttpContext
. Когда у вас есть контекст, вы можете получить HttpRequest
экземпляр HttpContext.Request
и использовать его свойства Scheme
, Host
, Protocol
и т.д., как в:
string scheme = HttpContextAccessor.HttpContext.Request.Scheme;
Например, вы можете потребовать, чтобы ваш класс был настроен с помощью HttpContextAccessor:
public static class UrlHelperExtensions
{
private static IHttpContextAccessor HttpContextAccessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
HttpContextAccessor = httpContextAccessor;
}
public static string AbsoluteAction(
this IUrlHelper url,
string actionName,
string controllerName,
object routeValues = null)
{
string scheme = HttpContextAccessor.HttpContext.Request.Scheme;
return url.Action(actionName, controllerName, routeValues, scheme);
}
....
}
Что вы можете сделать в своем классе Startup
(файл Startup.cs):
public void Configure(IApplicationBuilder app)
{
...
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
UrlHelperExtensions.Configure(httpContextAccessor);
...
}
Возможно, вы могли бы придумать разные способы получения IHttpContextAccessor
в вашем классе расширения, но если вы хотите, чтобы ваши методы были расширенными, в конце вам нужно будет ввести IHttpContextAccessor
в свой статический класс. (В противном случае вам понадобится IHttpContext
в качестве аргумента для каждого вызова)
Просто получая absoluteUri текущего запроса
Если вы просто хотите получить абсолютный uri текущего запроса, вы можете использовать методы расширения GetDisplayUrl
или GetEncodedUrl
из UriHelper
. (Что отличается от UrLHelper)
GetDisplayUrl. Возвращает комбинированные компоненты URL-адреса запроса в полностью неэкранированной форме (за исключением QueryString), подходящей только для дисплея. Этот формат не должен использоваться в заголовках HTTP или других HTTP-операции.
GetEncodedUrl. Возвращает комбинированные компоненты URL-адреса запроса в полностью экранированной форме, подходящей для использования в заголовках HTTP и других HTTP-операции.
Чтобы использовать их:
- Включить пространство имен
Microsoft.AspNet.Http.Extensions
.
- Получить экземпляр
HttpContext
. Он уже доступен в некоторых классах (например, виды бритвы), но в других вам может понадобиться ввести IHttpContextAccessor
, как описано выше.
- Затем используйте их как в
this.Context.Request.GetDisplayUrl()
Альтернативой этим методам было бы вручную создать абсолютный uri, используя значения в объекте HttpContext.Request
(подобно тому, как RequireHttpsAttribute делает):
var absoluteUri = string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent(),
request.Path.ToUriComponent(),
request.QueryString.ToUriComponent());
Ответ 3
Если вам просто нужен Uri для метода с аннотацией маршрута, у меня сработало следующее.
меры
Получить относительный URL
Отметив имя маршрута целевого действия, получите относительный URL-адрес, используя свойство URL-адреса контроллера:
var routeUrl = Url.RouteUrl("*Route Name Here*", new { *Route parameters here* });
Создать абсолютный URL
var absUrl = string.Format("{0}://{1}{2}", Request.Scheme,
Request.Host, routeUrl);
Создать новый Uri
var uri = new Uri(absUrl, UriKind.Absolute)
пример
[Produces("application/json")]
[Route("api/Children")]
public class ChildrenController : Controller
{
private readonly ApplicationDbContext _context;
public ChildrenController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/Children
[HttpGet]
public IEnumerable<Child> GetChild()
{
return _context.Child;
}
[HttpGet("uris")]
public IEnumerable<Uri> GetChildUris()
{
return from c in _context.Child
select
new Uri(
$"{Request.Scheme}://{Request.Host}{Url.RouteUrl("GetChildRoute", new { id = c.ChildId })}",
UriKind.Absolute);
}
// GET: api/Children/5
[HttpGet("{id}", Name = "GetChildRoute")]
public IActionResult GetChild([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return HttpBadRequest(ModelState);
}
Child child = _context.Child.Single(m => m.ChildId == id);
if (child == null)
{
return HttpNotFound();
}
return Ok(child);
}
}
Ответ 4
Это вариация anwser Мухаммад Рехан Саид, причем класс получает паразитную привязку к существующему классу MVC ядра .net с тем же именем, чтобы все просто работало.
namespace Microsoft.AspNetCore.Mvc
{
/// <summary>
/// <see cref="IUrlHelper"/> extension methods.
/// </summary>
public static partial class UrlHelperExtensions
{
/// <summary>
/// Generates a fully qualified URL to an action method by using the specified action name, controller name and
/// route values.
/// </summary>
/// <param name="url">The URL helper.</param>
/// <param name="actionName">The name of the action method.</param>
/// <param name="controllerName">The name of the controller.</param>
/// <param name="routeValues">The route values.</param>
/// <returns>The absolute URL.</returns>
public static string AbsoluteAction(
this IUrlHelper url,
string actionName,
string controllerName,
object routeValues = null)
{
return url.Action(actionName, controllerName, routeValues, url.ActionContext.HttpContext.Request.Scheme);
}
/// <summary>
/// Generates a fully qualified URL to the specified content by using the specified content path. Converts a
/// virtual (relative) path to an application absolute path.
/// </summary>
/// <param name="url">The URL helper.</param>
/// <param name="contentPath">The content path.</param>
/// <returns>The absolute URL.</returns>
public static string AbsoluteContent(
this IUrlHelper url,
string contentPath)
{
HttpRequest request = url.ActionContext.HttpContext.Request;
return new Uri(new Uri(request.Scheme + "://" + request.Host.Value), url.Content(contentPath)).ToString();
}
/// <summary>
/// Generates a fully qualified URL to the specified route by using the route name and route values.
/// </summary>
/// <param name="url">The URL helper.</param>
/// <param name="routeName">Name of the route.</param>
/// <param name="routeValues">The route values.</param>
/// <returns>The absolute URL.</returns>
public static string AbsoluteRouteUrl(
this IUrlHelper url,
string routeName,
object routeValues = null)
{
return url.RouteUrl(routeName, routeValues, url.ActionContext.HttpContext.Request.Scheme);
}
}
}
Ответ 5
В новом проекте ASP.Net 5 MVC в действии контроллера вы все равно можете делать this.Context
и this.Context.Request
Похоже, что в запросе уже нет свойства Url, но его свойства (схема, хост и т.д.) ) все находятся непосредственно на объекте запроса.
public IActionResult About()
{
ViewBag.Message = "Your application description page.";
var schema = this.Context.Request.Scheme;
return View();
}
Скорее или нет, вы хотите использовать this.Context или ввести свойство - это другой разговор.
Инъекция зависимостей в ASP.NET vNext
Ответ 6
Вы можете получить URL, как это:
Request.Headers["Referer"]
объяснение
Request.UrlReferer
System.UriFormatException
если заголовок HTTP Request.UrlReferer
имеет неправильный формат (что может произойти, поскольку он обычно не находится под вашим контролем).
Что касается использования Request.ServerVariables
, для MSDN:
Request.ServerVariables Collection
Коллекция ServerVariables извлекает значения предопределенных переменных среды и информацию заголовка запроса.
Request.Headers Недвижимость
Получает коллекцию заголовков HTTP.
Думаю, я не понимаю, почему вы бы предпочли Request.ServerVariables
Request.Headers
, поскольку Request.ServerVariables
содержит все переменные среды, а также заголовки, где Request.Headers - гораздо более короткий список, который содержит только заголовки.,
Поэтому лучшим решением является использование коллекции Request.Headers
для непосредственного чтения значения. Обратите внимание на предупреждения Microsoft о HTML-кодировке значения, если вы собираетесь отображать его в форме.
Ответ 7
Вам не нужно создавать метод расширения для этого
@Url.Action("Action", "Controller", null, this.Context.Request.Scheme);
Ответ 8
Если вы просто хотите преобразовать относительный путь с необязательными параметрами, я создал метод расширения для IHttpContextAccessor
public static string AbsoluteUrl(this IHttpContextAccessor httpContextAccessor, string relativeUrl, object parameters = null)
{
var request = httpContextAccessor.HttpContext.Request;
var url = new Uri(new Uri($"{request.Scheme}://{request.Host.Value}"), relativeUrl).ToString();
if (parameters != null)
{
url = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, ToDictionary(parameters));
}
return url;
}
private static Dictionary<string, string> ToDictionary(object obj)
{
var json = JsonConvert.SerializeObject(obj);
return JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
Затем вы можете вызвать метод из вашего сервиса/представления, используя введенный IHttpContextAccessor
var callbackUrl = _httpContextAccessor.AbsoluteUrl("/Identity/Account/ConfirmEmail", new { userId = applicationUser.Id, code });