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

Spring MVC Validation如何实现仅必填用户名或手机号两个字段其中一个

实现方案

有两种常用实现方式,可根据场景选择:

方案1:直接在Controller方法内加校验(快速实现,适合单个接口使用)

首先修正现有代码的隐藏问题:你将user-name标注为@PathVariable,但/users路径没有对应的{user-name}占位符,会直接启动报错,先统一调整为请求参数。
修改后代码如下:

@GetMapping("/users")
@Operation(summary = "API to return users based on username or phone number")
public ResponseEntity<List<UserResponseObject>> usersByNameOrNumber(
        @RequestParam(name = "phone-number", required = false) @Pattern(regexp = "^(\\d{5}|\\d{9})$") String userPhoneNumber,
        @RequestParam(name = "user-name", required = false) @Pattern(regexp = "^[a-zA-Z ]*$") String userName) {

    // 新增互斥校验逻辑
    boolean isPhoneEmpty = userPhoneNumber == null || userPhoneNumber.isBlank();
    boolean isNameEmpty = userName == null || userName.isBlank();
    if (isPhoneEmpty && isNameEmpty) {
        return ResponseEntity.badRequest().body(null); // 可自定义返回错误提示
    }
    if (!isPhoneEmpty && !isNameEmpty) {
        return ResponseEntity.badRequest().body(null); // 可自定义返回错误提示
    }

    try {
            // 原有业务逻辑
    } catch (Exception e) {
            // 原有异常处理
    }
}

注意:单个请求参数上的@Pattern校验要生效,需要在Controller类上添加@Validated注解。

方案2:自定义校验注解(规范实现,适合多接口复用同类校验规则)

如果多个接口都有类似参数互斥需求,可通过JSR-380自定义校验注解实现,逻辑和业务代码解耦:

步骤1:定义自定义校验注解

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

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MutexParamValidator.class)
public @interface MutexParam {
    String message() default "仅允许传入互斥参数中的一个";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    // 指定互斥的两个字段名
    String field1();
    String field2();
}

步骤2:实现校验器逻辑

import org.springframework.beans.BeanWrapperImpl;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class MutexParamValidator implements ConstraintValidator<MutexParam, Object> {

    private String field1;
    private String field2;

    @Override
    public void initialize(MutexParam constraintAnnotation) {
        this.field1 = constraintAnnotation.field1();
        this.field2 = constraintAnnotation.field2();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        Object fieldValue1 = new BeanWrapperImpl(value).getPropertyValue(field1);
        Object fieldValue2 = new BeanWrapperImpl(value).getPropertyValue(field2);
        
        boolean field1Empty = fieldValue1 == null || (fieldValue1 instanceof String && ((String) fieldValue1).isBlank());
        boolean field2Empty = fieldValue2 == null || (fieldValue2 instanceof String && ((String) fieldValue2).isBlank());
        
        // 仅当其中一个非空时校验通过
        return field1Empty ^ field2Empty;
    }
}

步骤3:封装请求参数DTO

import javax.validation.constraints.Pattern;

@MutexParam(field1 = "userPhoneNumber", field2 = "userName", message = "必须且仅能传入手机号或用户名其中一个参数")
public class UserQueryDTO {

    @Pattern(regexp = "^(\\d{5}|\\d{9})$", message = "手机号仅允许5位或9位数字")
    private String userPhoneNumber;

    @Pattern(regexp = "^[a-zA-Z ]*$", message = "用户名仅允许英文字母和空格")
    private String userName;

    // 省略getter、setter
}

步骤4:修改Controller代码

@GetMapping("/users")
@Operation(summary = "API to return users based on username or phone number")
public ResponseEntity<List<UserResponseObject>> usersByNameOrNumber(@Valid UserQueryDTO queryDTO) {
    try {
            // 原有业务逻辑,从queryDTO中取参数即可
    } catch (Exception e) {
            // 原有异常处理
    }
}

校验失败时会自动抛出MethodArgumentNotValidException,可通过全局异常拦截统一返回错误提示即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 02:54:05