JavaRush /Java 博客 /Random-ZH /Spring Boot 控制器中的异常处理
Павел
第 11 级

Spring Boot 控制器中的异常处理

已在 Random-ZH 群组中发布
文章内容 再次您好!是时候掸去键盘上的灰尘了。创建一个 spring-boot 项目。从 Maven 依赖项中我们需要:
<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <java.version>1.8</java.version>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.0.RELEASE</version>
    <relativePath/><!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
在进一步阅读之前,创建一个项目结构: Spring Boot 控制器中的异常处理 - 1 BusinessException 和 CustomException:
public class BusinessException extends Exception{
    public BusinessException(String message) {
        super(message);
    }
}

public class CustomException extends Exception{
    public CustomException(String message) {
        super(message);
    }
}
响应类
public class Response {

    private String message;

    public Response() {
    }

    public Response(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
现在,我将用我的耳朵来表演一下,并请 Alexey Kutepov 发言,在他的文章《Spring 控制器中的异常处理》中, 他将告诉我们如何用正确的内容填充这些文件。慢慢阅读,小心地将所有示例复制到您的项目中,在 Postman 中运行和测试。如果在 Alexey 的文章中,以下行对您提出了问题: Produces = APPLICATION_JSON_VALUE,那么知道它与异常处理无关,它表示默认情况下该控制器的所有方法都将返回 JSON。如果有必要,在特定的方法中,可以将这个值重写为另一个MediaType。 如果您已经阅读过,请继续。上面的文章讨论了处理程序的不同选项。其中最灵活的是: @ControllerAdvice - 它允许您在发生错误时更改标准响应的代码和正文。此外,它还允许您在一种方法中同时处理多个异常。但这还不是全部,如果您进一步阅读,您将完全免费获得改进的@ControllerAdvice 。让我们做一些准备工作:我希望响应显示自定义和标准错误消息。为此,我们对Response类进行更改:再添加一个字段
private String debugMessage;
让我们创建一个额外的构造函数:
public Response(String message, String debugMessage) {
    this.message = message;
    this.debugMessage = debugMessage;
}
并且不要忘记为新字段创建 Getter 和 Setter。现在进入正题。让我们编写另一个控制器:
@RestController
public class Example7Controller {
    @GetMapping(value = "/testExtendsControllerAdvice")
    public ResponseEntity<?> testExtendsControllerAdvice(@RequestBody Response response) {
        return  ResponseEntity.ok(response);
    }
}
让我们在邮递员中测试:将 JSON 发送 到http://localhost:8080/testExtendsControllerAdvice
{
    "message": "message"
}
作为响应,我们将收到状态 200 和正文
{
    "message": "message",
    "debugMessage": null
}
现在我们将发送明显不正确的 JSON
{
    11"message": "message"
}
作为响应,我们将收到状态 400(如果您忘记了它的含义,请在 Internet 上查找)和空响应正文。当然,没有人对此感到满意,我们还是斗争吧。之前,我们从头开始创建了@ControllerAdvice,但在 Spring Boot 中有一个模板 - ResponseEntityExceptionHandler。它已经处理了许多异常,例如:NoHandlerFoundExceptionHttpMessageNotReadableExceptionMethodArgumentNotValidException等。这个类处理错误。它有很多方法,其名称基于句柄+异常名称的原则。如果我们想处理一些基本的异常,那么我们继承这个类并重写所需的方法。让我们最终确定默认的咨询类
@ControllerAdvice
public class DefaultAdvice extends ResponseEntityExceptionHandler {//унаследовались от обработчика-заготовки

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<Response> handleException(BusinessException e) {
        Response response = new Response(e.getMessage());
        return new ResponseEntity<>(response, HttpStatus.OK);
    }
//Небольшое отступление: В обработчике выше, обратите внимание на HttpStatus.OK,
//он может быть и HttpStatus.BAD_REQUEST or другим, тут ограничений нет,
//попробуйте поменять статусы и потестить этот обработчик


    @Override//переопределor метод родительского класса
    protected ResponseEntity<Object> handleHttpMessageNotReadable
            (HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Response response = new Response("Не правильный JSON",ex.getMessage());
        return new ResponseEntity<>(response, status);
    }
}
正如您所注意到的,负责HttpMessageNotReadableException 的处理程序已被覆盖。当传入控制器方法的请求正文不可读时(例如,不正确的 JSON),会发生此异常。该异常是由handleHttpMessageNotReadable ()方法造成的。让我们使用不正确的 JSON 再次发出请求:到http://localhost:8080/testExtendsControllerAdvice
{
    11"message": "message"
}
我们收到带有代码 400(错误请求)和正文的响应:
{
    "message": "Не правильный JSON",
    "debugMessage": "JSON parse error: Unexpected character ('1' (code 49)): was expecting double-quote to start field name; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('1' (code 49)): was expecting double-quote to start field name\n at [Source: (PushbackInputStream); line: 2, column: 6]"
}
现在,响应不仅包含正确的代码,还包含包含信息性消息的正文。让我们检查一下它如何处理正确的 JSON 请求:
{
    "message": "message"
}
我们收到了答复:
{
    "message": "message",
    "debugMessage": null
}
老实说,我不喜欢响应包含值为null 的字段,我们现在将快速修复该问题。转到Response类并在必填字段上添加注释
@JsonInclude(JsonInclude.Include.NON_NULL)
private String debugMessage;
我们重新启动项目,再次发出之前的请求,并在响应中得到:
{
    "message": "message"
}
感谢@JsonInclude(JsonInclude.Include.NON_NULL) 注解,只有当我们指定该字段时,该字段才会包含在响应中。@JsonInclude包含在Jackson注释库中,了解它的功能非常有用。这里有两篇文章可供选择: 杰克逊注释。 作者翻译了,但没有完成翻译;谷歌翻译做得很好。 验证 有必要用验证这样的概念来补充这个主题。简单来说,这是检查该对象是否是我们期望的对象。例如:如果在“电话目录”应用程序中我们需要检查数据库中是否存在电话号码,那么在进入数据库之前,检查用户是否输入了字母而不是数字是合乎逻辑的。三篇关于验证的文章,其复杂性不断增加: Spring 中 bean 的验证 在 Spring 框架中设置 DTO 验证 Spring Boot 中的数据验证 我们已经完成了今天的理论。 对于培训,我建议执行以下任务: 您需要实现 NightclubBouncer 应用程序。要求: 1) 应用程序必须接受 JSON 作为输入并写入数据库。JSON 示例:
{
    "name": "Katy Perry"
    “status”:super star”
}
响应正文应包含以下内容:欢迎 +姓名!2)应用程序必须实现以下方法: - 根据id从数据库输出一条记录到客户端(Postman)。- 按字段删除记录:name。3)必须实现从dto层到实体的映射以及返回。4) 如果传入的 JSON 中的status字段不等于:super star,应用程序必须抛出KickInTheAssException错误(需要自己开发) 5) KickInTheAssException错误必须由 ControllerAdvice处理,并且响应正文必须包含消息:“别让我再在这里见到你!响应状态应为 400。 6) 标准错误EntityNotFoundException,例如,如果只有 Katy Perry 来到俱乐部并使用id = 1保存到数据库,并且您调用了“按 id 显示记录”方法并想要显示id = 2的记录,该记录不在数据库中。此错误必须由ResponseEntityExceptionHandler类的重写方法来处理,具体是哪个方法由您自己确定。响应必须具有适当的状态。7) 进行验证:一个简单的选项 - JSON 字段不能为空,或者更困难的是,“名称”字段必须由拉丁字母的两个单词组成,并且它们都必须以大写字母开头。无效值应该抛出异常,以任何方式处理它,打印适当的错误代码和错误消息:No validate。并在不使用 Lombok 库的情况下实现这一切,不要将其包含为项目依赖项 😅
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION