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

RESTful API异常捕获位置:Service层还是Controller层?

RESTful API异常捕获:Service层还是Controller层?

这不是主观选择,而是要根据异常类型和职责划分来决定:

一、业务类异常:Service层抛出,Controller/全局过滤器处理

Service层是业务逻辑的核心,最清楚业务规则(比如用户是否存在、参数是否符合业务要求)。这类异常应该由Service层主动抛出自定义业务异常,而非在Service层捕获处理——因为Service层的职责是实现业务逻辑,不是处理HTTP响应。

比如修改你的Service层代码,加入业务校验:

public async Task<User> UpdateAsync(User user, int id)
{
    var existingUser = await _repository.GetByIdAsync(id);
    if (existingUser == null)
    {
        // 抛出自定义业务异常,明确业务错误类型
        throw new UserNotFoundException($"用户ID {id} 不存在");
    }
    // 其他业务规则校验,比如用户权限、参数合法性
    if (user.Age < 18)
    {
        throw new InvalidUserInputException("用户年龄不能小于18岁");
    }
    
    await _repository.UpdateAsync(user, id);
    return user;
}

二、通用/系统类异常:Controller层或全局过滤器统一捕获

像数据库连接失败、未知异常这类和业务无关的系统异常,不需要在每个Service或Controller方法里单独捕获。推荐用全局异常过滤器统一处理,这样能保证所有API的错误返回格式一致,也避免重复代码。

比如用全局过滤器替代Controller里的try-catch:

public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly ILogger<GlobalExceptionFilter> _logger;

    public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
    {
        _logger = logger;
    }

    public void OnException(ExceptionContext context)
    {
        // 统一记录异常日志
        _logger.LogError(context.Exception, "API请求发生未处理异常");

        // 根据异常类型返回对应HTTP状态码
        var (statusCode, message) = context.Exception switch
        {
            UserNotFoundException => (StatusCodes.Status404NotFound, context.Exception.Message),
            InvalidUserInputException => (StatusCodes.Status400BadRequest, context.Exception.Message),
            _ => (StatusCodes.Status500InternalServerError, "服务器内部错误")
        };

        context.Result = new ObjectResult(new 
        { 
            Code = statusCode,
            Message = message
        })
        {
            StatusCode = statusCode
        };
        context.ExceptionHandled = true;
    }
}

此时Controller层可以简化,不用写try-catch:

[HttpPut]
[Route("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[ProducesResponseType(400)]
public async Task<ActionResult<UserDto>> UpdateUserInfo(int id, UserDto userRequest)
{
    var user = _userMapper.ConvertToEntity(userRequest);
    var updatedUser = await _userService.UpdateAsync(user, id);
    var result = _userMapper.ConvertToUserDto(updatedUser);

    return Ok(result);
}

总结

  • Service层:负责根据业务规则抛出具体的自定义异常,不处理HTTP响应相关逻辑。
  • Controller/全局过滤器:负责统一捕获所有异常,转换成符合REST规范的HTTP响应,同时统一记录日志。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 00:35:26