在生产环境中,最糟糕的错误体验莫过于两类极端:
- 直接暴露未捕获的黄页/堆栈(Stack Trace Leak):将
NullReferenceException原始异常栈直接返回给前端,暴露敏感 SQL 结构与服务器路径,存在巨大安全隐患; - 所有错误一律返回 200 OK +
{ code: -1, msg: "未知错误" }:破坏标准 HTTP 协议语义,导致 CDN、网关与客户端无法有效区分重试条件。
BitzOrcas.Modern 采用“分层错误治理 + RFC 9457 Problem Details 标准响应”:
- 预期业务拒绝:由领域层返回强类型
Result.Failure(Error),由管道自动映射为对应的 4xx 状态码; - 非预期系统崩溃:由 ASP.NET Core
IExceptionHandler全局兜底拦截,屏蔽堆栈并返回标准的 500 结构。
错误治理与异常拦截全景
第一步:领域层与 Handler 统一返回 Result
严禁在业务逻辑中 throw 自定义异常!所有业务分支统一使用强类型 Result:
using System.Threading;using System.Threading.Tasks;using BitzOrcas.Domain.Abstractions;using BitzOrcas.Domain.Results;
public static class OrderErrors{ public static readonly Error NotFound = Error.NotFound("Order.NotFound", "指定的订单不存在。");
public static readonly Error CannotCancel = Error.Conflict("Order.CannotCancel", "已完成或已发货的订单不可取消。");}
public sealed class CancelOrderCommandHandler( ICommandRepository<Order, string> orderRepository){ public async ValueTask<Result> Handle(CancelOrderCommand command, CancellationToken ct) { // 1. 加载聚合根 var orderResult = await orderRepository.FindAsync(command.OrderId, ct); if (orderResult.IsFailure) { // 2. 资源不存在:返回强类型 NotFound 错误对象,绝不抛异常 return Result.Failure(orderResult.Error); }
var order = orderResult.GetValueOrThrow();
// 3. 评估领域取消状态 var cancelResult = order.Cancel(command.Reason); if (cancelResult.IsFailure) { // 4. 业务状态冲突:返回强类型 Conflict 错误对象 return cancelResult; }
await orderRepository.SaveAsync(order, ct); return Result.Success(); }}第二步:全局异常兜底拦截(GlobalExceptionHandler)
对于非预期的数据库宕机或空指针异常,由全局处理器统一收口:
using Microsoft.AspNetCore.Diagnostics;using Microsoft.AspNetCore.Http;using Microsoft.Extensions.Logging;
public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) : IExceptionHandler{ public async ValueTask<bool> TryHandleAsync( HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { var traceId = httpContext.TraceIdentifier;
// 1. 结构化记录致命错误日志(包含完整堆栈供后台排查) logger.LogError(exception, "未捕获的系统未预期异常已拦截,TraceId: {TraceId}", traceId);
// 2. 向客户端安全返回符合 RFC 9457 规范的 500 响应 httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; httpContext.Response.ContentType = "application/problem+json";
var problemDetails = new { type = "https://errors.bitzorcas.corp/internal-error", title = "Internal Server Error", status = 500, detail = "系统发生未预期故障,请联系管理员并提供追踪号。", traceId };
await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken); return true; }}总结
BitzOrcas 的分层错误体系提供了工业级的鲁棒性:
- 无异常流控:业务分支 0 堆栈开销,性能极致;
- 安全防泄密:对外彻底屏蔽物理堆栈与内部数据库路径;
- 标准化响应:严格遵循 RFC 9457 Problem Details 规范。