You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 8谷歌登录报错:StatusCode无法设置(响应已启动)

ASP.NET Core 8谷歌登录后/signin-google触发InvalidOperationException的问题解决

问题描述

成功通过谷歌登录后,应用重定向至/signin-google路径时触发未处理异常:

System.InvalidOperationException: StatusCode cannot be set because the response has already started. at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowResponseAlreadyStartedException(String value)

注:访问令牌和刷新令牌已正常生成且可正常使用。

AuthController代码

[ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
    private readonly ILogger<AuthController> _logger;
    private readonly SignInManager<MyUser> _signInManager;

    public AuthController(
        ILogger<AuthController> logger,
        SignInManager<MyUser> signInManager)
    {
        _logger = logger;
        _signInManager = signInManager;
    }

    [HttpGet("google")]
    public IActionResult Login()
    {
        var properties = _signInManager.ConfigureExternalAuthenticationProperties(
            GoogleDefaults.AuthenticationScheme, null);
        return Challenge(properties, GoogleDefaults.AuthenticationScheme);
    }
}

Program.cs代码

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddAuthentication(IdentityConstants.BearerScheme)
            .AddGoogle(googleOptions =>
            {
                googleOptions.ClientId = "client";
                googleOptions.ClientSecret = "secret";
            })
            .AddBearerToken(IdentityConstants.BearerScheme);

        builder.Services.AddAuthorizationBuilder();

        builder.Services.AddDbContext<AppDbContext>(x => x.UseInMemoryDatabase("AuthNet8"));

        builder.Services.AddIdentityCore<MyUser>()
            .AddEntityFrameworkStores<AppDbContext>()
            .AddApiEndpoints();

        builder.Services.AddControllers();

        var app = builder.Build();

        app.MapGet("/", (ClaimsPrincipal user) => $"Hello {user.Identity.Name}")
        .RequireAuthorization();

        app.MapControllers();

        app.Run();
    }
}

异常成因

  1. 缺少外部登录回调处理逻辑:谷歌认证完成后默认重定向到/signin-google,ASP.NET Core认证中间件完成认证后,需要后续逻辑生成Bearer令牌并返回响应,但当前代码没有配置对应的处理流程,导致中间件尝试修改已开始发送的响应状态码,触发异常。
  2. 未映射Identity API端点:虽然添加了AddApiEndpoints(),但没有在Program.cs中调用MapIdentityApi<MyUser>(),导致Identity提供的外部登录回调、令牌发放等核心端点未注册,无法处理/signin-google后的认证收尾流程。

修复方案

方案1:使用Identity内置API端点(推荐)

直接利用Identity提供的API端点处理外部登录流程,无需手动编写回调逻辑:

  1. 修改Program.cs,添加MapIdentityApi<MyUser>():
var app = builder.Build();

app.MapGet("/", (ClaimsPrincipal user) => $"Hello {user.Identity.Name}")
    .RequireAuthorization();

// 映射Identity的API端点,自动处理外部登录回调、令牌发放
app.MapIdentityApi<MyUser>();

app.MapControllers();

app.Run();
  1. 修改AuthController的Login方法,指定回调Url为Identity的外部登录端点:
[HttpGet("google")]
public IActionResult Login()
{
    // 回调到Identity内置的外部登录处理端点
    var callbackUrl = Url.Action("Login", "External", null, Request.Scheme);
    var properties = _signInManager.ConfigureExternalAuthenticationProperties(
        GoogleDefaults.AuthenticationScheme, callbackUrl);
    return Challenge(properties, GoogleDefaults.AuthenticationScheme);
}

方案2:手动实现回调处理逻辑

如果需要自定义回调流程,可以手动编写回调方法:

  1. 在AuthController中添加回调处理方法:
[HttpGet("callback")]
public async Task<IActionResult> Callback()
{
    var loginInfo = await _signInManager.GetExternalLoginInfoAsync();
    if (loginInfo == null)
    {
        return BadRequest("无法获取外部登录信息");
    }

    // 尝试登录已有用户
    var signInResult = await _signInManager.ExternalLoginSignInAsync(
        loginInfo.LoginProvider, 
        loginInfo.ProviderKey, 
        isPersistent: false, 
        bypassTwoFactor: true);

    if (signInResult.Succeeded)
    {
        // 生成Bearer令牌
        var user = await _signInManager.UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey);
        var accessToken = await _signInManager.UserManager.GenerateUserTokenAsync(
            user, 
            IdentityConstants.BearerScheme, 
            "Login");
        return Ok(new { AccessToken = accessToken });
    }
    else
    {
        // 创建新用户(如果不存在)
        var email = loginInfo.Principal.FindFirstValue(ClaimTypes.Email);
        var newUser = new MyUser { UserName = email, Email = email };
        var createResult = await _signInManager.UserManager.CreateAsync(newUser);
        
        if (createResult.Succeeded)
        {
            await _signInManager.UserManager.AddLoginAsync(newUser, loginInfo);
            await _signInManager.SignInAsync(newUser, isPersistent: false);
            
            var accessToken = await _signInManager.UserManager.GenerateUserTokenAsync(
                newUser, 
                IdentityConstants.BearerScheme, 
                "Login");
            return Ok(new { AccessToken = accessToken });
        }
        
        return BadRequest(createResult.Errors.Select(e => e.Description));
    }
}
  1. 修改Login方法的回调Url:
[HttpGet("google")]
public IActionResult Login()
{
    var callbackUrl = Url.Action("Callback", "Auth", null, Request.Scheme);
    var properties = _signInManager.ConfigureExternalAuthenticationProperties(
        GoogleDefaults.AuthenticationScheme, callbackUrl);
    return Challenge(properties, GoogleDefaults.AuthenticationScheme);
}

内容的提问来源于stack exchange,提问作者Jack Leprado

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 00:27:18