SpringBoot实战第二天

今日战报

继续完善用户相关接口开发:

        1.完成获取用户信息功能

        2.完成更新用户信息功能

        3.完成更新用户头像功能

        4.完成更新用户密码功能

获取用户信息

接口文档

如接口文档所示,我们需要做的就是从header中的Authorization中读取token,解码后返回用户的全部信息,接口如下:

    @GetMapping("/userInfo")
    public Result<User> userInfo(@RequestHeader(name = "Authorization") String token){
        Map<String,Object> map = JwtUtil.parseToken(token);
        String username = (String) map.get("username");
        User user = userService.getByUserName(username);
        return Result.success(user);
    }

 这是运行结果

这里会发现一个问题,我们返回的数据中有经过加密的用户密码,这显然是不合适的,所以我们需要解决这个问题,Spring也给我们提供了用于解决这个问题的注解

    @JsonIgnore//SpringMVC把当前对象转换为json字符串时,忽略password,最终的json字符串中就没有password

把这行注解加到user实体类中的password变量声明的上方即可

还有一个小问题,我们在数据库中对于时间的名称使用的是_,例如创建时间create_time,而在实体类中使用的却是驼峰法,这样会导致mybatis从数据库中接收出的时间数据为null(上图是我解决了这个问题后截的),我么们需要在yml配置文件中解决这个问题

mybatis:
  configuration:
    map-underscore-to-camel-case: true #开启驼峰命名和下划线命名的自动转换

ThreadLocal

提供线程局部变量

        用来存取数据:set()/get()

        使用ThreadLocal存储的数据,线程安全

在ThreadLocal中,每个线程get到的数据只能是它自身set的,是读取不到其他线程set的数据的

而在TomCat运行时会给每个用户提供一个单独的线程,故可以通过ThreadLocal来在拦截器中set我们需要的信息,再去对应的接口中get信息,形成同一个线程内的数据共享,以减少参数的传递

ThreadLocal优化

先给出要用的ThreadLocal工具类

/**
 * ThreadLocal 工具类
 */
@SuppressWarnings("all")
public class ThreadLocalUtil {
    //提供ThreadLocal对象,
    private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();

    //根据键获取值
    public static <T> T get(){
        return (T) THREAD_LOCAL.get();
    }
	
    //存储键值对
    public static void set(Object value){
        THREAD_LOCAL.set(value);
    }


    //清除ThreadLocal 防止内存泄漏
    public static void remove(){
        THREAD_LOCAL.remove();
    }
}

然后在拦截器的preHandle方法中将方法内解析出的token数据通过get方法存储

        try {
            Map<String,Object> claims =  JwtUtil.parseToken(token);
            //将token解析出的树蕨存入到ThreadLocal中去
            ThreadLocalUtil.set(claims);
            //放行
            return true;

然后在需要用到claims的接口中通过get()方法取出数据(以获取用户信息时为例)

 

    @GetMapping("/userInfo")
    public Result<User> userInfo(){
//        Map<String,Object> map = JwtUtil.parseToken(token);
//        String username = (String) map.get("username");
        Map<String,Object> map = ThreadLocalUtil.get();
        String username = (String) map.get("username");
        User user = userService.getByUserName(username);
        return Result.success(user);
    }

当然,为了防止数据长期存储导致内存泄露,我们也需要在请求结束后释放掉存储的信息,在拦截器的 afterCompletion方法中调用方法类中的close方法即可

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //在请求结束后 清空ThreadLocal中的数据
        ThreadLocalUtil.remove();
    }

 

更新用户信息

接口文档

 

分析接口文档,我们需要从1json数据中获取一个User对象,然后更新其中的nickname和email,当然,也要写入新的更新时间。同时,参数校验也是保证程序健壮性不可或缺的一环

先看Controller层

    @PutMapping("/update")
    public Result update(@RequestBody User user){
        userService.update(user);
        return Result.success();
    }

Service层

    @Override
    public void update(User user) {
        user.setUpdateTime(LocalDateTime.now());
        userMapper.update(user);
    }

在Service层实现更新时间的写入

Mapper层

    @Update("update user set nickname = #{nickname} , email = #{email} , update_time = #{updateTime} where id = #{id}")
    void update(User user);

注意,等号前是数据库列名,#{}内是user实体类中的变量名

 

测试结果如上,当然,Header中要写入token(因为拦截器的存在)

 

参数校验

由于我们不提供username的修改,所以我们并不关注username的限制,我们要关注的参数校验主要集中在id与email中 

我们可以使用如下注解

 User实体类修改如下

    @NotNull
    private Integer id;//主键ID

    @NotEmpty
    @Pattern(regexp = "^//S{1,10}$")
    private String nickname;//昵称
    
    @NotEmpty
    @Email
    private String email;//邮箱

当然,为了使这些规则生效,在调用规则对应实体时我么要使用 @Validated注解来使规范生效

接口更新如下:

    @PutMapping("/update")
    public Result update(@RequestBody @Validated User user){
        userService.update(user);
        return Result.success();
    }

更新用户头像

接口文档

    @Override
    public void updateAvatar(String avatarUrl) {
        Map<String,Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        userMapper.updateAvatar(avatarUrl,id);
    }

 读取文档,我们需要注意的点便是头像格式是一个url路径

请求方式也是之前没有用过的pATCH

Controller层

    @PatchMapping("updateAvatar")
    public Result updateAvatar(@RequestParam String avatarUrl){
        userService.updateAvatar(avatarUrl);
        return Result.success();

    }

Service层

    @Override
    public void updateAvatar(String avatarUrl) {
        Map<String,Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        userMapper.updateAvatar(avatarUrl,id);
    }

 我们这里利用ThreadLocal来获取当前请求用户的id,一并传给mapper层 

Mapper层

    @Update("update user set user_pic = #{avatarUrl} , update_time = now() where id = #{id}")
    void updateAvatar(String avatarUrl,Integer id);

我们通过sql自带的now()函数来获取更改的时间

  参数校验

可以通过@URL来校验参数是否满足URL格式

Controller层更新如下

    @PatchMapping("updateAvatar")
    public Result updateAvatar(@RequestParam @URL String avatarUrl){
        userService.updateAvatar(avatarUrl);
        return Result.success();

    }

更新用户密码

接口文档

 

阅读更新文档,json传递的数据不像以前可以直接对应到User的变量,所以我们需要使用一个map来接收数据

并且我们发现文档没有考虑密码可能会被修改为空,所以也需要校验一下密码格式

写的好累,直接上Controller层和dao层吧

    @PatchMapping("updatePwd")
    @Validated
    public Result updatePwd(@RequestBody @Valid 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 lUser  = userService.getByUserName(username);
        if(!lUser.getPassword().equals(Md5Util.getMD5String(oldPwd)))
        {return Result.error("原密码输入错误");}

        //校验新密码是否符合格式
        if(!newPwd.matches(PWD_REGEXP))
        {return Result.error("新密码非法!");}

        //校验newPwd与rePwd是否一致
        if (!rePwd.equals(newPwd))
        {return Result.error("两次填写新密码不一致");}



        userService.updatePwd( newPwd);

        return Result.success();
    }
    @Update("update user set password = #{newPwd} , update_time = now() where id = #{id}")
    void updatePwd(String newPwd,Integer id);

对了,还用了一个正则,我单独放到正则类里去了(顺便把之前用到的正则全甩里面去了)

package com.cacb.pattern;

public class RegexPatterns {
    public static final String PWD_REGEXP =  "^\\S{11,16}$";
    public static final String NICKNAME_REGEXP = "^\\S{1,10}$";
    public static final String USERNAME_REGEXP = "^\\S{4,16}$";

    
            
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/372750.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浅谈QT的几种线程的使用和区别。

简介&#xff1a; 线程是操作系统中的基本执行单元&#xff0c;是一个独立的执行路径。每个线程都有自己的栈空间&#xff0c;用于存储本地变量和函数调用的上下文。多个线程可以在同一进程中并发执行&#xff0c;从而实现并发处理&#xff0c;提高程序的性能和响应能力。 与进…

Unet 实战分割项目、多尺度训练、多类别分割

1. 介绍 之前写了篇二值图像分割的项目&#xff0c;支持多尺度训练&#xff0c;网络采用backbone为vgg的unet网络。缺点就是没法实现多类别的分割&#xff0c;具体可以参考&#xff1a;二值图像分割统一项目 本章只对增加的代码进行介绍&#xff0c;其余的参考上述链接博文 本…

追觅发布多款旗舰新品,双机械臂扫地机器人X40领衔登场

2月2日&#xff0c;追觅科技全球首创仿生“双”机械臂新品发布会在苏州举行。会上&#xff0c;追觅科技中国区总裁郭人杰分享了追觅科技全球化发展的业绩成果。郭人杰称&#xff0c;2019-2023年&#xff0c;追觅科技5年复合年增长率超过100%&#xff0c;增速领跑智能清洁行业&a…

JAVA中的代码块

一、基本语法 [修饰符]{代码; }; {System.out.println(111); } 1.修饰符可选&#xff0c;要写的话也只能写static2.代码块分为两类 使用static修饰的是静态代码块 没有static修饰的叫普通代码块3.逻辑语句可以为任何逻辑语句4.;可以不写 1)静态代码块 作用是对类进行初始化…

SpringBoot源码解读与原理分析(十八)启动SpringApplication逻辑分析

文章目录 6.2 启动SpringApplication6.2.1 前置准备6.2.1.1 计时器对象的使用6.2.1.2 awt的设置6.2.1.3 对比SpringBoot 2.0.x-2.2.x6.2.1.4 对比SpringBoot 2.4.x 6.2.2 获取SpringApplicationRunListeners6.2.2.1 EventPublishingRunListener6.2.2.2 与其他版本的对比 6.2.3 …

TP框架 之think-auth权限认证

一、安装think-auth composer require 5ini99/think-auth二、数据表 -- ---------------------------- -- think_auth_rule&#xff0c;规则表&#xff0c; -- id:主键&#xff0c;name&#xff1a;规则唯一标识, title&#xff1a;规则中文名称 status 状态&#xff1a;为1正常…

我在代码随想录|写代码Day26 |回溯算法|332. 重新安排行程 , 51. N皇后 , 37. 解数独

学习目标&#xff1a; 博主介绍: 27dCnc 专题 : 数据结构帮助小白快速入门 &#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d; ☆*: .&#xff61;. o(≧▽≦)…

私域市场如何突破?解锁高效转化的三个核心要素!

一、私域电商三要素 一是私域、二是社交、三是电商。 私域就是承载用户的地方&#xff0c;比如微信&#xff0c;然后做好私域运营。 社交就是通过内容触达用户于用户建立社交关系。 电商就是通过私域卖产品给用户。 私域电商有几个公式&#xff1a; 社交红利信息关系链互…

redis(6)

文章目录 一、redis clusterRedis Cluster 工作原理Redis cluster 基本架构Redis cluster主从架构Redis Cluster 部署架构说明部署方式介绍 原生命令手动部署原生命令实战案例&#xff1a;利用原生命令手动部署redis cluster 实战案例&#xff1a;基于Redis 5 的redis cluster部…

Nicn的刷题日常之获得月份天数

目录 1.题目描述 描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 2.解题 1.题目描述 描述 KiKi想获得某年某月有多少天&#xff0c;请帮他编程实现。输入年份和月份&#xff0c;计算这一年这个月有多少天。 输入描述&#xff1a; 多组输入&#xff0c;一行有两…

外包干了10个月,技术退步明显...

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

如何把大容量10G的文件分享给别人?整理了3个简单的方法~

如果文件过大&#xff0c;比如10G的文件发送起来简直问题重重&#xff0c;不仅费时费流量而且还很可能在发送的中途失败&#xff0c;这时候就需要借助一些压缩软件对文件进行压缩&#xff0c;下面就向大家介绍3个好用的压缩软件~ 方法一&#xff1a;使用嗨格式压缩大师压缩后发…

深入理解Java中的二叉树

目录 一、什么是二叉树? 二、二叉树的主要类型 三、二叉树的实现 四、二叉树的应用 五、关于二叉树的题目 引言: 二叉树是计算机科学中常用的一种数据结构&#xff0c;它是由节点组成的层级结构&#xff0c;每个节点最多有两个子节点。在Java编程语言中&#xff0c;二…

架构学习(三):scrapy-redis源码分析并实现自定义初始请求

scrapy-redis源码分析并实现自定义初始请求 前言关卡&#xff1a;如何自定义初始请求背景思考简单又粗暴的方式源码分析 结束 前言 通过这篇文章架构学习(二)&#xff1a;原生scrapy如何接入scrapy-redis&#xff0c;初步入局分布式&#xff0c;我们正式开启scrapy-redis分布式…

C语言递归与迭代并举:双重视角下的C语言阶乘计算实现

引言 计算一个正整数的阶乘是常见的数学问题。阶乘的定义为&#xff1a;n的阶乘&#xff08;记作n!&#xff09;是所有小于及等于n的正整数的乘积。例如&#xff0c;5的阶乘&#xff08;5!&#xff09;就是54321120。下面我们将通过一个使用递归方法实现阶乘的C语言代码示例&am…

Qt|实现时间选择小功能

在软件开发过程中&#xff0c;QtDesigner系统给出的控件很多时候都无法满足炫酷的效果&#xff0c;前一段时间需要用Qt实现选择时间的小功能&#xff0c;今天为大家分享一下&#xff01; 首先看一下时间效果吧&#xff01; 如果有需要继续往下看下去哟~ 功能 1&#xff1a;开…

linux 05重定向和管道管理

01.重定向 例子&#xff1a; 关键字 > date 中的数据要写入 887.txt 02.FD 进程的句柄文件 进程的信息的传输&#xff1a; porcess 会有 0 号文件来接收键盘的信息 1 号文件 向终端 来输出信息 FD文件存储在proc文件中&#xff0c;可以看看 举个例子&#xff1a; 查看pro…

计算机网络原理基础

目录 前言&#xff1a; 1.网络发展史 2.网络通信基础 2.1IP地址 2.1.1定义 2.1.2格式 2.2端口号 2.2.1定义 2.2.2格式 2.3协议 2.3.1定义 2.3.2作用 2.3.3分层 2.4五元组 2.4.1定义 2.4.2组成 3.TCP/IP五层网络模型 3.1模型概念 3.2模型构成 3.3网络分层对应…

docker-compose部署laravel项目实战(主机nginx连接项目容器)(详细配置过程)

我用的是主机上的nginx,没有用docker安装nginx&#xff0c; 所以需要先在主机上安装nginx # 更新系统yum sudo yum update# 安装安装包sudo yum install epel-release sudo yum install wget# 安装Nginx sudo yum install nginx #启动 sudo systemctl start nginx #开机自启动…

C语言笔试题之反转链表(头插法)

实例要求&#xff1a; 1、给定单链表的头节点 head &#xff1b;2、请反转链表&#xff1b;3、最后返回反转后的链表&#xff1b; 案例展示&#xff1a; 实例分析&#xff1a; 1、入参合理性检查&#xff0c;即head ! NULL || head->next ! NULL&#xff1b;2、while循环…