首页 > 解决方案 > Spring WebFlux:如果抛出此异常,如何在网页上显示来自自定义异常类的消息?

问题描述

这是我的模型类的片段Student

@Table
public class Student {

    @Id
    private Long id;

    private String name;

    private String surname;

    private Integer age;

    public Student(String name, String surname, Integer age) {

        validateInput(name, surname, age);

        this.name = name;
        this.surname = surname;
        this.age = age;
    }

    private void validateInput(String name, String surname, Integer age) {

        if (name == null || name.isBlank()) throw new InvalidNewStudentNameException(name);

        if (surname == null || surname.isBlank()) throw new InvalidNewStudentSurnameException(surname);

        if (age == null || age < 16) throw new InvalidNewStudentAgeException(age);
    }
}

因此,如果尝试使用无效数据创建对象,则会引发相应的自定义异常,并且不会创建对象。

一个这样的自定义类 ,InvalidNewStudentAgeException如下所示:

public class InvalidNewStudentAgeException extends RuntimeException {

    public InvalidNewStudentAgeException(Integer age) {

        super("Could not create a student: student's age, " + age + ", should not be empty or less than 16.");
    }
}

我添加新学生的端点StudentController

@GetMapping("/add")
@ResponseBody
public Mono<Long> add(@RequestParam String name, @RequestParam String surname, @RequestParam Integer age) {

    return studentService.add(name, surname, age).map(student -> student.getId());
} 

以及相应的方法StudentService

@Override
public Mono<Student> add(String name, String surname, Integer age) {

    return client.execute("INSERT INTO student (age, name, surname) VALUES(:age, :name, :surname)")
                 .bind("age", age)
                 .bind("name", name)
                 .bind("surname", surname)
                 .as(Student.class).fetch().one();
}

我运行该应用程序并尝试通过以下方式添加无效学生(年龄小于 16 岁的学生):

http://localhost:8080/add/?name=SomeName&surname=SomeSurname&age=14

我确实抛出了我的自定义异常,但是,该消息仅在我的 IDE 中的堆栈跟踪中可见,但在浏览器中不可见。应用程序终止,如果我随后访问 http://localhost:8080/,我会得到 Whitelabel Error Page with Internal Server Error (500),或者如果我添加server.error.whitelabel.enabled=false到我的application.properties则只是没有页面的错误

我尝试了各种方法来处理此异常,以便为用户(浏览器)获取自定义错误消息,但到目前为止没有成功。

请帮助我以正确的方式在控制器中捕获我的自定义异常并以在客户端浏览器中显示有关无效数据的消息的方式处理它。

即使是禁用白标签页面和在项目目录中添加error.html文件的肮脏且明显错误的解决方法src\main\resources\templates也因某种原因不起作用......

这是我尝试执行此操作的一种方法。我改变了我StudentControlleradd()方法是这样的:

@GetMapping("/add")
@ResponseBody
public Mono<Long> add(@RequestParam String name, @RequestParam String surname, @RequestParam Integer age) {
    try {
        return studentService.add(name, surname, age).map(student -> student.getId());
    }
    catch (InvalidNewStudentAgeException invNewStudAgeEx) {
        return Mono.error(invNewStudAgeEx);
    }
    catch (InvalidNewStudentNameException invNewStudNameEx) {
        return Mono.error(invNewStudNameEx);
    }
    catch (InvalidNewStudentSurnameException invNewStudSurnameEx) {
        return Mono.error(invNewStudSurnameEx);
    }
}

我添加了这样的处理程序:

 @ResponseStatus(
      value = HttpStatus.BAD_REQUEST,
      reason = "Invalid student's age: age should not be empty or less than 16.")
    @ExceptionHandler(InvalidNewStudentAgeException.class)
    public void invalidNewStudentAgeHandler() {

    }

    @ResponseStatus(
      value = HttpStatus.BAD_REQUEST,
      reason = "Invalid student's name: the name should not be empty.")
    @ExceptionHandler(InvalidNewStudentNameException.class)
    public void invalidNewStudentNameHandler() {

    }

    @ResponseStatus(
      value = HttpStatus.BAD_REQUEST,
      reason = "Invalid student's surname: the surname should not be empty.")
    @ExceptionHandler(InvalidNewStudentSurnameException.class)
    public void invalidNewStudentSurnameHandler() {

    }

我发现如果我将HttpStatus's ENUM 更改为无效年龄处理程序,然后尝试添加未成年学生,它会根据 ENUM 返回不同的响应代码。所以它确实在处理程序中捕获了异常,但我怎样才能获得不仅仅是像 500 或 404 这样的数字?我可以以某种方式让浏览器显示“Invadin 学生的年龄......”而不仅仅是HTTP ERROR 509???

标签: javaspring-bootwebexceptionspring-webflux

解决方案


由于这个问题,我找到了解决方案:

如何在 HTTP 状态码后添加自定义消息

我的端点的最终add()方法StudentController如下所示:

@GetMapping("/add")
@ResponseBody
public Mono<Long> add(@RequestParam String name, @RequestParam String surname, @RequestParam Integer age) {
    try {
        return studentService.add(name, surname, age).map(student -> student.getId());
    }
    catch (InvalidInputException invalidInputException) {
        return Mono.error(invalidInputException);
    }
}

现在我只有一个处理程序方法StudentController

@ResponseStatus(
  value = HttpStatus.UNPROCESSABLE_ENTITY,
  reason = "Invalid input data for new student.")
@ExceptionHandler(InvalidInputException.class)
public ResponseEntity invalidInputHandler(InvalidInputException e) {
    return ResponseEntity.status(e.getHttpStatus()).body(e.getMessage());
}

我为这样的泛型添加了一个新类InvalidInputException

public class InvalidInputException extends RuntimeException {

    protected HttpStatus httpStatus;

    public InvalidInputException(HttpStatus httpStatus, String message) {

        super(message);

        this.httpStatus = httpStatus;
    }

    public HttpStatus getHttpStatus() {
        return httpStatus;
    }
}

每个无效输入都有一个特定的类。这是一个此类的示例InvalidNewStudentAgeException

public class InvalidNewStudentAgeException extends InvalidInputException {

    public InvalidNewStudentAgeException(HttpStatus httpStatus, Integer age) {
        super(httpStatus, "Could not create a student: student's age, " + age + ", should not be empty or less than 16.");
    }
}

除了特定的异常构造函数的签名之外,其他一切都保持不变。异常仍然像以前一样被抛出,在Student类的构造函数中通过调用那里的私有方法:

public Student(String name, String surname, Integer age) {

    validateInput(name, surname, age);

    this.name = name;
    this.surname = surname;
    this.age = age;
}

private void validateInput(String name, String surname, Integer age) {

    if (name == null || name.isBlank()) throw new InvalidNewStudentNameException(HttpStatus.UNPROCESSABLE_ENTITY, name);

    if (surname == null || surname.isBlank()) throw new InvalidNewStudentSurnameException(HttpStatus.UNPROCESSABLE_ENTITY, surname);

    if (age == null || age < 16) throw new InvalidNewStudentAgeException(HttpStatus.UNPROCESSABLE_ENTITY, age);
}

现在一切都按预期工作。流程如下:

  1. 运行应用程序;
  2. 通过链接添加“无效”学生:http://localhost:8080/add/?name=TestName&surname=TestSurname&age=14
  3. 之后,转到 localhost:8080 并查看来自我的自定义异常构造函数的纯文本消息!

推荐阅读