【问题标题】:Get field name when javax.validation.ConstraintViolationException is thrown抛出 javax.validation.ConstraintViolationException 时获取字段名称
【发布时间】:2016-08-01 23:46:39
【问题描述】:

当 PathVariable 'name' 未通过验证时,会抛出 javax.validation.ConstraintViolationException。有没有办法在抛出的 javax.validation.ConstraintViolationException 中检索参数名称?

@RestController
@Validated
public class HelloController {

@RequestMapping("/hi/{name}")
public String sayHi(@Size(max = 10, min = 3, message = "name should    have between 3 and 10 characters") @PathVariable("name") String name) {
  return "Hi " + name;
}

【问题讨论】:

  • 你解决了吗?

标签: spring bean-validation


【解决方案1】:

在ControllerAdvice类中,我们可以处理ConstraintViolationException

import javax.validation.Path;
import javax.validation.Path.Node;
import org.hibernate.validator.internal.engine.path.PathImpl;

....
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<FieldErrorResponse> handleConstraintViolationException(final ConstraintViolationException ex, WebRequest request) {

    List<FieldError> errors = ex.getConstraintViolations().stream()
      .map(violation -> new FieldError(getFieldFromPath(violation.getPropertyPath()), violation.getMessage(), violation.getInvalidValue()))
      .collect(Collectors.toList());
    ....
}

private String getFieldFromPath(Path fieldPath) {

    PathImpl pathImpl = (PathImpl) fieldPath;
    return pathImpl.getLeafNode().toString();

    // OR
    /**
    Iterator<Node> nodes = fieldPath.iterator();
    String fieldName = null;
    while (nodes.hasNext()) {
      fieldName = nodes.next().toString();
    }
    return fieldName;
    */
}

有两种方法可以获取字段名称。首先,一个使用休眠类。如果没有,您可以使用注释方法。

【讨论】:

    【解决方案2】:

    获取字段名称和消息,使用以下代码:

    @ExceptionHandler(value = {ConstraintViolationException.class})
    public ResponseEntity<Object> handleConstraintViolationException(
        ConstraintViolationException ex, WebRequest request) {
        log.error(INVALID_REQUEST, ex);
        Map<String, Object> errors = new HashMap<>();
        if (!ex.getConstraintViolations().isEmpty()) {
            for (ConstraintViolation constraintViolation : ex.getConstraintViolations()) {
                String fieldName = null;
                for (Node node : constraintViolation.getPropertyPath()) {
                    fieldName = node.getName();
                }
                errors.put(fieldName, constraintViolation.getMessage());
            }
        }
        return new ResponseEntity<>(getErrorResponse(code, errors), getHttpCode());
    }
    

    【讨论】:

      【解决方案3】:

      只获取Path最后一部分的参数名称。

      violations.stream()
                      .map(violation -> String.format("%s value '%s' %s", StreamSupport.stream(violation.getPropertyPath().spliterator(), false).reduce((first, second) -> second).orElse(null),
                              violation.getInvalidValue(), violation.getMessage())).collect(Collectors.toList());
      

      【讨论】:

        【解决方案4】:

        如果您检查getPropertyPath() 的返回值,您会发现它是Iterable&lt;Node&gt;,并且迭代器的最后一个元素是字段名称。以下代码适用于我:

        // I only need the first violation
        ConstraintViolation<?> violation = ex.getConstraintViolations().iterator().next();
        // get the last node of the violation
        String field = null;
        for (Node node : violation.getPropertyPath()) {
            field = node.getName();
        }
        

        【讨论】:

        • 恕我直言,这确实是正确的答案。它不会对您不应该使用的类使用任何强制转换并返回所要求的内容。
        • javax.validation.Path.Node
        • 非常感谢,经过数小时的搜索,这解决了我的问题
        【解决方案5】:

        以下异常处理程序展示了它的工作原理:

        @ExceptionHandler(ConstraintViolationException.class)
        
        ResponseEntity<Set<String>> handleConstraintViolation(ConstraintViolationException e) {
            Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        
        Set<String> messages = new HashSet<>(constraintViolations.size());
        messages.addAll(constraintViolations.stream()
                .map(constraintViolation -> String.format("%s value '%s' %s", constraintViolation.getPropertyPath(),
                        constraintViolation.getInvalidValue(), constraintViolation.getMessage()))
                .collect(Collectors.toList()));
        
        return new ResponseEntity<>(messages, HttpStatus.BAD_REQUEST);
        
        }
        

        您可以使用

        访问无效值(名称)
         constraintViolation.getInvalidValue()
        

        您可以使用

        访问属性名称“name”
        constraintViolation.getPropertyPath()
        

        【讨论】:

        • 感谢 Stefan,但我正在寻找参数名称,而不是值。在上面的示例中,我想访问“名称”。
        • 添加了如何获取“名称”
        • 我试过了,但 getPropertyPath 返回 sayHi.arg0
        • getInvalidValue() 不返回成员的名称。它返回成员的值。
        • 嗨,有没有办法获得所需的价值。例如。在最大/最小限制的情况下?
        【解决方案6】:

        使用此方法(例如 ConstraintViolationException 实例):

        Set<ConstraintViolation<?>> set =  ex.getConstraintViolations();
            List<ErrorField> errorFields = new ArrayList<>(set.size());
            ErrorField field = null;
            for (Iterator<ConstraintViolation<?>> iterator = set.iterator();iterator.hasNext(); ) {
                ConstraintViolation<?> next =  iterator.next();
               System.out.println(((PathImpl)next.getPropertyPath())
                        .getLeafNode().getName() + "  " +next.getMessage());
        
        
            }
        

        【讨论】:

          【解决方案7】:

          我遇到了同样的问题,但也从 getPropertyPath 得到了“sayHi.arg0”。我选择向 NotNull 注释添加一条消息,因为它们是我们公共 API 的一部分。喜欢:

           @NotNull(message = "timezone param is mandatory")
          

          你可以通过调用获取消息

          ConstraintViolation#getMessage()

          【讨论】:

            猜你喜欢
            • 2013-02-03
            • 1970-01-01
            • 2021-08-24
            • 1970-01-01
            • 1970-01-01
            • 2021-01-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多