24

I am trying to figure out how to access the current absolute Uri -- i.e. the absolute url of the view that is currently being rendered -- from a user class in .Net Core 1.1

I found this link but it seems to be outdated and throws error after error: Getting absolute URLs using ASP.NET Core MVC 6


In my Startup.cs I have under ConfigureServices:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

In my Startup.cs I have under Configure:

IHttpContextAccessor httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
Extensions.Context.Configure(httpContextAccessor);

I have the following class:

using Microsoft.AspNetCore.Http;
using System;

namespace Framework.Extensions
{
    public static class Context
    {
        private static IHttpContextAccessor HttpContextAccessor;
        public static void Configure(IHttpContextAccessor httpContextAccessor)
        {
            HttpContextAccessor = httpContextAccessor;
        }

        private static HttpContext GetCurrentContext()
        {
            return HttpContextAccessor.HttpContext;
        }
        public static HttpContext Current = GetCurrentContext();

        private static Uri GetAbsoluteUri()
        {
            UriBuilder uriBuilder = new UriBuilder();
            uriBuilder.Scheme = GetCurrentContext().Request.Scheme;
            uriBuilder.Host = GetCurrentContext().Request.Host.ToString();
            uriBuilder.Path = GetCurrentContext().Request.Path.ToString();
            uriBuilder.Query = GetCurrentContext().Request.QueryString.ToString();
            return uriBuilder.Uri;
        }
        public static Uri AbsoluteUri = GetAbsoluteUri();
        public static string Url = GetAbsoluteUri().ToString();
        public static string AbsolutePath = GetAbsoluteUri().AbsolutePath;
    }
}

I get the following exception:

System.TypeInitializationException was unhandled by user code
HResult=-2146233036 Message=The type initializer for 'Framework.Extensions.Context' threw an exception.
TypeName=Framework.Extensions.Context InnerException: HResult=-2147467261 Message=Object reference not set to an instance of an object. Source=www StackTrace: at Framework.Extensions.Context.GetCurrentContext() in E:\Websites\Stage\www\Extensions\Context.cs:line 16 at Framework.Extensions.Context..cctor() in E:\Websites\Stage\www\Extensions\Context.cs:line 18 InnerException:

Lukas
  • 1,699
  • 1
  • 16
  • 49
eat-sleep-code
  • 4,753
  • 13
  • 52
  • 98
  • [This works](http://stackoverflow.com/a/37609162/1836935). Which error are you getting? – Daniel J.G. Mar 02 '17 at 08:14
  • @DanielJ.G. I added my code and the current error being thrown. – eat-sleep-code Mar 02 '17 at 13:29
  • You cannot have a static property `Current`! That would mean you always use the same context, but you want that per request, and of course you cannot use it on your initializer as there is no request the moment that class is initialized. Just keep the `IHttpContextAccessor ` in the static prop, then retrieve the current context inside `GetAbsoluteUri` – Daniel J.G. Mar 02 '17 at 14:46
  • @DanielJ.G. I tried removing the static property Context and accessing the current context inside the `GetAbsoluteUri()` method, but I get "Object reference not set to an instance of an object" on the following line inside the `GetAbsoluteUri()` method: `HttpContext current = HttpContextAccessor.HttpContext;` – eat-sleep-code Mar 03 '17 at 01:41
  • 1
    http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it - Make sure the `HttpContextAccessor` is not null, because it should have been set in the Configure method which you called on your startup – Daniel J.G. Mar 03 '17 at 10:36
  • It is magically working this morning. – eat-sleep-code Mar 03 '17 at 16:17
  • @DanielJ.G. If you "Answer" the question below I will give you some points. – eat-sleep-code Mar 03 '17 at 16:42

3 Answers3

38

You may use the GetDisplayUrl extension method.

var url = httpContextAccessor.HttpContext?.Request?.GetDisplayUrl();

Assuming httpContextAccessor is an object of IHttpContextAccessor which was injected via DI.

This extension method is defined in Microsoft.AspNetCore.Http.Extensions namespace. So you need to have a using statement to include it in your file.

using Microsoft.AspNetCore.Http.Extensions;

Shyju
  • 214,206
  • 104
  • 411
  • 497
  • null for me, where you using this to get a url ? :/ – Tom Stickel Feb 05 '19 at 20:58
  • Oh, actually that works fine in Controller in .net core, I wanted to access from startup.cs ... as i am migrating "legacy" .net to .net core and the accessibility is much better – Tom Stickel Feb 05 '19 at 21:08
  • GetDisplayUrl is exactly what I needed. I wonder why it's "hidden" in the extensions? – duck Nov 04 '19 at 19:44
28

You want the IHttpContextAccessor "configured or injected" in your Startup so later on when you use the helper during the context of a request you can use it to access the current HttpContext object.

You cannot store the context on a static field as that context only makes sense while serving a specific request. Typically you will leave the accessor in a static field and use it every time your helper is called.

  • Even worse you are using static fields with initializers, which are executed the first time the class is used. That means they are executed right before you call the Configure method, so there will be no IHttpContextAccessor yet configured and you will get those null references.

This would be a simple thing of writing what you want:

public static class Context
{
    private static IHttpContextAccessor HttpContextAccessor;
    public static void Configure(IHttpContextAccessor httpContextAccessor)
    {
        HttpContextAccessor = httpContextAccessor;
    }

    private static Uri GetAbsoluteUri()
    {
        var request = HttpContextAccessor.HttpContext.Request;
        UriBuilder uriBuilder = new UriBuilder();
        uriBuilder.Scheme = request.Scheme;
        uriBuilder.Host = request.Host.Host;
        uriBuilder.Path = request.Path.ToString();
        uriBuilder.Query = request.QueryString.ToString();
        return uriBuilder.Uri;
    }

    // Similar methods for Url/AbsolutePath which internally call GetAbsoluteUri
    public static string GetAbsoluteUrl() { }
    public static string GetAbsolutePath() { }
}

One more thing to bear in mind:

  • In the original question, the helper was created as a static class because they were created as extension methods. If you are not using extension methods you are not forced to use a static class.
ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
Daniel J.G.
  • 34,266
  • 9
  • 112
  • 112
  • 2
    One caveat to this: you should also consider port, so that it works in dev environments (ie localhost:1234). if (request.Host.Port.HasValue && request.Host.Port != 80 && request.Host.Port != 443) uriBuilder.Port = request.Host.Port.Value; – Daniel May 17 '18 at 16:02
  • Won't `var request = HttpContextAccessor.HttpContext.Request;` throw an error .. null ? – Tom Stickel Feb 05 '19 at 20:37
0

Issues with the other answers:

  • GetDisplayUrl does not include the query string (from this answer)
  • UriBuilder... is tedious and boilerplate-y (from this answer; the accepted answer at this time of writing)

There is a different extension method in UriHelper called GetEncodedUrl that will return the absolute path, including the query string. It's been around since .NET Core 1.0.

Usage:

using Microsoft.AspNetCore.Http.Extensions;
// ...
IHttpContextAccessor _httpContextAccessor; // Dependency-injected into constructor
// ...
var absoluteUri = _httpContextAccessor.HttpContext?.Request?.GetEncodedUrl();

If it's not null, absoluteUri takes on a value like https://<yoursite>/a/b/c?somevalue=1&othervalue=2

AndyG
  • 39,700
  • 8
  • 109
  • 143