我有自定义的ConstraintValidator,它检查@RequestParam的有效值。我也有一些带有@NotNull注释的属性的hibernate实体。
默认行为是将ConstraintViolationException转换为HttpStatus=500。这对于从hibernate(即持久化实体等(引发错误是需要的。但我想为从@RestController引发的ConstraintViolations引发HttpStatus=400。
@ExceptionHandler可以工作,但它无法区分从hibernate层和spring层引发的冲突。我想这两层都使用hibernate验证器依赖关系,但我们应该能够区分它们。
@ExceptionHandler(ConstraintViolationException.class)
protected ResponseEntity<Object> handleConstraintViolationException(ConstraintViolationException ex, WebRequest request) {
return getObjectResponseEntity(ex, request, HttpStatus.BAD_REQUEST);
}
我想知道是否还有其他方法可以实现这一点。我尝试过@InitBinder验证器,但它似乎不适用于@RequestParam。
你有什么想法吗?谢谢
其中一个解决方案是列出给定异常的所有违规行为,并检查根bean是否是像*Controller这样的名称,但这不是一个完全干净的解决方案,并将开发人员绑定到以这种方式命名的控制器。
@ExceptionHandler(ConstraintViolationException.class)
protected ResponseEntity<Object> handleConstraintViolationException(ConstraintViolationException ex, WebRequest request) {
// TODO ugly
boolean isFromController = ex.getConstraintViolations().stream()
.map(cv -> cv.getRootBeanClass().getSimpleName())
.anyMatch(cv -> cv.contains("Controller"));
return getObjectResponseEntity(ex, request, isFromController ? HttpStatus.BAD_REQUEST : HttpStatus.INTERNAL_SERVER_ERROR);
}
带有@SupportedFileTypes自定义验证器的控制器看起来是这样的,该验证器根据内部枚举检查字符串值。
@GetMapping(value = "/{requirementId}/export-cisel", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public CisRadDto exportRequirementNumbers(
@PathVariable UUID requirementId,
@SupportedFileTypes @RequestParam(value = "formatSouboru", required = false) String fileFormat
) {
return pozadavekFacade.exportRequirementNumbers(requirementId, fileFormat != null ? fileFormat : "default");
}
pozadavekFacade只保存创建的具有以下检查的实体:
@NotBlank
@Column(length = 10, nullable = false)
private String idUzivatele;
由于代码分布在不同的层中,您应该能够在每个层中捕获ConstraintViolationException
。
因此,在您的facade中,您可以捕获它并将它们重新列为RuntimeException
(或任何您想要的异常(。然后,您的控制器层会将此异常映射到500内部错误。
在您的控制器层中,保留您的逻辑并将ConstraintViolationException
转换为400错误的请求响应。
更新:您不必在任何地方添加try-catch样板代码。
示例:(不要复制粘贴这个代码。这只是为了演示如何在多个地方摆脱try-catch块(
class Service {
String a() {throw new RuntimeException();}
String b() {throw new RuntimeException();}
String c() {throw new RuntimeException();}
}
class Facade {
Service service;
String a() {return tryRun(() -> service.a());}
String b() {return tryRun(() -> service.b());}
String c() {return tryRun(() -> service.c());}
String tryRun(Supplier<String> run) {
try {
return run.get();
}
catch(ConstraintViolationException e) {
throw new RuntimeException(""); // don't forget to extract useful information from ConstraintViolationEx type.
}
}
}