我今天问你这个问题是因为我已经被这个问题困扰了一周了。实际上,我想使用服务层类来处理用户在我的网站中的登录和注册。注册部分工作得很好,但登录部分没有,因为我收到以下错误:
Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer:警告:未处理的异常呈现组件:标头为只读,响应已启动。
System.InvalidOperationException:标头为只读,响应具有已启动。在Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()在Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseHeaders.Microsoft.AspNetCore.Http.HeaderDictionary.set_SetCookie(字符串值值)在Microsoft.AspNetCore.Http.ResponseCookies.Append(字符串键、字符串值、CookieOptions选项)Microsoft.AspNetCore.Authentication.Cookies.ChunkingCookieManager.AppendResponseCookie(HttpContext上下文、字符串键、字符串值、CookieOptions选项)Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.HandleSignInAsync(ClaimsPrincipal用户,AuthenticationProperties属性)Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext上下文,字符串方案,ClaimsPrincipal主体,AuthenticationProperties属性)Microsoft.AspNetCore.Identity.SignInManager
1.SignInWithClaimsAsync(TUser user, AuthenticationProperties authenticationProperties, IEnumerable
1额外索赔)Microsoft.AspNetCore.Identity.SignInManager1.SignInOrTwoFactorAsync(TUser user, Boolean isPersistent, String loginProvider, Boolean bypassTwoFactor) at Microsoft.AspNetCore.Identity.SignInManager
1.密码SignInAsync(TUseruser,字符串密码,布尔值isPersistent,布尔值lockoutOnFailure)位于Rpg_Agenda.Services.LoginService.LoginServer`1.Login(LoginModellogInInfos)C: \Users\cleme\source\repos\Rpg_Agenda\Rpg_Agenda\Services\LogiService\LogiService.cs:line41在Rpg_Agenda.Pages.Account.Login.LoginCicked()C: \Users\cleme\source\repos\Rpg_Agenda\Rpg_Agenda\Pages\Account\Login.razor:line42 atMicrosoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(任务任务)在MudBrazor.MudBaseButton.OnClickHandler(MouseEventArgs-ev)在Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(任务任务)Microsoft.AspNetCore.Components.RenderTree.RRenderer.GetErrorHandledTask(任务taskToHandle,ComponentState owningComponentState)Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost:错误:电路中未处理的异常'2psGTBryvsuncvK6qlYl3f4w_cpcOsDaescmCRQRRA'。System.InvalidOperationException:标头为只读,响应具有已启动。在Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()在Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseHeaders.Microsoft.AspNetCore.Http.HeaderDictionary.set_SetCookie(字符串值值)在Microsoft.AspNetCore.Http.ResponseCookies.Append(字符串键、字符串值、CookieOptions选项)Microsoft.AspNetCore.Authentication.Cookies.ChunkingCookieManager.AppendResponseCookie(HttpContext上下文、字符串键、字符串值、CookieOptions选项)Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.HandleSignInAsync(ClaimsPrincipal用户,AuthenticationProperties属性)Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext上下文,字符串方案,ClaimsPrincipal主体,AuthenticationProperties属性)Microsoft.AspNetCore.Identity.SignInManager
1.SignInWithClaimsAsync(TUser user, AuthenticationProperties authenticationProperties, IEnumerable
1额外索赔)Microsoft.AspNetCore.Identity.SignInManager1.SignInOrTwoFactorAsync(TUser user, Boolean isPersistent, String loginProvider, Boolean bypassTwoFactor) at Microsoft.AspNetCore.Identity.SignInManager
1.密码SignInAsync(TUseruser,字符串密码,布尔值isPersistent,布尔值lockoutOnFailure)位于Rpg_Agenda.Services.LoginService.LoginServer`1.Login(LoginModellogInInfos)C: \Users\cleme\source\repos\Rpg_Agenda\Rpg_Agenda\Services\LogiService\LogiService.cs:line41在Rpg_Agenda.Pages.Account.Login.LoginCicked()C: \Users\cleme\source\repos\Rpg_Agenda\Rpg_Agenda\Pages\Account\Login.razor:line42 atMicrosoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(任务任务)在MudBrazor.MudBaseButton.OnClickHandler(MouseEventArgs-ev)在Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(任务任务)Microsoft.AspNetCore.Components.RenderTree.RRenderer.GetErrorHandledTask(任务taskToHandle,ComponentState owningComponentState)
在以下行:SignInResult SignInResult=await _signInManager.PasswordSignInAsync(user,logInInfos.Password,logInInfos.RememberMe,lockoutOnFailure:false);
如果我使用用户电子邮件而不是用户对象调用PasswordSignInAsync,我也会遇到同样的错误。
我以前尝试过中间件层Login,它很有效,但我真的不喜欢它在这个用例中,因为每次我需要Invoke时都会调用它。
这是我的代码:
登录服务.cs:
public class LoginService<TUser> where TUser : class
{
private readonly SignInManager<TUser> _signInManager;
private readonly UserManager<TUser> _userManager;
public LoginService(SignInManager<TUser> signInManager, UserManager<TUser> userManager)
{
_signInManager = signInManager;
_userManager = userManager;
}
public async Task<bool> Login(LoginModel logInInfos)
{
TUser user = await _userManager.FindByEmailAsync(logInInfos.Email);
if (user == null)
{
logInInfos.Error = ServiceErrors.LOGIN_WRONG_CREDENTIALS;
return false;
}
if (!await _signInManager.CanSignInAsync(user))
{
logInInfos.Error = ServiceErrors.LOGIN_ACCOUNT_NOT_ACTIVE;
return false;
}
if (!(await _signInManager.CheckPasswordSignInAsync(user, logInInfos.Password, true)).Succeeded)
{
logInInfos.Error = ServiceErrors.LOGIN_WRONG_CREDENTIALS;
return false;
}
SignInResult signInResult = await _signInManager.PasswordSignInAsync(user, logInInfos.Password, logInInfos.RememberMe, lockoutOnFailure: false);
if (signInResult.IsLockedOut)
{
logInInfos.Error = ServiceErrors.LOGIN_LOCKED_OUT;
return false;
}
else if (signInResult.IsNotAllowed)
{
logInInfos.Error = ServiceErrors.LOGIN_NOT_ALLOWED;
return false;
}
else if (!signInResult.Succeeded)
{
logInInfos.Error = ServiceErrors.LOGIN_ERROR;
return false;
}
logInInfos.Password = null;
return true;
}
}
登录剃须刀:
@page "/login"
@using Microsoft.AspNetCore.Identity
@using RpgAgenda.Data.Entities
@using Rpg_Agenda.Shared
@using Rpg_Agenda.Services.LoginService
@using Rpg_Agenda.Services.LoginService.Models
@inject LoginService<User> loginService
@inject NavigationManager NavMgr
<MudGrid Justify="Justify.Center">
<MudItem xs="4">
<MudPaper Class="pa-4" Elevation="3">
<MudForm @ref="form">
<ErrorField errorValue="@loginModel.Error"></ErrorField>
<MudTextField T="string" Label="Email" Required="true" @ref="email" RequiredError="Email is required" />
<MudTextField T="string" InputType="InputType.Password" Label="Password" Required="true" @ref="password" RequiredError="Password is required" />
<div class="d-flex align-center justify-center mt-6">
<MudButton FullWidth="true" OnClick="LoginClicked" Variant="Variant.Filled" Color="Color.Primary">Login</MudButton>
</div>
</MudForm>
</MudPaper>
</MudItem>
</MudGrid>
@code {
private MudTextField<string> email;
private MudTextField<string> password;
private MudForm form;
private LoginModel loginModel = new LoginModel();
private async Task LoginClicked()
{
await form.Validate();
if (form.IsValid)
{
loginModel.Email = email.Value;
loginModel.Password = password.Value;
bool isLoggedIn = await loginService.Login(loginModel);
if(isLoggedIn)
{
NavMgr.NavigateTo("/");
}
}
}
}
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddMudServices();
services.AddDbContext<Rpg_AgendaContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<User>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<Rpg_AgendaContext>();
services.AddScoped<SignInManager<User>>();
services.AddScoped<UserManager<User>>();
services.AddScoped<LoginService<User>>();
services.AddScoped<RegisterService<User>>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
提前感谢你对我的帮助。
你的问题最好用另一个问题来回答——为什么Blazor没有能力任意轻松地登录?为什么它使用MVC风格的会员页面(脚手架页面等?),许多人觉得这很烦人,也很不方便?
我想你会发现html请求并没有按照你想要的方式工作。一旦请求通过中间件,你就无法更改用户的声明。
中间件可以拦截完整的请求,包括发布的密码数据,所以我建议使用自定义URL登录。您可以捕获URL,登录客户端,然后导航到上一页。