Reputation: 38457
In MVC 5, I had the following extension methods to generate absolute URLs, instead of relative ones:
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);
}
}
What would the equivalent be in ASP.NET Core?
UrlHelper.RequestContext
no longer exists.HttpContext
as there is no longer a static HttpContext.Current
property.As far as I can see, you would now require the HttpContext
or HttpRequest
objects to be passed in also. Am I right? Is there some way to get hold of the current request?
Am I even on the right track, should the domain now be an environment variable, which is simple appended to the relative URL? Would this be a better approach?
Upvotes: 119
Views: 232208
Reputation: 4899
ASP.NET Core 3.0 and above already ships with the ActionLink
and PageLink
extension methods for IUrlHelper
whose very purpose is to generate absolute URLs for actions and pages respectively.
In a controller action or page, the IUrlHelper
instance can be accessed via the Url
property:
public IActionResult Index()
{
string absoluteActionUrl = Url.ActionLink("ActionName", "ControllerName");
string absolutePageUrl = Url.PageLink("/Page");
...
}
Upvotes: 3
Reputation: 38457
/// <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);
}
}
You can't directly register an IUrlHelper
in the DI container. Resolving an instance of IUrlHelper
requires you to use the IUrlHelperFactory
and IActionContextAccessor
. However, you can do the following as a shortcut:
services
.AddSingleton<IActionContextAccessor, ActionContextAccessor>()
.AddScoped<IUrlHelper>(x => x
.GetRequiredService<IUrlHelperFactory>()
.GetUrlHelper(x.GetRequiredService<IActionContextAccessor>().ActionContext));
UPDATE: This won't make ASP.NET Core 5
There are indications that you will be able to use LinkGenerator
to create absolute URLs without the need to provide a HttpContext
(This was the biggest downside of LinkGenerator
and why IUrlHelper
although more complex to setup using the solution below was easier to use) See "Make it easy to configure a host/scheme for absolute URLs with LinkGenerator".
Upvotes: 58
Reputation: 4427
You don't need to create an extension method for this
@Url.Action("Action", "Controller", values: null);
Action
- Name of the actionController
- Name of the controllervalues
- Object containing route values: aka GET parametersThere are also lots of other overloads to Url.Action
you can use to generate links.
Upvotes: 17
Reputation: 393
I just discovered you can do it with this call:
Url.Action(new UrlActionContext
{
Protocol = Request.Scheme,
Host = Request.Host.Value,
Action = "Action"
})
This will maintain the scheme, host, port, everything.
Upvotes: 8
Reputation: 807
If you just want to convert a relative path with optional parameters I created an extension method for 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);
}
You can then call the method from your service/view using the injected IHttpContextAccessor
var callbackUrl = _httpContextAccessor.AbsoluteUrl("/Identity/Account/ConfirmEmail", new { userId = applicationUser.Id, code });
Upvotes: 3
Reputation: 9803
If you simply want a Uri for a method that has a route annotation, the following worked for me.
Noting the Route name of the target action, get the relative URL using the controller's URL property as follows:
var routeUrl = Url.RouteUrl("*Route Name Here*", new { *Route parameters here* });
var absUrl = string.Format("{0}://{1}{2}", Request.Scheme,
Request.Host, routeUrl);
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);
}
}
Upvotes: 20
Reputation: 421
You can get the url like this:
Request.Headers["Referer"]
Explanation
The Request.UrlReferer
will throw a System.UriFormatException
if the referer HTTP header is malformed (which can happen since it is not usually under your control).
As for using Request.ServerVariables
, per MSDN:
Request.ServerVariables Collection
The ServerVariables collection retrieves the values of predetermined environment variables and request header information.
Request.Headers Property
Gets a collection of HTTP headers.
I guess I don't understand why you would prefer the Request.ServerVariables
over Request.Headers
, since Request.ServerVariables
contains all of the environment variables as well as the headers, where Request.Headers is a much shorter list that only contains the headers.
So the best solution is to use the Request.Headers
collection to read the value directly. Do heed Microsoft's warnings about HTML encoding the value if you are going to display it on a form, though.
Upvotes: 4
Reputation: 34992
After RC2 and 1.0 you no longer need to inject an IHttpContextAccessor
to you extension class. It is immediately available in the IUrlHelper
through the urlhelper.ActionContext.HttpContext.Request
. You would then create an extension class following the same idea, but simpler since there will be no injection involved.
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);
}
Leaving the details on how to build it injecting the accesor in case they are useful to someone. You might also just be interested in the absolute url of the current request, in which case take a look at the end of the answer.
You could modify your extension class to use the IHttpContextAccessor
interface to get the HttpContext
. Once you have the context, then you can get the HttpRequest
instance from HttpContext.Request
and use its properties Scheme
, Host
, Protocol
etc as in:
string scheme = HttpContextAccessor.HttpContext.Request.Scheme;
For example, you could require your class to be configured with an 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);
}
....
}
Which is something you can do on your Startup
class (Startup.cs file):
public void Configure(IApplicationBuilder app)
{
...
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
UrlHelperExtensions.Configure(httpContextAccessor);
...
}
You could probably come up with different ways of getting the IHttpContextAccessor
in your extension class, but if you want to keep your methods as extension methods in the end you will need to inject the IHttpContextAccessor
into your static class. (Otherwise you will need the IHttpContext
as an argument on each call)
Just getting the absoluteUri of the current request
If you just want to get the absolute uri of the current request, you can use the extension methods GetDisplayUrl
or GetEncodedUrl
from the UriHelper
class. (Which is different from the UrLHelper)
GetDisplayUrl. Returns the combined components of the request URL in a fully un-escaped form (except for the QueryString) suitable only for display. This format should not be used in HTTP headers or other HTTP operations.
GetEncodedUrl. Returns the combined components of the request URL in a fully escaped form suitable for use in HTTP headers and other HTTP operations.
In order to use them:
Microsoft.AspNet.Http.Extensions
. HttpContext
instance. It is already available in some classes (like razor views), but in others you might need to inject an IHttpContextAccessor
as explained above. this.Context.Request.GetDisplayUrl()
An alternative to those methods would be manually crafting yourself the absolute uri using the values in the HttpContext.Request
object (Similar to what the RequireHttpsAttribute does):
var absoluteUri = string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent(),
request.Path.ToUriComponent(),
request.QueryString.ToUriComponent());
Upvotes: 104
Reputation: 504
This is a variation of the anwser by Muhammad Rehan Saeed, with the class getting parasitically attached to the existing .net core MVC class of the same name, so that everything just works.
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);
}
}
}
Upvotes: 12
Reputation: 1474
In a new ASP.Net 5 MVC project in a controller action you can still do this.Context
and this.Context.Request
It looks like on the Request there is no longer a Url property but the child properties (schema, host, etc) are all on the request object directly.
public IActionResult About()
{
ViewBag.Message = "Your application description page.";
var schema = this.Context.Request.Scheme;
return View();
}
Rather or not you want to use this.Context or inject the property is another conversation. Dependency Injection in ASP.NET vNext
Upvotes: 4