【SpringBoot】 黑马大事件笔记-day2

目录

用户部分

实体类属性的参数校验

更新用户密码

文章部分

规定josn日期输出格式

分组校验


上期回顾:【SpringBoot】 黑马大事件笔记-day1

用户部分


实体类属性的参数校验

对应的接口文档:

基本信息

请求路径:/user/update

请求方式:PUT

接口描述:该接口用于更新已登录用户的基本信息(除头像和密码)

请求参数

请求参数格式:application/json

请求参数说明:

请求数据样例:

复制代码
{
"id":5,
"username":"wangba",
"nickname":"wb",
"email":"[email protected]"
}

响应数据

响应数据类型:application/json

响应参数说明:

响应数据样例:

复制代码
{
"code": 0,
"message": "操作成功",
"data": null
}

这种其实比较简单,就是底层的增删查改;++明确接口文档的需求:username 不是必传项,而其他属性必须要传。而且传的属性值需要进行校验,确保数据的正确性。++ 比如邮箱的格式需要规范,否则发不了短信找回账号。++其次修改日期需要进行更新。++

Contorller

cs 复制代码
    @RequestMapping("/update")
    public Result update(@RequestBody User user) {
        userService.update(user);
        return Result.success();
    }

Service

我们可以使用LocalDateTime.now() 方法来记录当前系统时间,这样用户信息的更新时间便有了。

cs 复制代码
    @Override
    public void update(Category category) {
        category.setUpdateTime(LocalDateTime.now());
        categoryMapper.update(category);
    }

Mapper

css 复制代码
    <update id="update">
        UPDATE user SET nickname=#{nickname},email=#{email},update_time=#{updateTime} where id=#{id}
    </update>

我们发现参数没有进行校验,容易导致一些错误:用户名有奇怪字符以及邮箱不正确导致发送不了验证码。

所以我们要对参数进行校验:++实体类的成员变量上添加注解++

Pojo

cpp 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class  User {
    @NotNull
    private Integer id;//主键ID
    private String username;//用户名
    @JsonIgnore 
    private String password;//密码
    @NotEmpty
    @Pattern(regexp = "^\\S{1,10}$")
    private String nickname;//昵称
    @NotEmpty
    @Email
    private String email;//邮箱
    private String userPic;//用户头像地址
    private LocalDateTime createTime;//创建时间
    private LocalDateTime updateTime;//更新时间
}

对实体类的属性添加注解,可以起到一定的约束作用:

注解 作用
@NotNull 不能为 null,但可以为 empty,一般用在 Integer 类型的基本数据类型的非空校验上
@NotEmpty 不能为 null,且长度必须大于 0,一般用在集合类上或者数组上
@Email 用于验证字符串是否符合电子邮件的格式,一般应用于 String 类的字段上
@Pattern 被注解的元素必须符合给定的正则表达式,一般用来规定该属性的长度区间

注意: 要使这些注解生效还有一个条件,就是在++控制层传入参数的这里加上 @Validated 注解++

cs 复制代码
    @RequestMapping("/update")
    public Result update(@RequestBody @Validated User user) {
        userService.update(user);
        return Result.success();
    }

这样当我们重新运行项目时,就会抛出校验失败的异常。

更新用户密码

对应的接口文档:

基本信息

请求路径:/user/updatePwd

请求方式:PATCH

接口描述:该接口用于更新已登录用户的密码

请求参数

请求参数格式:application/json

请求参数说明:

请求数据样例:

cs 复制代码
{
"old_pwd":"123456",
"new_pwd":"234567",
"re_pwd":"234567"
}

根据接口文档的说,我们可以知道修改密码需要三个属性:原密码、新密码、确认密码。

所以我们后端需要用 Map 来接收参数;为了保证密码的规范还需要对其进行校验:

|-------------------|
| 密码的长度是否合法,有没有缺少参数 |
| 输入的原密码是否与数据库中的匹配 |
| 新密码与确认密码是否一致 |

只有满足以上条件,密码才能修改成功。

Controller

cs 复制代码
@PatchMapping("/updatePwd")
    public Result updatePwd(@RequestBody Map<String,String> params){
        // 校验参数
        String oldPwd = params.get("old_pwd");
        String newPwd = params.get("new_pwd");
        String rePwd = params.get("re_pwd");

        if(!StringUtils.hasLength(oldPwd) || !StringUtils.hasLength(newPwd) || !StringUtils.hasLength(rePwd)) {
            return Result.error("缺少必要参数");
        }

        // 判断原密码
        Map<String,Object> map =  ThreadLocalUtil.get();
        String username = (String) map.get("username");
        User LoginUser =   userService.findByUserName(username);
        if(!Md5Util.getMD5String(oldPwd).equals(LoginUser.getPassword())) {
            return Result.error("原密码填写不正确");
        }

        // 修改密码和确认密码是否一样
        if(!newPwd.equals(rePwd)) {
            return Result.error("两次填写的密码不一致");
        }

        // 调用Service完成密码更新
        userService.updatePwd(newPwd);
        return Result.success();
    }

由于在数据库中的密码是经过 Md5Util 加密的,所以比较时需要将输入原密码通过 Md5Util 转化后在比较。

Serivce

cs 复制代码
    @Override
    public void updatePwd(String newPwd) {
        Map<String,Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        userMapper.updatePwd(Md5Util.getMD5String(newPwd),id);
    }

由于需要修改密码,首先要获取用户的信息;之前在 ThreadLocal 存放的用户信息 id 此时就就可以直接获取。

Mapper

别忘记每次更新数据库数据都需要更新修改时间。

cs 复制代码
     <update id="updatePwd">
        UPDATE user SET password=#{newPwd},update_time=now() WHERE id=#{id}
    </update>

文章部分


规定josn日期输出格式

@JsonFormat 是在 Jackson 中定义的一个注解,是一个时间格式化注解。此注解用于属性上,作用是把 Date 类型的数据转化成为我们想要的格式。

cpp 复制代码
    @JsonFormat(pattern = "yyyy-mm-dd HH:mm:ss")
    private LocalDateTime createTime;//创建时间
    @JsonFormat(pattern = "yyyy-mm-dd HH:mm:ss")
    private LocalDateTime updateTime;//更新时间

分组校验

需求

我们经常会碰到这样的一个场景:

Controller

cpp 复制代码
    @PostMapping
    public Result add(@RequestBody @Validated Category category) {
        categoryService.add(category);
        return Result.success();
    }


    @PutMapping
    public Result update(@RequestBody @Validated Category category) {
        categoryService.update(category);
        return Result.success();
    }

Pojo

cpp 复制代码
    @NotNull
    private Integer id;//主键ID

更新的时候某些字段为必填(比如id), 新增的时候非必填:

Service

cpp 复制代码
    @Override
    public void add(Category category) {
        // 补充属性
        category.setCreateTime(LocalDateTime.now());
        category.setUpdateTime(LocalDateTime.now());
        // 获取用户id
        Map<String,Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        category.setCreateUser(id);
        categoryMapper.add(category);
    }


    @Override
    public void update(Category category) {
        category.setUpdateTime(LocalDateTime.now());
        categoryMapper.update(category);
    }

Mapper

cpp 复制代码
    <insert id="add">
        INSERT INTO category(category_name, category_alias, create_user, create_time, update_time)
        VALUES (#{categoryName},#{categoryAlias},#{createUser},#{createTime},#{updateTime})
    </insert>

     <update id="update">
        UPDATE category SET category_name=#{categoryName},category_alias=#{categoryAlias},update_time=#{updateTime}
        WHERE id=#{id}
    </update>

新增的时候只需获取 ThreadLocal 中的用户 id 进行有效的插入即可,Mapper 并不涉及 id 的操作,所以获取请求时不需要传入 id;更新的时候 Mapper 需要 id 进行文章信息的定位,所以获取请求时需要传入 id。++但是我们在 Pojo 给 id 属性加了 @NotNull 注解,表示不能为空;所以新增在获取对象请求的时候必须传入 id 否则就会抛出异常。++

如何解决这种问题呢?Validator 校验框架提供了分组校验,可以帮助我们快速的实现这样的需求。简单来说就是,++新增时使用新增校验规则,更新时使用更新校验规则。++

分组校验
把校验项进行归类分组,在完成不同的功能的时候,校验指定组中的校验项。

步骤:

|---------------|
| 定义分组 |
| 定义校验项时指定归属的分组 |
| 校验时指定要校验的分组 |

定义分组:

我们以在 Pojo 实体类中定义两个接口,说明分了 Add、Update 两个组。

cpp 复制代码
public class Category {

    public interface Add {

    }

    public interface Update {

    }
}

定义校验项时指定归属的分组:

cpp 复制代码
public class Category {
    @NotNull(groups = Update.class)
    private Integer id;
    @NotEmpty(groups= {Add.class,Update.class})
    private String categoryName;
    @NotEmpty(groups= {Add.class,Update.class})
    private String categoryAlias;

    public interface Add {}
    public interface Update {}
}

校验时指定要校验的分组:

cpp 复制代码
    @PostMapping
    public Result addCategory(@RequestBody @Validated(Category.Add.class) Category category) {
        categoryService.add(category);
        return Result.success();
    }


    @PutMapping
    public Result update(@RequestBody @Validated(Category.Update.class) Category category) {
        categoryService.update(category);
        return Result.success();
    }

这样新增时就不需要传入 id 了。

结合 @Validated 源码:我们来看一下

cpp 复制代码
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
    Class<?>[] value() default {};
}

例如:@Validated 注解中增加了 Category.Add.class 参数,表示对于定义了分组校验的字段使用 Add 校验规则,其他字段使用默认规则。

这样就又出现了另一个问题:如果同一个校验项属于多个分组的话,就需要在 groups= {} 中传入多个参数;这样我们就可以使用 @Validated 默认分组来优化这个问题。

举个例子:

如果说某个校验项没有指定分组,默认属于 Default 分组。分组之间可以继承,A extends B 那么 A 中拥有 B 中所有的校验项。

cpp 复制代码
public class Category {
    @NotNull(groups = Update.class)
    private Integer id;
    @NotEmpty
    private String categoryName;
    @NotEmpty
    private String categoryAlias;
   
    public interface Add extends Default {}

    public interface Update extends Default{}
}

所以 @NotEmpty 就相当于 groups= {Add.class,Update.class},而 @NotNull(groups = Update.class) 指定了校验项,所以只有更新的操作才进行校验。

相关推荐
刘婉晴14 分钟前
【信息安全工程师备考笔记】第三章 密码学基本理论
笔记·安全·密码学
晓数2 小时前
【硬核干货】JetBrains AI Assistant 干货笔记
人工智能·笔记·jetbrains·ai assistant
我的golang之路果然有问题3 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
lwewan3 小时前
26考研——存储系统(3)
c语言·笔记·考研
搞机小能手3 小时前
六个能够白嫖学习资料的网站
笔记·学习·分类
nongcunqq4 小时前
爬虫练习 js 逆向
笔记·爬虫
汐汐咯4 小时前
终端运行java出现???
笔记
无敌小茶6 小时前
Linux学习笔记之环境变量
linux·笔记
帅云毅6 小时前
Web3.0的认知补充(去中心化)
笔记·学习·web3·去中心化·区块链
豆豆6 小时前
day32 学习笔记
图像处理·笔记·opencv·学习·计算机视觉