【问题标题】:How to validate Spring Boot Rest response?如何验证 Spring Boot Rest 响应?
【发布时间】:2018-02-23 03:11:16
【问题描述】:

我用 Spring Boot Rest 实现了控制器:

@RestController
@RequestMapping("/example")
public class ExampleController {

    @Autowired
    private ExampleService exampleService;

    @GetMapping("/{id}")
    public ExampleResponse getExample(@NotNull @PathVariable("id") String id) {
        return exampleService.getExample(id);
    }
}

并响应 DTO:

public class ExampleResponse {

    @NotNull
    private String id;

    @NotNull
    private String otherStuff;

    // setters and getters
}

响应正文未经过验证。我已经用@Valid 对其进行了注释,但null 值仍然通过。请求验证效果很好。

如何验证响应正文?

【问题讨论】:

  • 可以分享服务逻辑吗?你是如何在 exampleService 中返回的?
  • 这是我第一次听说“响应验证”,通常你只是验证请求。你确定有这样的事情吗?
  • 我需要检查是否填写了所有必需的值。我可以使用“@Autowired Validator 验证器”或在服务中明确检查它们,但如果有其他方法可以做到这一点,我会感兴趣。
  • 好吧,撇开您这样做的原因,您可以在返回之前手动验证响应,例如以下链接应该会有所帮助:stackoverflow.com/questions/19190592/…
  • 你想达到什么目的?是否应省略空字段,控制器是否应返回不同的响应(4xx/5xx)?如果您应该返回不同的响应,请使用手动验证对象,如果不应序列化空字段,请使用杰克逊注解省略空字段

标签: java spring validation spring-boot spring-rest


【解决方案1】:

在 Rest Controller 上使用 @Validated,在必须验证返回对象的方法上使用 @Valid。例如:

RestController:

@RestController
@RequestMapping("/tasks")
@Validated
public class TaskController {

    @GetMapping("/{taskId}")
    @Valid
    public TaskDTO getTask(@PathVariable UUID taskId) {
        return convertToDto(taskService.findById(taskId));
    }  

}

DTO 类:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ValidTaskDTO
public class TaskDTO {

    @FutureOrPresent
    @NotNull
    private ZonedDateTime dueDate;

    @NotBlank(message = "Title cannot be null or blank")
    private String title;

    private String description;

    @NotNull
    private RecurrenceType recurrenceType;

    @Future
    @NotNull
    private ZonedDateTime repeatUntil;

}

我的返回对象TaskDTO 有空dueDaterepeatUntil。所以报错信息如下图:

{
  "timestamp": "2021-01-20T11:09:37.303929",
  "status": 400,
  "error": "Bad Request",
  "message": "getTask.<return value>.dueDate: must not be null, getTask.<return value>.repeatUntil: must not be null",
  "path": null
}

我希望这会有所帮助。有关自定义类级别约束的详细信息,请查看this video

【讨论】:

  • 太棒了——适合我! (我想知道为什么没有人支持这个出色的答案!)
【解决方案2】:

实现的响应验证器:

@Aspect
@Component
public class ControllerResponseValidator {

    Logger logger = Logger.getLogger(ControllerResponseValidator.class);

    @Autowired
    private Validator validator;

    @AfterReturning(pointcut = "execution(* com.example.controller.*.*(..))", returning = "result")
    public void validateResponse(JoinPoint joinPoint, Object result) {
        validateResponse(result);
    }

    private void validateResponse(Object object) {

        Set<ConstraintViolation<Object>> validationResults = validator.validate(object);

        if (validationResults.size() > 0) {

            StringBuffer sb = new StringBuffer();

            for (ConstraintViolation<Object> error : validationResults) {
                sb.append(error.getPropertyPath()).append(" - ").append(error.getMessage()).append("\n");
            }

            String msg = sb.toString();
            logger.error(msg);
            throw new RestException(HttpStatus.INTERNAL_SERVER_ERROR, msg);
        }
    }
}

【讨论】:

    【解决方案3】:

    你期望会发生什么?我认为您应该考虑的事情很少。

    1. 如果一个对象确实不能具有值为null 的字段,则应在将对象保存到您的存储库时验证这一点(您喜欢哪种类型的)。然后,如果您的服务返回某些内容,则您知道它已经有效,如果它没有返回任何内容;您可以为客户端(4xx/5xx)返回正确的状态代码和消息。您还可以将特定异常映射到某种类型的status code,这样您就可以从代码中抛出异常,并让它们被spring-boot中的默认异常处理程序捕获和处理

    2. 如果你的字段可以是null,但是你想在序列化中省略它们,你可以使用jackson annotations@JsonInclude(JsonInclude.Include.NON_NULL) 只会序列化响应中的非空值。

    【讨论】:

      【解决方案4】:

      你不应该把它注释为跟随sn-p吗?

       public @ResponseBody ExampleResponse getExample(@NotNull @PathVariable("id") String id) {
              return exampleService.getExample(id);
          }
      

      【讨论】:

        【解决方案5】:

        您可以添加“@Validated @ResponseBody”注解

        public @Validated @ResponseBody getExample(@NotNull @PathVariable("id") String id) {
        

        【讨论】:

          猜你喜欢
          • 2022-02-03
          • 2014-01-09
          • 1970-01-01
          • 1970-01-01
          • 2022-01-10
          • 1970-01-01
          • 2016-12-01
          • 2016-05-01
          • 1970-01-01
          相关资源
          最近更新 更多