我从 Spring 引导中收到不充分的错误消息

I'm Getting Inadequate Error Messages from Spring Boot

在我的 Spring 引导应用程序中,我使用 OpenApi 3.0.0 指定了我的 API。当我测试它对错误输入的响应时,我对某些错误消息感到不满意。当 Hibernate 无法处理我的输入时,这些消息很有用。它们包括 class、字段,甚至非法值。但是当 Spring Boot 甚至没有输入我的代码就拒绝我的输入时,我只得到模糊的消息 The request cannot be fulfilled due to bad syntax. 没有关于哪个字段是坏的,或者哪个对象持有坏字段值的信息。

当我在 .yaml 文件中指定我的 DTO 时,需要两个字段:

MenuItemOptionDto:
  type: object
  description: Option for a MenuItem
  properties:
    name:
      type: string
    deltaPrice:
      type: number
      description: Floating point price. Strings are easier to work with.
    id:
      type: integer
      format: int32
  required:
    - name
    - deltaPrice

但假设我提交了一个缺少 deltaPrice 的 DTO,如下所示: {"name": "onions"} 错误消息只是说 The request cannot be fulfilled due to bad syntax. 我希望错误消息说明哪个 DTO 不正确,哪个字段是失踪。

我已经指定了三个相关的应用程序属性。这些中的任何一个都会给我 Hibernate 验证错误消息,但是 none 给我 spring-boot 验证消息:

server.error.include-message=always
server.error.include-binding-errors=always
server.error.include-exception=true

我收到了向我的主应用程序添加验证器 bean 的建议,但没有帮助:

@ComponentScan(basePackages = {"com.myWork.dummy","org.openapitools",})
@EnableCaching
@SpringBootApplication
public class ServerMaster implements CommandLineRunner {
  private static final Logger log = LoggerFactory.getLogger(ServerMaster.class);
  public static void main(String[] args) {
    new SpringApplication(ServerMaster.class).run(args);
  }

  @Override
  public void run(String... arg0) { ... }

  // This was suggested at 
  // in order to give me better error messages when OpenAPI validations are triggered, but it doesn't help.
  @Bean public Validator validator() {
    return new LocalValidatorFactoryBean();
  }
}

当我生成代码时,是否打开 performBeanValidationuseBeanValidation 选项并不重要。生成的代码不会改变。无论哪种方式,@NotNull 注释都应用于 name 和 deltaPrice 字段的 getter,并且这些注释被服务器接受,但没有有用的错误消息。

最后,我正在使用 Spring-Boot 2.3.4,并且我声明了对 Spring 引导注释的依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Spring-Boot 正确地拒绝了输入,因为 OpenAPI 生成器在生成的 MenuItemOptionDTO 的 getter 上放置了 @NotNull 注解,但是由于生成了代码,我无法使用错误消息自定义它们,而且我不想关闭生成器。我怎样才能得到 Spring 或 OpenAPI 来给我更好的错误信息?

测试用例

要查看这些消息的实际效果,请查看 https://github.com/SwingGuy1024/SpringBootDemo.22.05.25

中的代码

默认的SpringBooterror-handler不提供MethodArgumentNotValidException的响应体:

    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return this.handleExceptionInternal(ex, (Object)null, headers, status, request);
    }

好消息:您可以在 GlobalResponseExceptionHandler 中覆盖它 class:

  @Override
  protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
    return handleExceptionInternal(ex, ex.getBindingResult(), headers, status, request);
  }

在上面的代码中,我们简单的return整个binding-result作为响应体。如果你愿意,你可以调整它(例如只包括错误)。

当您使用无效负载调用控制器时,您现在将收到以下响应:

{
  "timestamp": "2022-05-28T19:40:47.295+00:00",
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.web.bind.MethodArgumentNotValidException",
  "message": "Validation failed for object='menuItemOptionDto'. Error count: 1",
  "errors": [
    {
      "codes": [
        "NotNull.menuItemOptionDto.deltaPrice",
        "NotNull.deltaPrice",
        "NotNull.java.math.BigDecimal",
        "NotNull"
      ],
      "arguments": [
        {
          "codes": [
            "menuItemOptionDto.deltaPrice",
            "deltaPrice"
          ],
          "arguments": null,
          "defaultMessage": "deltaPrice",
          "code": "deltaPrice"
        }
      ],
      "defaultMessage": "must not be null",
      "objectName": "menuItemOptionDto",
      "field": "deltaPrice",
      "rejectedValue": null,
      "bindingFailure": false,
      "code": "NotNull"
    }
  ],
  "path": "/demo/admin/menuItem/addOption/1"
}

基于OpenAPI generator to spring-boot with custom java validations

  • You can add some another validation layer in your code, which is independent of OpenAPI generator. This layer will be called from
    PetsController and PetsController will validate only basic OpenAPI
    known constraints.

  • You can add you validations not via annotations, but via xml config as shown here.

  • maybe something else.

  • Hack it a bit. I was looking for a solution in which my custom validation will be defined in OpenAPI spec same way as “required”.
    Naturally I decided not to use solutions 1 or 2 (even thought it
    might be the right way for a lot of cases). I found out the
    openapi-generator actually provides a way of modifying the way the
    code is generated. That means that I can actually define custom
    constraint in OpenAPI specs as my own made up properties.

请按照上述link中的说明执行最后一个方法。