You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为生成式ID实现静态校验?阻止POST/PUT传入ID返回400错误

解决方案:阻止DTO的ID字段在POST/PUT请求体中被传入

针对你提到的场景,推荐两种基于注解的实现方案,无需在控制器中编写重复判断逻辑,适合大规模代码库:

方案一:自定义校验注解(灵活通用)

通过自定义校验注解,专门约束“请求体中不能传入指定字段”的规则,全局生效且易于维护。

1. 定义自定义校验注解

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NotPresentInRequestBodyValidator.class)
public @interface NotPresentInRequestBody {
    String message() default "该字段不允许在请求体中传入";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

2. 实现校验器逻辑

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class NotPresentInRequestBodyValidator implements ConstraintValidator<NotPresentInRequestBody, Object> {
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        // 字段值不为null,说明请求体中传入了该字段,校验不通过
        return value == null;
    }
}

3. 修改DTO并启用校验

将id字段改为包装类型Long(避免基本类型默认值干扰判断),添加自定义注解:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SampleApiDto {
    
    @NotPresentInRequestBody
    private Long id;

    private String description;
    private String columnName;
    private String code;
}

在控制器方法上添加@Valid注解,触发请求体校验:

@RestController
@RequestMapping("/samples")
public class SampleController {

    @PostMapping
    public ResponseEntity<Void> createSample(@Valid @RequestBody SampleApiDto dto) {
        // 处理创建逻辑,此时dto.id为null,后续可自行赋值服务端生成的ID
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

    @PutMapping("/{id}")
    public ResponseEntity<Void> updateSample(@PathVariable Long id, @Valid @RequestBody SampleApiDto dto) {
        // 处理更新逻辑,使用路径变量的id即可,dto.id为null
        return ResponseEntity.ok().build();
    }
}

当请求体中包含id字段时,会自动触发校验失败,返回400 Bad Request,且后续服务端可正常给id赋值并访问。

方案二:分组校验(基于现有注解)

利用Bean Validation的分组功能,仅在POST/PUT请求的校验场景中要求id为null,不影响后续字段访问。

1. 定义校验分组

public interface RequestValidationGroup {}

2. 修改DTO配置分组校验

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SampleApiDto {
    
    @Null(groups = RequestValidationGroup.class, message = "id不允许在请求体中传入")
    private Long id;

    private String description;
    private String columnName;
    private String code;
}

3. 控制器中指定校验分组

@RestController
@RequestMapping("/samples")
public class SampleController {

    @PostMapping
    public ResponseEntity<Void> createSample(@Validated(RequestValidationGroup.class) @RequestBody SampleApiDto dto) {
        // 处理创建逻辑
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

    @PutMapping("/{id}")
    public ResponseEntity<Void> updateSample(@PathVariable Long id, @Validated(RequestValidationGroup.class) @RequestBody SampleApiDto dto) {
        // 处理更新逻辑
        return ResponseEntity.ok().build();
    }
}

关键说明

  • 推荐将long改为Long:基本类型long默认值为0,无法区分“未传入字段”和“传入0”的情况,包装类型Long默认值为null,能准确判断请求体是否传入了id。
  • 两种方案都不会影响后续对id字段的访问:服务端可自行赋值,之后通过dto.getId()正常获取值。

内容的提问来源于stack exchange,提问作者zakpruitt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 08:20:38