当前位置: 代码网 > it编程>编程语言>Java > SpringBoot中注解参数校验的实战指南

SpringBoot中注解参数校验的实战指南

2026年01月21日 Java 我要评论
一、参数校验:程序员的“防杠精神器”假如你的api像个热情的饭店服务员,用户说“随便来点吃的”,你就真给他上了盘空气——这可不妙

一、参数校验:程序员的“防杠精神器”

假如你的api像个热情的饭店服务员,用户说“随便来点吃的”,你就真给他上了盘空气——这可不妙!参数校验就像是那个会耐心问“要辣的还是不辣的?要牛肉还是鸡肉?”的细心服务员,确保不闹出“我要咖啡你却给我上了杯洗脚水”的尴尬。

springboot的注解校验就像给你的方法参数请了个私人保镖,专门拦截那些不靠谱的输入。没有它?用户传个null过来,你的程序可能就会表演“当场崩溃”的绝活。

二、详细步骤:给代码戴上“紧箍咒”

第1步:先来点“开胃菜”——添加依赖

<!-- pom.xml里加入这个,就像泡面加卤蛋,标配! -->
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-validation</artifactid>
</dependency>

第2步:创建个“相亲简历”dto类

import javax.validation.constraints.*;
import java.util.date;
import java.util.list;

/**
 * 用户注册dto - 比相亲网站的个人资料要求还严格
 */
public class userregisterdto {
    
    @notblank(message = "用户名不能为空,难道您是无名氏?")
    @size(min = 2, max = 20, message = "用户名长度在2-20之间,太短没存在感,太长记不住")
    private string username;
    
    @email(message = "邮箱格式不对,这可不是在写情书,随便写写就行")
    private string email;
    
    @pattern(regexp = "^(?=.*[a-za-z])(?=.*\\d)[a-za-z\\d]{8,}$", 
             message = "密码至少8位,包含字母和数字,别再用123456了!")
    private string password;
    
    @min(value = 18, message = "未满18岁?小朋友先去写作业")
    @max(value = 120, message = "超过120岁?您是老神仙吧")
    private integer age;
    
    @notnull(message = "手机号必须填,不然外卖到了找谁?")
    private string phone;
    
    @asserttrue(message = "必须接受协议,虽然可能没人看")
    private boolean acceptedagreement;
    
    @future(message = "预约时间必须是未来,时光机还没发明呢")
    private date appointmenttime;
    
    @size(min = 1, max = 3, message = "最多选3个爱好,您是想成为全能超人吗?")
    private list<string> hobbies;
    
    // 此处省略getter和setter,但它们确实存在,我发誓!
    // 用lombok的@data也行,但今天咱们保持纯洁的java关系
    
    // 自定义校验注解示例
    @validgender
    private string gender;
}

第3步:自定义校验注解——打造专属“安检仪”

import javax.validation.constraint;
import javax.validation.payload;
import java.lang.annotation.*;

/**
 * 性别校验注解 - 咱们思想很开放,但数据要规范
 */
@target({elementtype.field})
@retention(retentionpolicy.runtime)
@constraint(validatedby = gendervalidator.class)
public @interface validgender {
    string message() default "性别必须是男、女或保密,您这是来自火星吗?";
    class<?>[] groups() default {};
    class<? extends payload>[] payload() default {};
}

/**
 * 性别校验器 - 严肃的判官
 */
public class gendervalidator implements constraintvalidator<validgender, string> {
    private static final set<string> valid_genders = 
        new hashset<>(arrays.aslist("男", "女", "保密"));
    
    @override
    public boolean isvalid(string value, constraintvalidatorcontext context) {
        if (value == null) {
            return true; // 用@notnull管非空,咱们只管格式
        }
        return valid_genders.contains(value);
    }
}

第4步:控制器里使用——给api装上“安检门”

import org.springframework.validation.annotation.validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.valid;
import javax.validation.constraints.notblank;

@restcontroller
@requestmapping("/api/users")
@validated // 这个注解让方法参数校验生效,就像给方法吃了“严格丸”
public class usercontroller {
    
    /**
     * 注册用户 - 参数校验比丈母娘挑女婿还严格
     */
    @postmapping("/register")
    public result register(@requestbody @valid userregisterdto userdto) {
        // 如果参数校验失败,根本走不到这里
        // 就像考试不及格,进不了下一轮面试
        return result.success("注册成功,恭喜通过严格审查!");
    }
    
    /**
     * 方法参数校验 - 连路径变量都不放过
     */
    @getmapping("/{id}")
    public result getuser(
            @pathvariable @min(value = 1, message = "id必须大于0,您这是要找空气用户吗?") long id,
            @requestparam @notblank(message = "令牌不能为空,您这是想蒙混过关?") string token) {
        return result.success("找到了用户id: " + id);
    }
    
    /**
     * 分组校验 - 根据不同场景使用不同规则
     * 就像上班穿正装,在家穿睡衣,场合要分清
     */
    @postmapping("/update")
    public result updateuser(@requestbody @validated(userupdategroup.class) userupdatedto dto) {
        return result.success("更新成功");
    }
}

// 分组接口定义
interface userupdategroup {}
interface usercreategroup {}

第5步:全局异常处理——优雅的“救火队员”

import org.springframework.web.bind.methodargumentnotvalidexception;
import org.springframework.web.bind.annotation.exceptionhandler;
import org.springframework.web.bind.annotation.restcontrolleradvice;
import org.springframework.validation.fielderror;

/**
 * 全局异常处理器 - 专业收拾校验失败的烂摊子
 */
@restcontrolleradvice
public class globalexceptionhandler {
    
    /**
     * 处理参数校验异常 - 把技术语言翻译成人话
     */
    @exceptionhandler(methodargumentnotvalidexception.class)
    public result handlevalidationexception(methodargumentnotvalidexception ex) {
        // 收集所有错误信息,就像收集考试错题
        map<string, string> errors = new hashmap<>();
        ex.getbindingresult().getallerrors().foreach(error -> {
            string fieldname = ((fielderror) error).getfield();
            string errormessage = error.getdefaultmessage();
            errors.put(fieldname, errormessage);
        });
        
        return result.error(400, "参数校验失败", errors)
                    .setmessage("您提交的数据有点小问题,请检查后再试哦~");
    }
    
    /**
     * 处理constraintviolationexception - 方法参数校验失败
     */
    @exceptionhandler(constraintviolationexception.class)
    public result handleconstraintviolationexception(constraintviolationexception ex) {
        list<string> errors = ex.getconstraintviolations().stream()
                .map(violation -> violation.getmessage())
                .collect(collectors.tolist());
        
        return result.error(400, "参数不合法", errors);
    }
}

/**
 * 统一返回结果 - 给前端一个标准的“成绩单”
 */
@data
@allargsconstructor
@noargsconstructor
public class result<t> {
    private integer code;
    private string message;
    private t data;
    private long timestamp = system.currenttimemillis();
    
    public static <t> result<t> success(t data) {
        return new result<>(200, "成功", data);
    }
    
    public static <t> result<t> error(integer code, string message, t data) {
        return new result<>(code, message, data);
    }
    
    public result<t> setmessage(string message) {
        this.message = message;
        return this;
    }
}

第6步:进阶玩法——嵌套校验和集合校验

/**
 * 订单dto - 俄罗斯套娃式的校验
 */
public class orderdto {
    @notnull(message = "订单信息不能为空")
    @valid // 这个注解让嵌套校验生效,就像班主任检查每个学生的作业
    private userdto user;
    
    @valid // 集合也要逐个校验,一个都别想逃
    private list<@valid orderitemdto> items;
    
    @valid
    private addressdto address;
}

/**
 * 地址dto - 精确到门牌号
 */
public class addressdto {
    @notblank(message = "省份不能空,您这是要寄到外太空?")
    private string province;
    
    @notblank(message = "城市不能空")
    private string city;
    
    @size(min = 5, max = 100, message = "详细地址5-100字,说清楚点,快递员会感谢您")
    private string detail;
}

三、测试一下:看看“保镖”工作认不认真

// 测试controller - 专门捣乱看系统反应
@springboottest
@autoconfiguremockmvc
class usercontrollertest {
    
    @autowired
    private mockmvc mockmvc;
    
    @test
    void testregisterwithinvaliddata() throws exception {
        string invaliduserjson = """
        {
            "username": "a",  // 太短了!
            "email": "not-an-email",  // 这不是邮箱
            "password": "123",  // 太弱了
            "age": 10,  // 未成年!
            "phone": null,  // 空值
            "acceptedagreement": false,  // 不同意协议
            "appointmenttime": "2020-01-01",  // 过去的时间
            "hobbies": ["吃饭", "睡觉", "打豆豆", "刷手机", "发呆"]  // 爱好太多
        }
        """;
        
        mockmvc.perform(mockmvcrequestbuilders.post("/api/users/register")
                .contenttype(mediatype.application_json)
                .content(invaliduserjson))
                .andexpect(status().isbadrequest())  // 应该返回400
                .andexpect(jsonpath("$.code").value(400))
                .andexpect(jsonpath("$.data").exists())  // 错误详情
                .anddo(print());  // 打印响应,看看“保镖”怎么怼你
    }
}

四、性能优化小贴士

/**
 * 校验配置 - 让校验既严格又高效
 */
@configuration
public class validationconfig {
    
    @bean
    public validator validator() {
        validatorfactory factory = validation.builddefaultvalidatorfactory();
        validator validator = factory.getvalidator();
        
        // 可以在这里配置一些自定义设置
        // 比如缓存校验器,避免重复创建
        
        return validator;
    }
    
    /**
     * 快速失败模式 - 发现一个错误就立即返回
     * 就像考试发现第一题错了就交卷(不建议真人尝试)
     */
    @bean
    public validator fastfailvalidator() {
        return validation.bydefaultprovider()
                .configure()
                .addproperty("hibernate.validator.fail_fast", "true")
                .buildvalidatorfactory()
                .getvalidator();
    }
}

总结:参数校验的“人生哲理”

为什么需要参数校验?

  • 防止gigo(垃圾进,垃圾出)——输入决定输出质量
  • 安全第一:很多安全漏洞都源于不可信的输入
  • 用户体验:早发现错误,早提示用户,别让用户猜谜

注解校验的优点:

  • 声明式:像贴标签一样简单,告别一堆if-else
  • 集中管理:规则在实体类上一目了然
  • 易于维护:改注解就能改规则,不用翻业务代码
  • 丰富内置:spring提供了几十种注解,总有一款适合你

最佳实践建议:

  • 在dto层做校验,保持业务层纯洁
  • 错误消息要友好,说人话,别甩技术术语
  • 区分必填和非必填字段,别要求用户填宇宙
  • 复杂逻辑用自定义校验器,别硬塞到一个注解里
  • 记得处理异常,给前端统一的错误格式

总结: 参数校验就像给你的代码请了个:

  • 门卫大爷:不合格的一律不让进
  • 语文老师:检查格式对不对,内容全不全
  • 健身教练:严格要求,不容马虎
  • 相声演员:出错时还能用幽默的方式告诉你

严谨的程序员对待输入就像猫奴对待猫主子,既要有爱,也要有规矩! 你的api会因为良好的参数校验而变得更加健壮、安全、用户友好。

以上就是springboot中注解参数校验的实战指南的详细内容,更多关于springboot注解参数校验的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com