43

I need to access HttpContext in a page (.cshtml) and in particular a request and then a cookie. Despite available, HttpContextAccessor always has a null value stored in its HttpContext property.

Any ideas would be much appreciated.

Thanks in advance.

EDIT: the Blazor version I use is: 0.7.0.

H H
  • 263,252
  • 30
  • 330
  • 514
Alexander Christov
  • 9,625
  • 7
  • 43
  • 58

5 Answers5

61

Add the following to Blazor.Web.App.Startup.cs:

services.AddHttpContextAccessor();

You also need this in <component-name>.cshtml

@using Microsoft.AspNetCore.Http
@inject IHttpContextAccessor httpContextAccessor

Note: At the time when this answer was written, accessing the HttpContext was done as described above. Since then, Blazor has been under rapid development, and has fundamentally changed. It is definitely deprecated the usage described above, but alas, you can still do the above, which is legitimate and right, if you access the HttpContext from a .cshtml page. This has not changed... Thus the only place from which you can access the HttpContext, without even adding the IHttpContextAccessor to the DI container, is the _Host.cshtml file, which is a Razor Pages file, with the .cshtml extension. When the code in this file is executed, Blazor is still not born, and the execution of this file will be serving the Blazor Server App. Please, see this answer as to how to do it properly...

Hope this helps...

enet
  • 41,195
  • 5
  • 76
  • 113
  • 1
    Is this "server-side" Blazor app? – Alexander Christov Dec 17 '18 at 16:29
  • It's one and the same...It doesn't matter. It can work on both... Just try it and tell us if it works for you... – enet Dec 17 '18 at 16:44
  • Seems this is the better approach. No need of extra libs. (And yes, it was not clear - the guess was correct however.) – Alexander Christov Dec 19 '18 at 15:02
  • 7
    Note that `IHttpContextAccessor` is not recommended by Microsoft: https://github.com/dotnet/aspnetcore/issues/17585#issuecomment-561715797 – user3071284 Mar 09 '20 at 19:02
  • 3
    You should not use the IHttpContextAccessor on Server side blazor. You should pass values to the App from cshtml. Please check this code sample https://github.com/wmgdev/BlazorGraphApi – Kalyan Chanumolu-MSFT Jun 23 '20 at 04:29
  • Agree...You can directly access the HttpContext without the mediation of the IHttpContextAccessor ;) – enet Jun 23 '20 at 05:12
  • Your see this answer on how to do it properly doesn't work. Despite being full of spelling mistakes and the HostModel not being constructed with the right name, the page will never render claiming that the param you've added doesn't exist in the .app. Maybe this works for WebApplication but it doesn't work server-side. If you use "ParamName=" instead of "param-ParamName=" it doesn't error, but the parameters are always null in any component. However, this answer works perfectly. – Tod Jul 22 '20 at 16:57
  • I have added the code above but still getting the error mesage ´The type or namespace name 'IHttpContextAccessor' could not be found´ – Simon Jul 31 '20 at 14:31
  • 1
    You've got to provide much more context. I can't answer your question like this. Perhaps you should create a question... Why do you need the HttpContext, where... type of application, and more. – enet Jul 31 '20 at 17:15
  • @KalyanChanumolu-MSFT in which file does your App access values from cshtml? and how can this be used to set cookies like IHttpContextAccessor? – symbiont Jan 29 '22 at 12:29
17

It depends what you want to access the HttpContext for.

Should you want to access authentication or user info, consider using the AuthenticationStateProvider instead:

@page "/"
@using System.Security.Claims
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider

<h3>ClaimsPrincipal Data</h3>

<button @onclick="GetClaimsPrincipalData">Get ClaimsPrincipal Data</button>

<p>@_authMessage</p>

@if (_claims.Count() > 0)
{
    <ul>
        @foreach (var claim in _claims)
        {
            <li>@claim.Type &ndash; @claim.Value</li>
        }
    </ul>
}

<p>@_surnameMessage</p>

@code {
    private string _authMessage;
    private string _surnameMessage;
    private IEnumerable<Claim> _claims = Enumerable.Empty<Claim>();

    private async Task GetClaimsPrincipalData()
    {
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity.IsAuthenticated)
        {
            _authMessage = $"{user.Identity.Name} is authenticated.";
            _claims = user.Claims;
            _surnameMessage = 
                $"Surname: {user.FindFirst(c => c.Type == ClaimTypes.Surname)?.Value}";
        }
        else
        {
            _authMessage = "The user is NOT authenticated.";
        }
    }
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
  • Thanks Shimmy, this is more feasible option. – ZKS Apr 22 '20 at 15:29
  • this works well. but why can't i debug through this? debugger drops out at GetAuthenticationStateAsync(). I find this irritating - don't know when this happens due to "can't debug this" or due to "faulty code never had a chance". – Gregor Jun 29 '20 at 14:29
  • 2
    this should be marked as answer. i have come across this issue as well and found HttpContextAccessor not work but can use AuthenticationStateProvider: var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var currUserClaimsPrincipal = authState.User; var currUser = await UserManager.GetUserAsync(currUserClaimsPrincipal); – James Rao Aug 19 '21 at 01:26
  • hopefully I can still reach you, but could you say how to register the AuthenticationStateProvider in the Program.cs of the Client app? I am trying to do exactly the same as you, which is actually the same reported in the documentation, but I have the problem saying that "Cannot provide a value for property 'AuthenticationStateProvider' on type 'MyApp.Blazor.Client.Shared.MainLayout'. There is no registered service of type 'Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider'." – Tarta Nov 02 '21 at 15:27
  • How can i sign in the user without using the HttpContext.SignInAsync() method? AuthenticationStateprovider doesnt provide such a method!? How do i tell the app "Hey, this identity right here, use this one!" without the signin method? – d00d Nov 16 '21 at 13:02
10

Please note that the documentation explicitly states that IHttpContextAccessor should not be used for Blazor apps: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-3.1#blazor-and-shared-state

(This does not mean that in your particular case it will not work for a particular scenario. But as you are anyway only able to get the cookie from the first request - once SignalR takes over the are no cookies anymore - you should maybe get the value when _Host.cshtml is renderend, and pass it on as a string value to the Blazor components from there.)

oliver
  • 221
  • 3
  • 7
  • To add to this answer getting the data in the _host.cshtml seems to be a recommend way from [ms](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-3.1#blazor-and-shared-state) see this [ms example](https://docs.microsoft.com/en-us/aspnet/core/blazor/security/server/additional-scenarios?view=aspnetcore-3.1#pass-tokens-to-a-blazor-server-app) that a solution can be modeled after. I got it working so in the app.razor you update a scoped service with data passed down from _host.cshtml then that can be used by injecting the service elsewhere in the app. – jkdba Dec 10 '21 at 20:39
4

blazor.Sever to Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddServerSideBlazor<Client.Startup>();

    // HttpContextAccessor
    services.AddHttpContextAccessor();
    services.AddScoped<HttpContextAccessor>();
}

blazor.Shared

public class HttpContextAccessor
{
   private readonly IHttpContextAccessor _httpContextAccessor;

   public HttpContextAccessor(IHttpContextAccessor httpContextAccessor)
   {
        _httpContextAccessor = httpContextAccessor;
   }

   public HttpContext Context => _httpContextAccessor.HttpContext;
}

blazor.Client to App.cshtml

@inject blazor.Shared.HttpContextAccessor HttpContext
<Router AppAssembly=typeof(Program).Assembly />

@functions 
{      
    protected override void OnInit()
    {
       HttpContext.Context.Request.Cookies.**

       // Or data passed through middleware in blazor.Server
       HttpContext.Context.Features.Get<T>()
    }
}

Credits: https://github.com/aspnet/Blazor/issues/1554

Flores
  • 8,226
  • 5
  • 49
  • 81
  • 1
    this cannot work: services.AddScoped(); IHttpContextAccessor is Singleton – enet Dec 17 '18 at 16:09
  • ...and shared is a library shared between both apps? not clear, have to guess. – Alexander Christov Dec 17 '18 at 16:31
  • 3
    Comrade, this solution is wrong and does not work. Incidentally, the HttpContextAccessor class is defined in Microsoft.AspNetCore.Http namespace, and should not be redefined. This class implement the IHttpContextAccessor interface. And the AddHttpContextAccessor creates a singleton object for you. So use it. – enet Dec 17 '18 at 16:51
  • 1
    Comrade says: ...and shared is a library shared between both apps? Why isn't it clear ? This is the basic of the Blazor structure. Asp.net Core developers and friends should understand that in order to be a Blazor developer, they should invest some time in learning Blazor. They can't just start developing without previous knowledge. The learning curve is less than that of Angular, but it still quite steep. – enet Dec 17 '18 at 17:01
  • This solution works well as it ensures the httpcontext will be captured back, even when doing full page refresh. – Tomislav Markovski Jul 27 '19 at 02:35
  • 10
    Note that `IHttpContextAccessor` is not recommended by Microsoft: https://github.com/dotnet/aspnetcore/issues/17585 – user3071284 Mar 09 '20 at 19:03
1

After following the Microsoft instructions here

https://learn.microsoft.com/en-us/aspnet/core/migration/31-to-50?view=aspnetcore-5.0&tabs=visual-studio

to upgrade my project to asp.net core 5.0 I caused this problem.

Step 6 says to replace the addtransient with addscoped. Not having an addtransient and just starting my blazor journey I took a guess and added the addscoped line to my code. This stops tokens from being passed along with the user.

I think the correct approach is to ONLY add this line if you are replacing the addTransient line

builder.Services.AddScoped(sp => new HttpClient{ BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 
Sinval
  • 1,315
  • 1
  • 16
  • 25
EricS
  • 45
  • 1
  • 1