
本文旨在介绍如何在Spring Boot项目中,利用抽象类和继承机制,结合`javax.validation`框架,实现灵活且可扩展的请求参数验证方案。通过定义抽象的请求参数类,并让具体的请求参数类继承它,我们可以实现公共参数的统一验证,并针对不同的业务场景进行定制化的参数验证。
在实际的Web应用开发中,经常会遇到需要根据不同的业务场景,对请求参数进行不同验证的情况。如果将所有的验证逻辑都写在一个Controller方法中,会导致代码臃肿、难以维护。本文将介绍一种利用抽象类和继承机制,结合Spring Validation框架,实现灵活的请求参数验证方案。
方案概述
该方案的核心思想是:
- 定义一个抽象的请求参数类,包含所有请求中可能出现的公共参数,并使用javax.validation注解对这些参数进行验证。
- 针对不同的业务场景,创建具体的请求参数类,继承自抽象的请求参数类,并添加该场景特有的参数,同样使用javax.validation注解进行验证。
- 在Controller方法中,接收抽象的请求参数类作为参数,Spring会自动根据请求参数,实例化对应的具体请求参数类,并进行验证。
- 通过javax.validation.Validator接口,手动触发验证,并处理验证结果。
具体实现
1. 定义抽象请求参数类
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Data
public abstract class ReportRequestDTO {
@NotEmpty(message = "foo不能为空")
private String foo;
@NotEmpty(message = "bar不能为空")
private String bar;
}在这个例子中,ReportRequestDTO是一个抽象类,包含了两个公共参数foo和bar,并使用@NotEmpty注解,表示这两个参数不能为空。message属性用于自定义验证失败时的错误信息。
2. 定义具体请求参数类
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Data
public class ReportADTO extends ReportRequestDTO {
@NotEmpty(message = "foobar不能为空")
private String foobar;
}ReportADTO继承自ReportRequestDTO,并添加了一个特有的参数foobar,同样使用@NotEmpty注解进行验证。
3. Controller方法
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.ConstraintViolation;
import java.util.Set;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
@RestController
@RequestMapping("/reports")
@Validated
public class ReportController {
@Autowired
private Validator validator;
@GetMapping
@ResponseBody
public ResponseEntity getReport(@RequestParam(value = "category") String category,
@Valid ReportRequestDTO reportRequestDTO) {
if ("A".equals(category)) {
ReportADTO reportADTO = new ReportADTO();
reportADTO.setFoo(reportRequestDTO.getFoo());
reportADTO.setBar(reportRequestDTO.getBar());
// simulate set parameter from request
reportADTO.setFoobar("test");
Set> violations = validator.validate(reportADTO);
if (!violations.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation violation : violations) {
sb.append(violation.getMessage()).append(";");
}
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, sb.toString());
}
return ResponseEntity.ok("Report A generated with: " + reportADTO.toString());
} else {
return ResponseEntity.ok("Other report");
}
}
} 在这个例子中,getReport方法接收一个ReportRequestDTO类型的参数reportRequestDTO。Spring会自动根据请求参数,尝试实例化ReportADTO或其他的ReportRequestDTO子类。
注意: 这里使用 javax.validation.Validator 手动触发验证。首先,需要注入Validator接口。然后,根据不同的category,手动创建对应的DTO对象,并将请求中的公共参数设置到该对象中。最后,调用validator.validate()方法进行验证,并处理验证结果。 如果验证失败,将错误信息封装到ResponseStatusException中,并抛出,Spring会自动处理该异常,并返回相应的错误信息。
4. 全局异常处理
为了统一处理验证失败的异常,可以自定义一个全局异常处理类。
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ResponseStatusException;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResponseStatusException.class)
public ResponseEntity handleValidationException(ResponseStatusException ex) {
return new ResponseEntity<>(ex.getReason(), ex.getStatus());
}
} 总结
通过以上步骤,我们实现了一个灵活且可扩展的请求参数验证方案。该方案的优点包括:
- 代码复用性高:公共参数的验证逻辑只需要定义一次,可以在多个具体的请求参数类中复用。
- 可扩展性强:当需要添加新的业务场景时,只需要创建新的请求参数类,并继承自抽象的请求参数类即可。
- 易于维护:每个请求参数类的验证逻辑都集中在该类中,易于理解和维护。
注意事项:
- 确保Spring Validation框架已正确配置。
- @Valid注解用于触发验证,需要添加到需要验证的参数上。
- javax.validation.Validator接口提供了手动触发验证的功能,可以灵活地控制验证的时机和方式。
- 全局异常处理可以统一处理验证失败的异常,并返回友好的错误信息。
- 在实际应用中,可以根据需要,自定义更多的验证注解,以满足不同的业务需求。










