【问题标题】:ConstraintViolationException - distinguish between spring a hibernate for HttpStatusConstraintViolationException - 区分 HttpStatus 的 spring 休眠
【发布时间】:2021-06-02 07:38:20
【问题描述】:

我有自定义 ConstraintValidator 来检查 @RequestParam 的有效值。 我也有一些带有 @NotNull 注释的属性的休眠实体。

默认行为是将 ConstraintViolationException 转换为 HttpStatus=500。 这是从休眠状态引发的错误(即持久实体等)所需要的。 但我想为 @RestController 提出的 ConstraintViolations 提出 HttpStatus=400。

@ExceptionHandler 有效,但它无法区分从休眠和弹簧层引发的违规行为。我猜这两个层都使用了休眠验证器依赖,但我们应该能够区分它们。

 @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;

【问题讨论】:

  • 中间有层吗?还是您的控制器直接从用户输入构建实体?也许您可以在问题中添加更多代码 sn-p
  • 其实是的。层数很少 - 使用服务的外观类。我现在要用一些代码 sn-ps 来编辑问题。

标签: java spring validation


【解决方案1】:

由于您的代码分布在不同的层中,您应该能够在每个层中捕获ConstraintViolationException

因此,在您的外观中,您可以捕获它并将它们重新抛出为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.
        }
    }

}

【讨论】:

  • 我试图避免这种情况,因为它会到处都是 try/catch。
  • 我更喜欢一些技术性的全球解决方案(很多代码要为我更改),但如果我没有找到其他建议,我会使用你的建议。谢谢。
猜你喜欢
  • 2013-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-15
  • 1970-01-01
  • 1970-01-01
  • 2018-09-28
  • 1970-01-01
相关资源
最近更新 更多