如何验证 Spring Boot Rest 响应?
How to validate Spring Boot Rest response?
我已经使用 Spring 启动 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
值仍然通过。请求验证效果很好。
如何验证响应正文?
您不应该将其注释为以下代码段吗?
public @ResponseBody ExampleResponse getExample(@NotNull @PathVariable("id") String id) {
return exampleService.getExample(id);
}
您可以添加“@Validated @ResponseBody”注解
public @Validated @ResponseBody getExample(@NotNull @PathVariable("id") String id) {
你希望发生什么?我认为您应该考虑的事情很少。
如果一个对象真的不能有值为 null
的字段,您应该在将对象保存到您的存储库(您喜欢的存储库)时验证这一点。然后,如果您的服务 return 是什么,您就知道它已经有效,如果它 return 什么都不是;您可以 return 为客户端 (4xx/5xx) 提供适当的状态代码和消息。您还可以将特定异常映射到特定类型的 status code
,这样您就可以从代码中抛出异常,并让它们被 spring-boot[=15 中的默认异常处理程序捕获和处理=]
如果你的字段可以是null
,但你想在序列化时省略它们,你可以使用jackson annotations
。 @JsonInclude(JsonInclude.Include.NON_NULL)
只会序列化响应中的非空值。
已实现的响应验证器:
@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);
}
}
}
在 Rest Controller 上使用 @Validated
,在必须验证 return 对象的方法上使用 @Valid
。例如:
休息控制器:
@RestController
@RequestMapping("/tasks")
@Validated
public class TaskController {
@GetMapping("/{taskId}")
@Valid
public TaskDTO getTask(@PathVariable UUID taskId) {
return convertToDto(taskService.findById(taskId));
}
}
DTO class:
@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;
}
我的 return 对象 TaskDTO
有空的 dueDate
和 repeatUntil
。所以错误信息将如下所示:
{
"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
}
希望对您有所帮助。有关自定义 class 级别约束的详细信息,请查看 this video.
我已经使用 Spring 启动 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
值仍然通过。请求验证效果很好。
如何验证响应正文?
您不应该将其注释为以下代码段吗?
public @ResponseBody ExampleResponse getExample(@NotNull @PathVariable("id") String id) {
return exampleService.getExample(id);
}
您可以添加“@Validated @ResponseBody”注解
public @Validated @ResponseBody getExample(@NotNull @PathVariable("id") String id) {
你希望发生什么?我认为您应该考虑的事情很少。
如果一个对象真的不能有值为
null
的字段,您应该在将对象保存到您的存储库(您喜欢的存储库)时验证这一点。然后,如果您的服务 return 是什么,您就知道它已经有效,如果它 return 什么都不是;您可以 return 为客户端 (4xx/5xx) 提供适当的状态代码和消息。您还可以将特定异常映射到特定类型的status code
,这样您就可以从代码中抛出异常,并让它们被 spring-boot[=15 中的默认异常处理程序捕获和处理=]如果你的字段可以是
null
,但你想在序列化时省略它们,你可以使用jackson annotations
。@JsonInclude(JsonInclude.Include.NON_NULL)
只会序列化响应中的非空值。
已实现的响应验证器:
@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);
}
}
}
在 Rest Controller 上使用 @Validated
,在必须验证 return 对象的方法上使用 @Valid
。例如:
休息控制器:
@RestController
@RequestMapping("/tasks")
@Validated
public class TaskController {
@GetMapping("/{taskId}")
@Valid
public TaskDTO getTask(@PathVariable UUID taskId) {
return convertToDto(taskService.findById(taskId));
}
}
DTO class:
@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;
}
我的 return 对象 TaskDTO
有空的 dueDate
和 repeatUntil
。所以错误信息将如下所示:
{
"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
}
希望对您有所帮助。有关自定义 class 级别约束的详细信息,请查看 this video.