spring - 如何使用 spring 为 Rest 服务返回带有无效属性列表的错误验证 bean

标签 spring spring-mvc spring-boot hibernate-validator

我有一个用 spring boot 开发的 Rest 服务器,我需要返回一个属性格式错误的错误报告(无效日期,无效整数,无效字符串,对象而不是数组,..)

问题是,如果发送的 json 对象中的参数无效,我们必须返回一个包含 {field, errorMsg} 列表的对象

例子

{
  "message": "Invalid request parameters",
  "properties_errors": [
    {
      "property": "mail",
      "cause": "Email format invalid"
    },
    {
      "property": "startDate",
      "cause": "Date format invalid"
    }
  ]
}

在 Controller 中,我用@Valid @RequestBody MyObj obj 注释了要验证的对象,作为参数给定 之后我找到了两个解决方案:

  1. 自定义反序列化器

定义自定义反序列化器 (com.fasterxml.jackson.databind.JsonDeserializer),将 json 对象的字符串或对象值转换为所需类型 实体属性的注释,并使用 @JsonDeserialize (using = MyCustomDeserializer.class) 注释这些属性。

这个解决方案的问题是它在第一个错误时失败(只返回一个属性错误)。 Spring 使用 HttpMessageConverter 将 http 请求主体转换为实体对象,我们案例中的 spring 实现是使用 jackson MappingJackson2HttpMessageConverter Objectmapper,它使用 com.fasterxml.jackson.databind.deser.BeanDeserializer,它在第一次反序列化错误时失败并出现 IOException,之后将被 MessageConverter 封装在 HttpMessageNotReadableException 中

这个方法看起来更简洁,但我不知道我们是否可以重新定义 ObjectMapper 或 BeanDeserializer 以失败并生成包含所有有问题的属性的根异常报告

  1. 实体的 DTO

第二种解决方案是定义要在 Controller (而不是实体)中使用的 DTO 对象,其中所有属性都是 Object 类型,并且在创建这个对象之后(让 spring 和 jackson 创建这个 dto)我们做这些属性的验证(使用特定的 javax.validation.ConstraintValidator 检查类型 Object 的 DTO 属性是否是我们所需类型的 instanceOf)

第二个解决方案给出了期望的最终结果,因为它使用了已经给出了 MethodArgumentNotValidException 的 spring API 验证。但是让具有 Object 类型属性的 DTO 进行验证然后将它们转换为业务层的正确模型是非常丑陋的,它不是通用的,...

是否有更好的解决方案或方法我错过了这样的结果

最佳答案

我没有在您的场景中测试以下内容,但我相信可以使用类似的解决方案。

使用@ControllerAdvice 处理带有@ExceptionHandler(MethodArgumentNotValidException.class) 的异常。获取所有错误结果,然后返回包含验证错误的对象列表。示例如下。

@ControllerAdvice
public class ControllerValidationHandler {
    @Autowired
    private MessageSource msgSource;

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ValidationMessages processValidationError(MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();
        return new ValidationMessages(fieldErrors.stream()
                .map(fieldError -> processFieldError(fieldError))
                .collect(Collectors.toList()));
    }

    private ValidationMessage processFieldError(FieldError error) {
        ValidationMessage message = null;
        if (error != null) {
            Locale currentLocale = LocaleContextHolder.getLocale();
            String msg;
            try {
                msg = msgSource.getMessage(error.getDefaultMessage(), null, currentLocale);
            } catch (Exception e) {
                msg = error.getDefaultMessage();
            }

            message = new ValidationMessage(error.getField(), msg, MessageType.ERROR);
        }
        return message;
    }
}

关于spring - 如何使用 spring 为 Rest 服务返回带有无效属性列表的错误验证 bean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38643894/

相关文章:

java - 在集成测试中覆盖 spring @Configuration

java - 如何在 JSON 输出中获取单引号字符串数组?

java - 绝地武士获取数据 : JedisConnectionFailureException iterating a section of code over long period of time

java - Spring/JSP 和 Servlet - 高级访问控制

angularjs - Spring MVC : How to map server unknown URLs to the main application page?

java - 使用现有数据库 hibernate

java - 如何在 mybatis 和 spring boot 中使用一对一映射?

java - Spring Boot 和 Jackson : Define which fields to serialize on external class

java - Spring Boot集成测试中如何获取JPA上下文?

java - MyBatis:尝试从具有原始返回类型的方法返回 null