The*_*uck 42 java validation spring list
我有以下控制器方法:
@RequestMapping(value="/map/update", method=RequestMethod.POST, produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntityWrapper updateMapTheme(
HttpServletRequest request,
@RequestBody @Valid List<CompanyTag> categories,
HttpServletResponse response
) throws ResourceNotFoundException, AuthorizationException {
...
}
Run Code Online (Sandbox Code Playgroud)
CompanyTag以这种方式定义:
public class CompanyTag {
@StringUUIDValidation String key;
String value;
String color;
String icon;
Icon iconObj;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
...
}
Run Code Online (Sandbox Code Playgroud)
问题是未触发验证,未验证CompanyTag列表,从不调用"StringUUIDValidation"验证器.
如果我删除了List并且只尝试发送一个CompanyTag,即代替:
@RequestBody @Valid List<CompanyTag> categories,
Run Code Online (Sandbox Code Playgroud)
使用:
@RequestBody @Valid CompanyTag category,
Run Code Online (Sandbox Code Playgroud)
它按预期工作,所以显然Spring不喜欢验证事物列表(尝试使用数组,但也没有用).
任何人都知道缺少什么?
小智 47
我找到了另一种方法.基本问题是您希望将列表作为服务的输入有效负载,但javax.validation不会验证列表,只验证JavaBean.诀窍是使用一个自定义列表类,它既作为List 又作为JavaBean:
@RequestBody @Valid List<CompanyTag> categories
Run Code Online (Sandbox Code Playgroud)
改成:
@RequestBody @Valid ValidList<CompanyTag> categories
Run Code Online (Sandbox Code Playgroud)
您的列表子类看起来像这样:
public class ValidList<E> implements List<E> {
@Valid
private List<E> list;
public ValidList() {
this.list = new ArrayList<E>();
}
public ValidList(List<E> list) {
this.list = list;
}
// Bean-like methods, used by javax.validation but ignored by JSON parsing
public List<E> getList() {
return list;
}
public void setList(List<E> list) {
this.list = list;
}
// List-like methods, used by JSON parsing but ignored by javax.validation
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
// Other list methods ...
}
Run Code Online (Sandbox Code Playgroud)
Leb*_*cca 17
我尝试在项目中使用Paul的方法,但有人说这太复杂了。不久之后,我发现了另一种简单的方法,类似于下面的代码:
@Validated
@RestController
@RequestMapping("/parent")
public class ParentController {
private FatherRepository fatherRepository;
/**
* DI
*/
public ParentController(FatherRepository fatherRepository) {
this.fatherRepository = fatherRepository;
}
@PostMapping("/test")
public void test(@RequestBody @Valid List<Father> fathers) {
}
}
Run Code Online (Sandbox Code Playgroud)
它有效且易于使用。关键点是类上的@Valiated批注。顺便说一句,我使用的是springBootVersion ='2.0.4.RELEASE'。
如评论中所述,我们必须在此处处理异常,例如以下代码:
@RestControllerAdvice
@Component
public class ControllerExceptionHandler {
/**
* handle controller methods parameter validation exceptions
*
* @param exception ex
* @return wrapped result
*/
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public DataContainer handle(ConstraintViolationException exception) {
Set<ConstraintViolation<?>> violations = exception.getConstraintViolations();
StringBuilder builder = new StringBuilder();
for (ConstraintViolation<?> violation : violations) {
builder.append(violation.getMessage());
break;
}
DataContainer container = new DataContainer(CommonCode.PARAMETER_ERROR_CODE, builder.toString());
return container;
}
}
Run Code Online (Sandbox Code Playgroud)
以http状态代码表示网络是可以的,这里仅返回第一个违规消息。您可以更改它以满足定制要求。
在类级别使用@Validated时,将在Spring Boot中通过所谓的方法级验证来验证方法的参数,这不仅适用于控制器,而且适用于IOC容器管理的任何bean 。
顺便说一下,通过以下方式增强了方法级别验证中的方法:
而典型的Spring Boot控制器方法验证则在
两者org.hibernate.validator.internal.engine.ValidatorImpl默认都会导致实际的验证操作,但是它们调用的方法不同,这导致验证逻辑上的差异。
MethodValidationInterceptor调用validateParameters方法ValidatorImplRequestResponseBodyMethodProcessor调用validate方法ValidatorImpl您可以深入了解更多细节。
我建议将List类别包装到一些DTO bean中并验证它.除了工作验证之外,您还将受益于更灵活的API.
@RequestMapping(value="/map/update", method=RequestMethod.POST, produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntityWrapper updateMapTheme(
HttpServletRequest request,
@RequestBody @Valid TagRequest tagRequest,
HttpServletResponse response
) throws ResourceNotFoundException, AuthorizationException {
...
}
public static class TagRequest {
@Valid
List<CompanyTag> categories;
// Gettes setters
}
Run Code Online (Sandbox Code Playgroud)
我执行了以下步骤来对列表进行验证:
@Validated在类级别注释其余控制器List<@Valid MyClass>另外,发现如果验证失败,我会得到 javax.validation.ConstraintViolationException
@Paul Strack的出色解决方案与Lombok魔术混合在一起:
@Data
public class ValidList<E> implements List<E> {
@Valid
@Delegate
private List<E> list = new ArrayList<>();
}
Run Code Online (Sandbox Code Playgroud)
用法(ValidList的交换列表):
public ResponseEntityWrapper updateMapTheme(
@RequestBody @Valid ValidList<CompanyTag> categories, ...)
Run Code Online (Sandbox Code Playgroud)
(需要Lombok,但是如果您还不使用它,那么您真的想尝试一下)
该@Valid注释可以在菱形运算符内部使用:
private List<@Valid MyType> types;
Run Code Online (Sandbox Code Playgroud)
或者
@Valid
private List<MyType> types;
Run Code Online (Sandbox Code Playgroud)
现在,每个列表项都将被验证。
使用Spring Boot 2.4.1:
@Validated给类添加注解
移动@Valid菱形运算符内的注释:
@RestController
@Validated // <-- This activates the Spring Validation AOP interceptor
public class MyController {
...
@RequestBody List<@Valid CompanyTag> categories
// ^^^ - NOTE: the @Valid annotation is inside <> brackets
Run Code Online (Sandbox Code Playgroud)
使用 spring 的更高版本,您现在可以执行此操作。
@RequestMapping(value="/map/update", method=RequestMethod.POST, produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntityWrapper updateMapTheme(
HttpServletRequest request,
@RequestBody List<@Valid CompanyTag> categories,
HttpServletResponse response
) throws ResourceNotFoundException, AuthorizationException {
...
}
Run Code Online (Sandbox Code Playgroud)
@Valid 注释位于通用参数中。
如果您使用自定义 javax 验证注释,请确保将 TYPE_USE 添加到注释目标
@Target({ ElementType.TYPE_USE})
public @interface ValidationAnnotation {.. }
Run Code Online (Sandbox Code Playgroud)
验证集合不能直接进行。
例如:如果多个元素验证失败该怎么办?第一次验证后停止?验证所有(如果是的话,如何处理消息集合)?
如果在您的配置中 Spring 委托给 Bean Validator 提供者(例如 Hibernate Validator),您应该在那里查找实现集合验证器的方法。
对于 Hibernate,这里讨论类似的问题
| 归档时间: |
|
| 查看次数: |
44287 次 |
| 最近记录: |