【问题标题】:Spring snake case and validation error responseSpring蛇案例和验证错误响应
【发布时间】:2019-01-29 13:06:06
【问题描述】:

我正在使用蛇形案例为我的 API 构建与 Spring Boot 的 json 映射。 spring 允许您在 application.properties 文件中轻松定义:

spring.jackson.property-naming-strategy=SNAKE_CASE

这很好用。但是,如果我添加验证并尝试发布无效的 json 正文,则响应消息包含骆驼案例中的缺失字段。这是我的控制器和传输对象:

import javax.validation.constraints.NotNull;

public class MyObject {
    @NotNull
    String camelCasedField;

    public MyObject() {}

    public String getCamelCasedField() {
        return camelCasedField;
    }

    public void setCamelCasedField(String camelCasedField) {
        this.camelCasedField = camelCasedField;
    }
}

控制器:

import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@RestController
@RequestMapping("/api/test")
public class MyController {    
    @PostMapping
    public void createTenant(@RequestBody @Valid MyObject myObject) {}
}

如果我发布以下 json 正文,一切都很好: _ { "camel_cased_field": "测试" }

如果我发布一个空的 json 正文,我会收到预期的错误,但字段名称是驼峰式大小写而不是蛇形大小写:

{
    "timestamp": "2018-08-23T07:43:18.987+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "NotNull.myObject.camelCasedField",
                "NotNull.camelCasedField",
                "NotNull.java.lang.String",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "myObject.camelCasedField",
                        "camelCasedField"
                    ],
                    "arguments": null,
                    "default_message": "camelCasedField",
                    "code": "camelCasedField"
                }
            ],
            "default_message": "must not be null",
            "object_name": "myObject",
            "field": "camelCasedField", // should be camel_cased_field
            "rejected_value": null,
            "binding_failure": false,
            "code": "NotNull"
        }
    ],
    "message": "Validation failed for object='myObject'. Error count: 1",
    "path": "/api/test"
}

有没有办法为 spring 应用程序的所有端点解决这个问题?

【问题讨论】:

  • 我认为是驼峰式,因为它包含 java 类的 java 文件名,而不是请求中缺少的 json 字段名...
  • 是的,但这正是问题所在。它应该包含 json 字段而不是 java 字段名称。
  • 那么您可能需要一个不同的错误处理程序,也许是您自己的...,但我对此一无所知...
  • 错误响应是你自定义的吗?检查您的应用程序是否使用 jackson ObjectMapper 的新实例进行错误处理。还要检查你是否在使用 webflux。

标签: java spring spring-boot


【解决方案1】:

默认情况下,javax.validation 框架将使用 Bean 字段大小写,这通常是 Java Bean 字段的驼峰式大小写。

但是有一种方法可以改变这种情况。不是一个简单的方法,但有一个方法。

  • 定义RestControllerAdvice 来处理项目中的异常。 (这通常是大多数 Spring 应用程序的一部分)
  • MethodArgumentNotValidExceptionConstraintViolationException 异常添加处理程序。一个用于 POST 正文违规,另一个用于路径/查询字符串验证违规。
  • MethodArgumentNotValidException 暴露了一个BindingResult 字段,其中有一个FieldError 对象列表,每个字段错误对象都有一个名为“field”的字段。您需要使用反射将此字段的值从驼峰式大小写设置为您想要的任何大小写。
  • 同样,ConstraintViolationException 公开了一个名为“propertyPath”的字段,您可以对其进行操作。

请注意,如果您不在 http 400 响应中按原样发出 MethodArgumentNotValidException,则可以避免反射和很多痛苦。通常,人们处理此异常并提取字段名称和消息,然后将这些消息作为列表发出,在 http 400(或 422 :))响应中。如果你这样做了,那么就需要转换字段名称,而不是弄乱FieldError 对象中的私有值。

这是一个示例:

@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ExceptionResolver {

  @ExceptionHandler(MethodArgumentNotValidException.class)
  @ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY)
  public ErrorResponse handleMethodArgumentNotValidException(HttpServletRequest request,
                 MethodArgumentNotValidException ex) {

    final List<String> errorMessages = new ArrayList<>();
    final BindingResult bindingResult = ex.getBindingResult();

    if (bindingResult.hasErrors()) {

      final List<FieldError> fieldErrorList = bindingResult.getFieldErrors();

      for (FieldError fieldError : fieldErrorList) {
        String kebabFieldName = YOUR_METHOD_TO_CONVERT_CAMEL_TO_KEBAB(fieldError.getField());
        // use kebabFieldName and message to construct your response.
      }
    }
  }
}

如果你使用 google-guava helper jar,YOUR_METHOD_TO_CONVERT_CAMEL_TO_KEBAB 的一个例子就是这个。

String kebab = com.google.common.base.CaseFormat.LOWER_CAMEL
                    .to(CaseFormat.LOWER_UNDERSCORE, fieldError.getField());

如果您找到更简单的方法,请告诉我!干杯。

【讨论】:

    猜你喜欢
    • 2022-02-03
    • 2013-10-06
    • 1970-01-01
    • 2022-01-05
    • 2020-09-04
    • 2021-08-31
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多