Spring Boot rest apis 和 Response statuses 常用实践

Posted

技术标签:

【中文标题】Spring Boot rest apis 和 Response statuses 常用实践【英文标题】:Springboot rest apis and Response statusses common practices 【发布时间】:2019-04-26 20:22:33 【问题描述】:

我正在尝试构建我的第一个基于 spring boot 的 rest api,并且我试图了解在找不到资源的情况下返回 404 的常见做法。

首先,我不知道我是否应该将不查找资源视为“异常事件”,或者只是通常发生的事情,而我的应用程序应该经常处理。

我发现的大多数解决方案都建议使用带注释的异常,告诉处理程序在找不到资源的情况下返回 404。

例如:

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found")
public class ResourceNotFoundException extends RuntimeException 
    public ResourceNotFoundException() 
        super();
    

    public ResourceNotFoundException(String message, Throwable cause) 
        super(message, cause);
    

    public ResourceNotFoundException(String message) 
        super(message);
    

    public ResourceNotFoundException(Throwable cause) 
        super(cause);
    

现在我想知道,考虑到我的代码使用最简单的结构这一事实:

    控制器 服务 存储库

我应该把它扔到哪里?在存储库级别?还是只返回 null 并在控制器级别引发异常?这会更有效还是只是一种不好的做法?

【问题讨论】:

【参考方案1】:

你应该在控制器中抛出你的异常。

【讨论】:

我对这个选择背后的原因更感兴趣。你能解释一下你为什么这么建议吗?如您所见,有不同的意见:)【参考方案2】:

假设您使用的是最新版本的 Spring Boot,因此最佳做法是在 Service 中抛出异常,因为您可以找到实体。

@Service
public class EntityService 

    @Autowired
    private EntityRepository repository;

    public Entity findById (Long id) 
       return repository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException(String.format("Entity not found with id %d", id)));
    

【讨论】:

【参考方案3】:

您返回错误的方式与您的 API 紧密相关,而您的服务代码应该与 API 无关。换句话说,如果您出于某种原因需要在 REST API 旁边添加一个 SOAP API,则服务代码应该能够为这两个 API 提供服务。因此,任何与 API 紧密耦合的东西都应该在实现 API 的层中处理,在本例中是您的控制器层。

在我目前的工作地点,我们检查控制器中是否存在正在操作的资源。由于我们使用 Hibernate,一旦从数据库中检索到实体,它会在会话期间保留在会话缓存中,并且如果您选择不向下传递实体,在服务层中再次检索不会产生额外的成本到服务。

在 SpringBoot 中,org.springframework.data.rest.webmvc.ResourceNotFoundException 绑定到 404 NOT_FOUND。因此,您无需为 API 实现任何类型的异常处理程序即可返回带有 404 http 状态码的响应。

【讨论】:

我真的不明白为什么抛出异常会使我的服务代码与我的 API 紧密耦合。为什么 SOAP API 的处理程序无法处理异常?我认为拥有自己的异常而不是使用 spring 库中包含的异常可以避免这种紧密耦合。你能详细说明一下吗?提前谢谢你 并不是抛出异常会造成紧密耦合...耦合是将异常转化为特定于 API 的东西。对我们来说,这是其中一个考虑因素。我喜欢在控制器层检查请求的一件事是服务方法不需要检查请求参数是否存在,即他们可以认为它们是安全的。这允许我们的服务方法专注于业务逻辑。这些不是非黑即白的选择……在服务层检查是完全有效的。 那么使用相同的异常和特定的处理程序可以解决问题吗?然后我可以在代码的任何级别使用异常。是这个意思吗? 是的,如果你愿意,你可以这样做【参考方案4】:

最佳实践是将异常从服务抛出到控制器,并在 @RestControllerAdvice 类中使用适当的 HttpStatus 处理异常。

例如

从服务类向控制器抛出异常

@Service
public class ResourceServiceImpl implements ResourceService 

    @Override
    public void findById(String id) throws ResourceNotFoundException
        throw new ResourceNotFoundException("your_error_code", "msg");
    


ControllerAdvice 类示例,用于处理异常并使用您的 HTTP 状态和您定义的 ErrorResponse 类对象作为 JSON 发送您的 rest API 的响应。

@RestControllerAdvice
public class ErrorHandler 

    @ExceptionHandler(value =  ResourceNotFoundException.class )
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) 
        logger.error("Handling resource not found exception ", ex);
        return new ErrorResponse(ex.getCode(), ex.getMessage());
     


【讨论】:

我有两个问题:您在示例中使用的 ErrorResponse 是什么?为什么您认为处理程序是必要的,而我使用的带注释的异常却不是?使用这两种方法,我都找不到 404。我可能在这里遗漏了一些东西:) 最佳实践是使用处理程序来处理代码中引发的所有异常,以便它可以向 API 调用发送正确的错误响应。 ErrorResponse 是这里的模型类,用于创建带有 error_code、消息、时间戳等的对象,以作为 JSON 发送来描述 API 响应。详情请查看***.com/questions/28902374/…。【参考方案5】:
ResponseEntityExceptionHandler is the default implementation of Spring for handling of various error .In order to customize the error, override the method.     

 @ControllerAdvice
            @Slf4j
            public class GlobalExceptionHandler extends ResponseEntityExceptionHandler 

         @ExceptionHandler(value =  ResourceNotFoundException.class )
            @ResponseStatus(value = HttpStatus.NOT_FOUND)
            public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) 
                logger.error("Handling resource not found exception ", ex);
                return new ErrorResponse(ex.getCode(), ex.getMessage());
             
            

【讨论】:

以上是关于Spring Boot rest apis 和 Response statuses 常用实践的主要内容,如果未能解决你的问题,请参考以下文章

基于Spring Boot的RESTful API实践

java 使用Rest Assured和Spring Boot来休息Api文档

使用 Spring Boot 和 JWT 保护 REST Api

Spring Boot Rest api 与 Spring Kafka

Spring Boot JWT - 如何实现刷新令牌和注销 REST-API

Spring boot 和 Jira REST API 在 maven 中给出依赖错误