如何在服务器端Blazor中访问HttpContext?

45

我需要在一个页面(.cshtml文件)中访问HttpContext,特别是请求和一个cookie。尽管可用,但HttpContextAccessor始终在其HttpContext属性中存储null值。

非常感谢您的任何建议。

提前致谢。

编辑:我使用的Blazor版本是:0.7.0。

5个回答

63
将以下内容添加到 Blazor.Web.App.Startup.cs 中:
services.AddHttpContextAccessor();

您还需要在<component-name>.cshtml中添加此内容。

@using Microsoft.AspNetCore.Http
@inject IHttpContextAccessor httpContextAccessor

注意:在回答撰写时,访问HttpContext的方法如上所述。此后,Blazor一直在快速发展,并且已经从根本上发生了变化。它肯定已经弃用了上述描述的用法,但是,如果您从.cshtml页面访问HttpContext,则仍然可以执行上述操作,这是合法和正确的。这一点并没有改变... 因此,您可以在不添加IHttpContextAccessor到DI容器的情况下访问HttpContext的唯一位置是_Host.cshtml文件,这是一个带有.cshtml扩展名的Razor Pages文件。当执行该文件中的代码时,Blazor仍未启动,并且此文件的执行将为Blazor Server应用程序提供服务。请参见此答案以了解如何正确地执行它...

希望这可以帮到您...


1
这是“服务器端”Blazor应用程序吗? - Alexander Christov
这是同样的东西……没关系。它可以在两者上运行……只需尝试并告诉我们是否适用于您…… - enet
7
请注意,Microsoft不推荐使用IHttpContextAccessor:https://github.com/dotnet/aspnetcore/issues/17585#issuecomment-561715797 - user3071284
3
在服务器端 Blazor 中,不应使用 IHttpContextAccessor。你应该从 cshtml 将值传递给应用程序。请查看此代码示例 https://github.com/wmgdev/BlazorGraphApi - Kalyan Chanumolu-MSFT
1
你需要提供更多的上下文信息,我无法像这样回答你的问题。也许你应该提出一个问题:为什么需要HttpContext,是哪种类型的应用程序,等等。 - enet
显示剩余5条评论

17

这取决于您希望访问 HttpContext 的目的。

如果您想要访问身份验证或用户信息,请考虑使用 AuthenticationStateProvider

@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,这是更可行的选择。 - ZKS
这个很好用。但是为什么我不能通过调试器进行调试?调试器在GetAuthenticationStateAsync()处退出。我觉得这很烦人 - 不知道这是由于“无法调试此代码”还是由于“有缺陷的代码从未有机会”。 - Gregor
2
我也遇到了这个问题,发现HttpContextAccessor无法使用,但可以使用AuthenticationStateProvider:var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var currUserClaimsPrincipal = authState.User; var currUser = await UserManager.GetUserAsync(currUserClaimsPrincipal); - James Rao
希望我现在还能联系到你,但你能否告诉我如何在客户端应用程序的Program.cs中注册AuthenticationStateProvider?我正试图与你完全相同,实际上这与文档中报告的相同,但是我遇到了问题,提示“无法为类型“MyApp.Blazor.Client.Shared.MainLayout”上的属性“AuthenticationStateProvider”提供值。没有类型为“Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider”的已注册服务。”。 - Tarta
我如何在不使用HttpContext.SignInAsync()方法的情况下登录用户?AuthenticationStateprovider没有提供这样的方法!?如果没有登录方法,我该如何告诉应用程序“嘿,就是这个身份,使用它!”? - d00d

10

补充一下,从ms中获取数据似乎是推荐的方式。可以参考这个ms示例来解决问题。我已经在应用程序中实现了它。在app.razor中,您可以使用从_host.cshtml传递下来的数据更新一个作用域服务,然后通过在应用程序中注入该服务来使用它。 - jkdba

4

将blazor.Server添加到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转换为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>()
    }
}

致谢:https://github.com/aspnet/Blazor/issues/1554


1
这是行不通的:services.AddScoped<HttpContextAccessor>(); IHttpContextAccessor 是 Singleton。 - enet
...而 shared 则是两个应用程序共享的库?不太清楚,需要猜测。 - Alexander Christov
3
同志,这个解决方案是错误的并且不起作用。顺便一提,HttpContextAccessor类被定义在Microsoft.AspNetCore.Http命名空间中,不应该重新定义。这个类实现了IHttpContextAccessor接口。AddHttpContextAccessor方法会为你创建一个单例对象,所以使用它即可。 - enet
1
同志说:... shared 是两个应用程序共享的库?为什么不清楚呢?这是 Blazor 结构的基础。Asp.net Core 开发人员和朋友们应该明白,要成为 Blazor 开发人员,他们需要投入一些时间学习 Blazor。他们不能没有先前的知识就开始开发。学习曲线比 Angular 小,但仍然相当陡峭。 - enet
这个解决方案很好,因为它确保了在进行完整页面刷新时,httpcontext 将被捕获回来。 - Tomislav Markovski
10
请注意,Microsoft不推荐使用IHttpContextAccessor:https://github.com/dotnet/aspnetcore/issues/17585 - user3071284

1
按照这里的微软说明进行操作。

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

我升级项目到asp.net core 5.0时导致了这个问题。

第6步要求将addtransient替换为addscoped。由于我没有addtransient,并且刚开始学习Blazor,我猜测并在我的代码中添加了addscoped行。这会阻止令牌随用户一起传递。

我认为正确的方法是:只有在替换addTransient行时才添加此行。

builder.Services.AddScoped(sp => new HttpClient{ BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接