抛出javax.validation.ConstraintViolationException时获取字段名称

时间:2016-04-11 17:12:30

标签: spring bean-validation

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

// hard coding songs.get(0) could lead to issues as well but at least fix the index i
for (int i = 0; i < songs.get(0).size(); i++)
{
    TextView textView = new TextView(this);
    ...
    //Error is thrown here
    textView.setText(songs.get(0).getVerses().get(i);
    ...
 }

7 个答案:

答案 0 :(得分:9)

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

@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()

您可以访问媒体名称&#39; name&#39;与

constraintViolation.getPropertyPath()

答案 1 :(得分:4)

使用此方法(ex是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());


    }

答案 2 :(得分:3)

我有同样的问题,但也得到了#34; sayHi.arg0&#34;来自getPropertyPath。我选择向NotNull注释添加消息,因为它们是我们公共API的一部分。像:

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

您可以致电

获取消息
  

ConstraintViolation#的getMessage()

答案 3 :(得分:2)

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

// 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();
}

答案 4 :(得分:0)

仅获取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());

答案 5 :(得分:0)

支持这样的嵌套字段

class A{int id;@Valid B b;}
class B{@NotNull String a;}

我的代码是

import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ElementKind;
import javax.validation.Path;
import javax.validation.Path.Node;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.ServletWebRequest;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@ControllerAdvice
public class ValidateControllerAdvice
{
    @Autowired
    private MessageSource messageSource;

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public @ResponseBody Map<String, Object> handleConstraintViolation(ConstraintViolationException e,
            ServletWebRequest request)
    {
        final Map<String, Object> result=new LinkedHashMap<>();
        result.put("timestamp", LocalDateTime.now());
        result.put("path", request.getRequest().getRequestURI());
        result.put("status", HttpStatus.BAD_REQUEST.value());
        result.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
        result.put("errors", e.getConstraintViolations().stream()
                .map(cv -> this.getErrorData(cv, request.getLocale())));
        return result;
    }
    
    private ErrorData getErrorData(ConstraintViolation<?> violation, Locale locale)
    {
        String msg =this.messageSource.getMessage(violation.getMessageTemplate(),
                new Object[]{violation.getLeafBean().getClass().getSimpleName(),
                        violation.getPropertyPath().toString(), violation.getInvalidValue()},
                violation.getMessage(), locale);
        Path exceptionPath = violation.getPropertyPath();
        List<Node> path = StreamSupport.stream(exceptionPath.spliterator(), false)
                .collect(Collectors.toList());
        LinkedList<Node> realPath = new LinkedList<>();
        for(int i = path.size() -1; i >= 0; --i)
        {
            if(!path.get(i).getKind().equals(ElementKind.PROPERTY))
            {
                break;
            }
            realPath.addFirst(path.get(i));
        }
        String field = realPath.stream()
                .map(n -> n.toString())
                .collect(Collectors.joining("."));
        String objectName=violation.getLeafBean().getClass().getSimpleName();
        Object rejectedValue=violation.getInvalidValue();
        ErrorData result=new ErrorData(msg, objectName, field, rejectedValue);
        return result;
    }
}




import lombok.Data;
import lombok.AllArgsConstructor;

@Data
@AllArgsConstructor
public class ErrorData
{
    private String message;
    private String objectName;
    private String field;
    private Object rejectedValue;
}

答案 6 :(得分:0)

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

@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());
}