我从Spring Boot获取错误消息不足

发布于 2025-01-31 15:03:41 字数 2505 浏览 3 评论 0 原文

在我的Spring Boot应用程序中,我使用OpenAPI 3.0.0指定了API。当我测试其对不良输入的响应时,我对某些错误消息不满意。当Hibernate无法处理我的输入时,这些消息很有用。它们包括班级,字段,甚至是非法价值。但是,当Spring Boot拒绝我的输入而无需输入我的代码时,我只会获得含糊的消息由于语法不好,因此无法满足请求。没有关于哪个字段不好的信息不良的现场价值。

当我在.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”} 仅此错误消息说<代码>由于不良语法而无法满足请求。我希望错误消息说哪个DTO不正确,并且缺少哪个字段。

我已经指定了三个相关的应用程序属性。其中任何一个都会给我冬眠验证错误消息,但是没有一个给我弹簧启动验证消息:

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 https://stackoverflow.com/questions/49538896/spring-boot-error-message-doesnt-work
  // 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();
  }
}

当我生成代码时,我是否打开 performbeanvalidation useBeanvalidation 选项都没关系。生成的代码不会更改。无论哪种方式, @notnull 注释都应用于名称和deltaprice字段的getters,这些@notnull notnull @notnull 。

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

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

Spring-Boot正确拒绝输入,因为OpenAPI Generator将 @notnull notations注释。生成的 menuiteMoptiondto ,但是由于生成了代码,因此我无法使用错误消息自定义它们,我不想关闭生成器。如何获得Spring或OpenAPI来给我更好的错误消息?

测试案例

要查看这些消息,请在

In my Spring Boot application, I specified my API using OpenApi 3.0.0. When I test its response to bad input, I'm not happy with some of the error messages. The messages are useful when Hibernate can't handle my input. They include the class, field, and even the illegal value. But when Spring Boot rejects my input without even entering my code, I just get the vague message The request cannot be fulfilled due to bad syntax. There's no information about what field is bad, or what object holds the bad field value.

When I specify my DTO in the .yaml file, two fields are required:

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

But suppose I submit a DTO with a missing deltaPrice, like this: {"name": "onions"} The error message just says The request cannot be fulfilled due to bad syntax. I want the error message to say which DTO is incorrect, and which field is missing.

I have specified three relevant application properties. Any one of these will give me Hibernate validation error messages, but none give me spring-boot validation messages:

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

And I've received advise to add a validator bean to my main application, which didn't help:

@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 https://stackoverflow.com/questions/49538896/spring-boot-error-message-doesnt-work
  // 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();
  }
}

When I generate the code, it doesn't matter if I turn on the performBeanValidation or useBeanValidation options. The generated code doesn't change. Either way, the @NotNull annotations are applied to the getters for the name and deltaPrice fields, and these are getting honored by the server, but without useful error messages.

Finally, I'm using Spring-Boot 2.3.4, and I declare a dependency on Spring Boot annotations:

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

Spring-Boot correctly rejects the input because the OpenAPI generator puts @NotNull annotations on the getters of the generated MenuItemOptionDTO, but since the code is generated, I can't customize them with an error message, and I don't want to turn off the generator. How can I get Spring or OpenAPI to give me better error messages?

Test Case

To see these messages in action, check out the code at https://github.com/SwingGuy1024/SpringBootDemo.22.05.25

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

南街女流氓 2025-02-07 15:03:41

默认的Springboot错误处理程序没有为MethodArgumentNotValideXception提供响应主体:

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

好消息:您可以在GlobalResponseExceptionHandler类中覆盖这一点:

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

在上述代码中,我们只是将整个绑定量返回为响应主体。如果需要,可以调整此(例如,仅包括错误)。

当您使用无效有效载荷调用控制器时,您现在会得到以下响应:

{
  "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"
}

The default SpringBoot error-handler does not provide a response body for MethodArgumentNotValidException:

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

The good news: you can override this in your GlobalResponseExceptionHandler class:

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

In the above code, we simply return the entire binding-result as the response body. If you want to, you can tweak this (e.g. only include the errors).

When you call the controller with the invalid payload, you'll now get the following response:

{
  "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"
}
如歌彻婉言 2025-02-07 15:03:41

基于

  • 您可以在代码中添加其他验证层,该验证层与OpenAPI Generator无关。该层将从
    调用
    PetsController和PetsController将仅验证基本的OpenAPI
    已知约束。

  • 您可以不是通过注释添加验证,而是通过XML config如图所示此处

  • 也许还有其他东西。

  • 稍微入侵。我正在寻找一种解决方案,其中我的自定义验证将以OpenAPI规格与“必需”相同的方式定义。
    自然,我决定不使用解决方案1或2(甚至认为它
    可能是很多情况下的正确方法)。我发现
    OpenAPI-Generator实际上提供了一种修改
    的方式
    代码是生成的。这意味着我实际上可以定义自定义
    openapi规格中的约束作为我自己的构成属性。

请按照上述链接中的说明进行执行最后方法。

Based on 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.

Please follow instructions in the above link for implementing last method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文