【问题标题】:How can I set up my application to ONLY return messages for ResponseStatusException?如何将我的应用程序设置为仅返回 ResponseStatusException 消息?
【发布时间】:2021-04-14 13:57:10
【问题描述】:

Spring Boot默认不返回任何异常的消息,包括ResponseStatusException,也就是说下面关于bar的消息不会返回给客户端:

    @GetMapping("/foo")
    @ResponseBody
    public Foo getFoo(@RequestParam(name = "bar", defaultValue = "0") int bar) {
        if (bar <= 0) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "bar must always be positive");
        }
        return example.getFoo(bar);
    }

这可以通过在application.properties 中设置server.error.include-message=always 来更改,但是这会导致所有异常消息都返回给客户端,包括这个:

    @GetMapping("/baz")
    @ResponseBody
    public Baz getBaz() {
        if (!security.checkSecurity()) {
            throw new RuntimeException("Security breach! Hope no one finds out!");
        }
        return example.getBaz();
    }

我知道这是一个简单的示例,解决方案只是“不要从控制器抛出服务器异常”,但异常实际上可能来自深埋在应用程序中的其他代码,甚至可能是 @ 987654328@ 或其他。

如何让应用程序仅显示来自ResponseStatusException 的消息而不显示其他类型的异常? (我想除了在每个控制器方法中添加 try-catch 子句之外。)

【问题讨论】:

  • 然后定义您的异常,它将按照您的意愿进行处理。今天检查我以前的答案之一stackoverflow.com/a/67090661/7237884
  • @Boug 这类似于 Akif 的回答。在这里你手动生成ResponseEntity...我希望Spring从我的ResponseStatusException生成ResponseEntity,否则我觉得使用ResponseStatusException毫无意义。有没有办法告诉 Spring 在您的 handleXYZ 方法中以默认方式处理 ResponseStatusException

标签: java spring spring-boot


【解决方案1】:

您可以向任何控制器添加额外的 (@ExceptionHandler) 方法,以专门处理同一控制器中的请求处理 (@RequestMapping) 方法引发的异常。此类方法可以:

  • 处理不带 @ResponseStatus 注释的异常(通常是您未编写的预定义异常)
  • 将用户重定向到专用错误视图
  • 构建完全自定义的错误响应

控制器建议允许您使用完全相同的异常处理技术,但将它们应用于整个应用程序,而不仅仅是单个控制器。您可以将它们视为注释驱动的拦截器。

使用@ControllerAdvice 注解的任何类都成为控制器建议,并支持三种类型的方法:

  • 用@ExceptionHandler注解的异常处理方法。
  • 用@ModelAttribute 注释的模型增强方法(用于向模型添加附加数据)。请注意,这些属性不适用于异常处理视图。
  • Binder 初始化方法(用于配置表单处理)注释为 @InitBinder。

解决方案:

@ControllerAdvice
public class RestControllerAdvice {

  @ExceptionHandler(ResponseStatusException.class)
  public ResponseEntity<String> handleStatusException(ResponseStatusException exception) {
    throw exception;
  }

  @ExceptionHandler(Exception.class)
  public ResponseEntity<String> handleException(Exception exception) {
    return new ResponseEntity<>("Exception", HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

【讨论】:

  • 这里是手动生成ResponseEntity...我希望Spring从我的ResponseStatusException生成ResponseEntity,否则我觉得使用ResponseStatusException毫无意义。有没有办法告诉 Spring 在您的 handleStatusException 方法中以默认方式处理 ResponseStatusException
  • 更新答案。是的,只要抛出一个状态异常,你就会有默认行为。
  • 嗯,这行得通,我对此感到非常惊讶,因为我一直认为异常处理程序抛出的异常不会得到处理。但是,有没有办法为默认异常做到这一点?我尝试了throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Unknown error occurred"),但没有成功,原始异常的消息被返回给客户端,而不是“发生未知错误”。 ResponseEntity 的问题是它需要您自己创建错误对象。对于ResponseStatusException,Spring会自动创建5个字段的错误对象
  • PS:我还发现,如果从异常处理程序中抛出任何异常,它将照常处理原始异常。您不必像以前那样重新抛出传入的异常。换句话说,Spring 忽略了异常处理程序抛出的异常,并将最初抛出的异常序列化。但在正常异常的情况下,我希望 Spring 序列化我正在创建的新 ResponseStatusException,而不是原始异常。
  • 我找到了一个可能的解决方案:(new DefaultErrorAttributes()).resolveException(request, response, this, new ResponseStatusException(...)); 但是这会返回一条消息500 INTERNAL_SERVER_ERROR "Unknown error occurred" 而不仅仅是Unknown error occurred
【解决方案2】:

捕获所有异常并在 catch 块中抛出 ResponseStatusException,如下所示:

@GetMapping("/actor/{id}")
public String getActorName(@PathVariable("id") int id) {
    try {
        return actorService.getActor(id);
    } catch (ActorNotFoundException ex) {
        throw new ResponseStatusException(
          HttpStatus.NOT_FOUND, "Actor Not Found", ex);
    }
}

【讨论】:

  • 我认为这就是我在原始帖子中所说的“(我想除了在每个控制器方法中添加 try-catch 子句之外。)”如果可能的话,我宁愿避免这样做对于运行时异常,因为它会导致每个控制器方法都被错误处理代码污染。对于已检查的异常,这很好,因为无论如何您都必须捕获这些异常,并且在控制器方法中使用 throws 子句是不好的做法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-01
  • 2012-06-01
  • 2016-09-22
  • 1970-01-01
  • 2017-01-02
  • 2017-03-12
  • 2012-02-27
相关资源
最近更新 更多