如何解决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 身份验证系统,则必须以其他方式存储状态信息(会话存储)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。