System.InvalidOperationException: '响应头无法修改,因为响应已经开始'

问题描述

我正在使用 Blazor 服务器端,并想为此设置 Cookie,因为我正在使用 HttpContext 的 SignInAsync 函数,但给我这个错误“无法修改响应标头,因为响应已经开始”。

到下一行时报错

await httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,new ClaimsPrincipal(claimsIdentity),authProperties);

我已经尝试了所有这些,但仍然面临错误

我的 Startup.cs 页面


    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using power_SLIC.Data;
    using System.Net.Http;
    using power_SLIC.Services;
    using Microsoft.AspNetCore.Components.Authorization;
    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection.Extensions;
    
    namespace power_SLIC
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            // For more information on how to configure your application,visit https://go.microsoft.com/fwlink/?LinkID=398940
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddRazorPages();
                services.AddServerSideBlazor();
                services.AddSingleton<WeatherForecastService>();
                // services.AddProtectedbrowserStorage();
                services.TryAddSingleton<IHttpContextAccessor,HttpContextAccessor>();
    
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,options => {
                            options.LoginPath = "/";
                        });
                //.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,act => {
                //    act.LoginPath = "/";
                //    act.AccessDeniedpath = "/";
                //    act.SlidingExpiration = true;
                //}
    
                // services.AddHttpClient();
                services.AddSingleton<HttpClient>();
              //  services.AddScoped<AuthenticationStateProvider,TokenAuthenticationServices>();
                //services.AddScoped<ILoginservices,TokenAuthenticationServices>();
               /// services.AddSingleton<HttpClient>();
                services.AddAuthorization(option =>
                {
                    option.AddPolicy("Employee",policy => policy.RequireClaim("IsUserEmployee","true"));
                });
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    app.UseExceptionHandler("/Error");
                    // The default HSTS value is 30 days. You may want to change this for production scenarios,see https://aka.ms/aspnetcore-hsts.
                    app.UseHsts();
                }
    
                app.UseHttpsRedirection();
                app.UseStaticFiles();
                app.UseRouting();
                app.UseAuthentication();
                app.UseAuthorization();
    
              
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapBlazorHub();
                    endpoints.MapFallbackToPage("/_Host");
                });
            }
        }
    }

我的 Razor 页面看起来像这样 登录screen.razor

@layout LoginLayout
@page "/"

@using System.Threading.Tasks
@using Microsoft.AspNetCore.Authentication
@using Microsoft.AspNetCore.Authentication.Cookies
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Mvc
@using Newtonsoft.Json
@using System.Text
@using Newtonsoft.Json.Linq
@using Microsoft.AspNetCore.ProtectedbrowserStorage
@using System.Security.Claims
@using System.Web
@*@inject ProtectedSessionStorage ProtectedSessionStore*@
@inject IJSRuntime JsRuntime;
@inject NavigationManager NavigationManager
@inject HttpClient http
@inject IHttpContextAccessor httpContextAccessor



<div style="margin-left:39%;margin-right:17%"> 
<div class="card">
    <h4 class="card-header">Login</h4>
    <div class="card-body">
        <EditForm Model="@model" >
            @*<DataAnnotationsValidator />*@
            <div class="form-group">
                <label>Username</label>
                <InputText @bind-Value="model.SSam_User" class="form-control" />
                @*<ValidationMessage For="@(() => model.Username)" />*@
            </div>
            <div class="form-group">
                <label>Password</label>
                <InputText @bind-Value="model.SSam_Pwd" type="password" class="form-control" />
                @*<ValidationMessage For="@(() => model.Password)" />*@
            </div>
            <button  class="btn btn-primary" @onclick = "AddItem" >
               
                Login
            </button>
           @* <NavLink href="account/register" @onClick = "window.location.href = 'home'" class="btn btn-link">Register</NavLink>*@
        </EditForm>
    </div>
</div>
</div>


@code{
    private bool isConnected = false;
    private Model.LoginModel model = new Model.LoginModel();
    class Values
    {
       
        public string result {get; set;}

    }


         




    class Result{
        public List<Values>  result { get; set;}
    };

   public async void AddItem()
    {
        var addItem = new Model.LoginModel {SSam_User=model.SSam_User,SSam_Pwd=model.SSam_Pwd,UnitSname = "HFHO" };
        string output = JsonConvert.SerializeObject(addItem);
        var Stringcontent = new StringContent(output,Encoding.UTF8,"application/json");
        
        var op = await http.PostAsync("https://localhost:44392/apI/Outputs/getpowerbi_token",Stringcontent);
         var resultcontent = op.Content.ReadAsstringAsync().Result;
            
       dynamic oit = JsonConvert.DeserializeObject<dynamic>(resultcontent);

        var accesstoken = oit["result"]["Token_status"].Value;
        var op1 = oit["result"]["login_response"][0]["status"].Value;

        if(op1 == "success")
        {
            var reo = httpContextAccessor.HttpContext.Request.Headers;
            var prerender = !httpContextAccessor.HttpContext.Response.Hasstarted;  

             if (!httpContextAccessor.HttpContext.Request.Headers.ContainsKey("Header"))
             {
                 if (!httpContextAccessor.HttpContext.Response.Hasstarted)
                 {
                     string result;
                     httpContextAccessor.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                     result = JsonConvert.SerializeObject(new { error = "Request doesn't contains header" });
                     httpContextAccessor.HttpContext.Response.ContentType = "application/json";
                     await httpContextAccessor.HttpContext.Response.WriteAsync(result);
                 }
                 else
                 {
                     await httpContextAccessor.HttpContext.Response.HttpContext.Response.WriteAsync(string.Empty);
                 }
                 var reo1 = httpContextAccessor.HttpContext.Response.HttpContext.Response;
    
                 AuthenticateResult.Fail("request doesn't contains header");
             }

          
            var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name,model.SSam_User),new Claim("Token",accesstoken),new Claim(ClaimTypes.Role,"User"),};



        var claimsIdentity = new ClaimsIdentity(
    claims,CookieAuthenticationDefaults.AuthenticationScheme);
            

            var authProperties = new AuthenticationProperties
            {
                IsPersistent = true,RedirectUri = "/",ExpiresUtc = DateTime.UtcNow.AddSeconds(30)
            };
            var j1 = CookieAuthenticationDefaults.AuthenticationScheme;

            
       await httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,authProperties);

     NavigationManager.Navigateto("/powerbi",true);

        }

      

    }
}
    

解决方法

问题是async void。这适用于在 Blazor 中未使用的异步事件处理程序。此类方法不能等待,这意味着调用者不会启动并等待它们完成。由于运行时不知道 AddItem 仍在运行,因此它开始向客户端发送响应。

没有结果的异步方法应该返回async Task,而不是async void

public async Task AddItem()
{
...
}
,

研究启用身份验证的模板应用:

登录页面是应用程序外部的 Razor 页面。强制您在登录时离开并重新启动 SPA 应用程序。 Blazor 应用程序只能从启动它的单个 HTTP 响应中读取 cookie。您不能写入这些 cookie。

因此,如果您想 DIY 身份验证系统,则必须以其他方式存储状态信息(会话存储)。