.NET 6 Web API项目如何处理运行时异常,避免生产环境崩溃?
ASP.NET Core 6 Web API 生产环境异常处理方案
针对你的问题,下面是一套能避免应用崩溃、规范异常处理的实操方案:
1. 全局异常中间件兜底,杜绝未处理异常崩溃
全局中间件是最后一道防线,必须放在所有中间件最前面,确保任何未被捕获的异常都会被拦截,不会导致应用直接挂掉。
var builder = WebApplication.CreateBuilder(args); // 注册你的服务(数据库、日志等) var app = builder.Build(); // 全局异常处理中间件,必须放在最前面 app.Use(async (context, next) => { try { await next(); } catch (Exception ex) { // 用ILogger记录完整异常信息(包括堆栈),方便后续排查 var logger = context.RequestServices.GetRequiredService<ILogger<Program>>(); logger.LogError(ex, "未处理的全局异常"); // 生产环境返回通用500响应,绝对不能暴露异常细节 context.Response.StatusCode = StatusCodes.Status500InternalServerError; context.Response.ContentType = "application/json"; await context.Response.WriteAsJsonAsync(new { StatusCode = 500, Message = "服务器内部错误,请稍后重试" }); } }); // 后续中间件按正常顺序添加:UseRouting、UseAuthorization等 app.MapControllers(); app.Run();
2. 针对性搞定类型冲突/转换异常
你遇到的InvalidCastException、FormatException这类运行时异常,要提前拦截,不能等到全局中间件处理:
方案一:用Action过滤器拦截控制器内的类型异常
写一个过滤器专门处理控制器层面的类型相关异常,同时提前校验参数:
public class TypeValidationFilter : IActionFilter, IOrderedFilter { private readonly ILogger<TypeValidationFilter> _logger; public TypeValidationFilter(ILogger<TypeValidationFilter> logger) { _logger = logger; } // 执行动作前验证参数类型 public void OnActionExecuting(ActionExecutingContext context) { foreach (var (paramName, paramValue) in context.ActionArguments) { var paramDescriptor = context.ActionDescriptor.Parameters.First(p => p.Name == paramName); // 检查参数是否匹配预期类型,避免后续转换报错 if (paramValue != null && paramValue.GetType() != paramDescriptor.ParameterType) { context.ModelState.AddModelError(paramName, $"参数{paramName}类型错误,预期{paramDescriptor.ParameterType.Name}"); context.Result = new UnprocessableEntityObjectResult(context.ModelState); } } } // 执行动作后捕获类型异常 public void OnActionExecuted(ActionExecutedContext context) { if (context.Exception is InvalidCastException || context.Exception is FormatException) { _logger.LogError(context.Exception, "类型转换异常"); context.Result = new UnprocessableEntityObjectResult(new { StatusCode = 422, Message = "请求参数类型错误,请检查输入格式" }); context.ExceptionHandled = true; // 标记异常已处理,不会再往上抛 } } }
然后在Program.cs注册这个过滤器:
builder.Services.AddControllers(options => { options.Filters.Add<TypeValidationFilter>(); });
方案二:业务代码里做防御性校验
在进行类型转换前,先用安全的方法验证,比如:
// 代替直接Convert.ToInt32,避免抛出FormatException if (!int.TryParse(inputStr, out var intValue)) { throw new FormatException("输入不是有效的整数"); } // 类型转换前先检查类型,避免InvalidCastException if (obj is not UserModel user) { throw new InvalidCastException("对象无法转换为UserModel类型"); }
3. 规范已知异常的处理
把业务场景里的已知异常(NotFound、Unauthorized、参数错误等)封装成自定义异常,统一处理成对应HTTP状态码:
定义自定义异常
public class NotFoundException : Exception { public NotFoundException(string message) : base(message) { } } public class UnauthorizedException : Exception { public UnauthorizedException(string message) : base(message) { } }
在过滤器里捕获自定义异常
扩展之前的TypeValidationFilter,加上自定义异常的处理:
public void OnActionExecuted(ActionExecutedContext context) { // 类型异常处理... if (context.Exception is NotFoundException notFoundEx) { context.Result = new NotFoundObjectResult(new { StatusCode = 404, Message = notFoundEx.Message }); context.ExceptionHandled = true; } else if (context.Exception is UnauthorizedException authEx) { context.Result = new ObjectResult(new { StatusCode = 403, Message = authEx.Message }) { StatusCode = StatusCodes.Status403Forbidden }; context.ExceptionHandled = true; } }
4. 生产环境必做配置
- 关闭开发者异常页:绝对不能在生产环境开启,避免泄露敏感代码信息
if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // 生产环境用全局异常中间件即可 - 完善日志:用ILogger或者Serilog等框架,记录异常的完整上下文(请求路径、参数、堆栈),方便排查问题
- 禁止直接抛出系统级异常:比如
NullReferenceException,这类异常要通过代码判空等方式避免,而不是靠异常捕获
5. 核心原则:避免崩溃的关键
- 所有执行入口(控制器方法、后台任务、定时任务)都要有异常捕获机制,要么局部try-catch,要么交给全局中间件
- 做防御性编程:对所有外部输入、第三方调用、数据库返回都做合法性校验,从源头减少异常
- 测试覆盖边界场景:比如输入非法类型、空值、超出范围的数值,确保异常被正确拦截处理
内容的提问来源于stack exchange,提问作者Shameel Perayil
相关产品推荐
相关产品推荐

