ASP.NET Core中Hot Chocolate返回错误至Angular Apollo Client异常排查
问题描述
在ASP.NET Core中使用Hot Chocolate实现GraphQL接口,当检测到邮箱已存在时抛出GraphQLException,期望Angular端通过Apollo Client捕获并显示错误,但实际控制台仅显示内部服务器错误,无法在前端错误回调中获取到自定义错误信息。
后端Mutation代码:
public async Task<UserType?> CreateUser([UseFluentValidation, UseValidator<RegisterInputTypeValidator>()]RegisterInputType model, [Service]IUserRepository userRepository) { var userExist = await userRepository.GetUserByEmail(model.Email); if (userExist != null) { throw new GraphQLException(new List<Error>() { new Error("Email already exists"), }); } AppUser user = new AppUser { Email = model.Email, FullName = model.FullName, UserName = model.Email, AvatarUrl = model.AvatarUrl, PhoneNumber = "" }; var r = await userRepository.CreateUserAsync(user, model.Password); if (r.Succeeded == false) { var err = r.Errors.Select(e => new Error(e.Description)).ToList(); throw new GraphQLException(err); } return new UserType { Email = user.Email, FullName = user.FullName, AvatarUrl = user.AvatarUrl, PhoneNumber = user.PhoneNumber!, FriendIds = user.FriendIds, GroupIds = user.GroupIds }; }
Angular前端代码:
this.apollo .mutate({ mutation: CREATE_USER, variables: { email: this.registerForm.controls['email'].value, fullName: this.registerForm.controls['fullName'].value, password: this.registerForm.controls['password'].value, reEnterPassword: this.registerForm.controls['reEnterPassword'].value, avatarUrl: '', }, }) .subscribe({ next: (result) => { console.log(result); }, error: (err) => { // 无法捕获自定义错误 console.log(err.message); } });
解决方案
1. 配置Hot Chocolate确保错误正确返回(后端)
默认情况下Hot Chocolate会将GraphQLException序列化为GraphQL规范的错误格式,但如果ASP.NET Core的全局错误处理中间件(如UseExceptionHandler)拦截了请求,会返回500状态码而非GraphQL的错误响应。可通过以下方式修复:
方法一:调整中间件顺序
在Program.cs中先注册GraphQL中间件,再注册全局错误处理中间件,避免GraphQL请求被全局错误处理拦截:
var app = builder.Build(); // 优先注册GraphQL中间件 app.MapGraphQL(); // 再注册全局错误处理等其他中间件 app.UseExceptionHandler("/error"); // 其他中间件配置...
方法二:显式配置Hot Chocolate错误处理
注册GraphQL服务时,设置异常处理器确保GraphQLException返回200状态码,保留错误信息:
builder.Services.AddGraphQLServer() .AddQueryType<Query>() .AddMutationType<Mutation>() // 其他类型、验证器等配置 .SetExceptionHandler(async context => { if (context.Exception is GraphQLException) { // 确保GraphQL错误返回200状态码,而非500 context.Response.StatusCode = StatusCodes.Status200OK; } await Task.CompletedTask; });
可选:为错误添加自定义Code
抛出异常时为错误对象添加Code属性,方便前端区分错误类型:
if (userExist != null) { throw new GraphQLException(new List<Error>() { new Error("邮箱已存在") { Code = "EMAIL_DUPLICATE" }, }); }
2. 调整Apollo Client错误处理逻辑(前端)
Apollo Client会将GraphQL规范的错误放在graphQLErrors数组中,而非直接在err.message里。修改错误回调逻辑,读取graphQLErrors获取自定义错误信息:
this.apollo .mutate({ mutation: CREATE_USER, variables: { email: this.registerForm.controls['email'].value, fullName: this.registerForm.controls['fullName'].value, password: this.registerForm.controls['password'].value, reEnterPassword: this.registerForm.controls['reEnterPassword'].value, avatarUrl: '', }, }) .subscribe({ next: (result) => { console.log(result); }, error: (err) => { // 处理GraphQL业务错误 if (err.graphQLErrors && err.graphQLErrors.length > 0) { const errorMessages = err.graphQLErrors.map(e => e.message).join(';'); console.log(errorMessages); // 可将错误信息绑定到页面显示 // this.errorMsg = errorMessages; } // 处理网络层面错误 else if (err.networkError) { console.log('网络请求失败:', err.networkError.message); } } });
验证步骤
- 重启后端服务确保配置生效
- 提交重复邮箱的注册请求,查看浏览器网络面板:GraphQL响应状态码应为200,响应体包含
errors数组 - 前端控制台应打印出自定义错误信息(如"邮箱已存在")
内容的提问来源于stack exchange,提问作者Aiquari
相关产品推荐
相关产品推荐

