Spring Boot中如何验证@RequestHeader?请求头缺失与格式错误校验
Hey there! Let's break down your Spring Boot request header validation questions step by step.
在Spring Boot里验证请求头,主要有几种常用方式,你可以根据需求复杂度来选择:
1. 基础必填验证
如果只是要确保请求头必须存在,直接用@RequestHeader的required属性就能搞定:
public ResponseEntity<> update(@RequestHeader(value="last-modified-date", required = true) String lastModDate, ...)
如果请求头缺失,Spring会自动返回400 Bad Request。
2. 结合JSR-380注解做简单格式校验
如果需要验证格式(比如正则匹配),可以用Spring支持的JSR-380注解(比如@Pattern、@NotBlank),前提是你已经引入了spring-boot-starter-validation依赖:
public ResponseEntity<> update( @RequestHeader(value="last-modified-date") @Pattern(regexp = "^[A-Za-z]{3}, \\d{2} [A-Za-z]{3} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT$", message = "日期格式必须为 'Mon, 28 Aug 2017 02:51:09 GMT'") String lastModDate, ...)
这种方式适合简单的格式校验,但如果是日期解析这类需要业务逻辑判断的验证,自定义校验会更靠谱。
3. 自定义校验器(复杂场景)
对于像日期解析这类需要逻辑判断的验证,最好自定义一个校验注解和对应的校验器,代码更清晰灵活——这也是解决问题2的核心思路。
针对你提供的PUT接口,我们需要实现两个校验逻辑:① 请求头不能缺失;② 格式必须符合Mon, 28 Aug 2017 02:51:09 GMT。下面是具体实现步骤:
步骤1:创建自定义校验注解
先定义一个@ValidLastModifiedDate注解,用来标记需要校验的请求头参数:
import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Documented @Constraint(validatedBy = LastModifiedDateValidator.class) @Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ValidLastModifiedDate { String message() default "请求头last-modified-date格式错误,必须为 'Mon, 28 Aug 2017 02:51:09 GMT' 格式,或请求头缺失"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
步骤2:实现校验器逻辑
接下来写对应的校验器LastModifiedDateValidator,负责实际的日期解析和校验:
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; public class LastModifiedDateValidator implements ConstraintValidator<ValidLastModifiedDate, String> { // 用Java 8+的内置格式,刚好匹配需求的GMT日期格式 private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.RFC_1123_DATE_TIME; @Override public boolean isValid(String lastModDate, ConstraintValidatorContext context) { // 先判断参数是否为空(处理请求头缺失的情况) if (lastModDate == null || lastModDate.isBlank()) { return false; } // 尝试解析日期,失败则返回false try { ZonedDateTime.parse(lastModDate, DATE_FORMATTER); return true; } catch (DateTimeParseException e) { return false; } } }
这里用ZonedDateTime和DateTimeFormatter比旧的SimpleDateFormat更安全,也更贴合现代Java开发习惯。
步骤3:在接口中使用自定义注解
修改你的PUT接口,给lastModDate参数加上@ValidLastModifiedDate注解,同时记得在类或者方法上加上@Validated注解——这是Spring触发校验的关键:
import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @RestController @Validated // 必须加这个注解,否则自定义校验不会生效 public class ExampleController { @RequestMapping(value = "/example/{id}", method = RequestMethod.PUT) public ResponseEntity<?> update( @RequestHeader(value="last-modified-date") @ValidLastModifiedDate String lastModDate, HttpServletRequest request, @RequestBody ReBody rebody, @PathVariable("id") int id) throws Exception { // 业务逻辑 return ResponseEntity.ok("更新成功"); } }
步骤4:处理校验异常(可选)
默认情况下,校验失败会返回500 Internal Server Error,你可以自定义全局异常处理器,返回更友好的400 Bad Request和错误信息:
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.HashMap; import java.util.Map; @RestControllerAdvice public class GlobalExceptionHandler { // 处理@Validated触发的校验异常 @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<Map<String, String>> handleConstraintViolationException(ConstraintViolationException ex) { Map<String, String> errors = new HashMap<>(); for (ConstraintViolation<?> violation : ex.getConstraintViolations()) { errors.put("message", violation.getMessage()); } return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); } }
这样当请求头缺失或者格式错误时,会返回类似下面的响应:
{ "message": "请求头last-modified-date格式错误,必须为 'Mon, 28 Aug 2017 02:51:09 GMT' 格式,或请求头缺失" }
内容的提问来源于stack exchange,提问作者xiao

