接着上篇博客学习。上篇博客是已经搭建好了后台的开发环境。现在逐一去开发和完善各个层的接口。首先就是从用户这个模块开始。
目录
2、数据库字段采用下划线命名,而实体类里面对应的采用驼峰命名。编辑
3、Lombok?在编译阶段,为实体类自动生成setter()、getter()、toString()方法。
9、接口测试(因为还没有写好前端,就要借助一个测试接口工具postman)
1、用户模块需要开发的接口
- 注册
- 登录
- 获取用户详细信息
- 更新用户基本信息
- 更新用户头像
- 更新用户密码
2、数据库字段采用下划线命名,而实体类里面对应的采用驼峰命名。
3、Lombok?在编译阶段,为实体类自动生成setter()、getter()、toString()方法。
(使用条件:在pom文件中引入依赖,在实体类上添加注解)
- 要安装并启用这个插件(辣椒)。
- 在pom.xml文件中引入lombok依赖。
- 然后在需要的实体类上添加注解@Data。
- 测试查看效果。
4、基本的开发流程。
- 明确需求。清楚的了解用户是如何使用。
- 阅读接口文档。明确接口的输入和输出。
- 思路分析。分析将来如何写代码、逻辑是啥?
- 开发。
- 测试。测试接口的正确性。
5、分析与实现注册接口。
- 注册就是用户在表单里面填写用户名、密码,再点击注册按钮。接着会在后台添加新用户。
- 查看接口开发文档(注册)
- data。返回给浏览器的主体数据。在案例中,它的格式是JSON格式的(以后基本上都是这个格式的)。
- 因为所有的响应数据,它的格式都是一样的。所以就去定义一个result类。类里面有三个成员变量,对应着"code"、"message"、"data"。当给浏览器响应一个result对象时,springboot对把这个对象自动的转换为JSON格式的字符串,这样格式就处理好了。
- result实体类
package com.feisi.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; //统一响应结果 @NoArgsConstructor @AllArgsConstructor @Data public class Result<T> { private Integer code;//业务状态码 0-成功 1-失败 private String message;//提示信息 private T data;//响应数据 //快速返回操作成功响应结果(带响应数据) public static <E> Result<E> success(E data) { return new Result<>(0, "操作成功", data); } //快速返回操作成功响应结果 public static Result success() { return new Result(0, "操作成功", null); } public static Result error(String message) { return new Result(1, message, null); } }
- 其中注解@NoArgsConstructor与注解@AllArgsConstructor是用lombok提供的自动生成无参构造方法与有参构造方法。
- 三个静态方法。其中业务状态码"code"为"0"代表成功、"code"为"1"代表失败。然后success()方法如果调用时传入数据data,就new一个Result的对象原封不动装起来。
- 如果时添加用户或者是添加文章,然后没有什么响应的主体数据,就不需要"data",重写构造方法,"data"里面传一个"null"就行了。
- 最后一个error()方法,调用时把提示的错误信息传进来,内部new一个Result,响应状态码为"1",然后"data"为"null"就行了。
- 请求路径:"/user"在UserController外层声明。
- 如何快速关闭所有打开的标签页
6、向AI文心一言提问:为什么注册时采用post请求?
在Web开发中,注册用户时采用POST请求而不是GET请求,主要是基于以下几个原因:
- 数据安全性:POST请求用于提交表单数据到服务器,这些数据包括用户名、密码等敏感信息。与GET请求不同,POST请求的数据不会附加在URL之后,因此不会在浏览器的历史记录、服务器日志或代理服务器日志中留下痕迹,从而提高了数据的安全性。
- 数据长度限制:GET请求通过URL传递数据,而URL的长度是有限制的(浏览器和服务器都可能对URL长度有限制)。这限制了可以通过GET请求发送的数据量。相反,POST请求通过HTTP请求体发送数据,可以发送大量数据,适合用于注册表单等需要传输大量信息的场景。
- 数据可见性:GET请求的数据会直接显示在URL中,这可能不是所有信息都希望被用户或第三方看到的(例如密码)。而POST请求的数据是包含在HTTP请求体中,对用户不可见,更符合注册等需要保护用户隐私的场景。
- 可缓存性:GET请求是可以被缓存的,这意味着浏览器或中间代理服务器可能会存储GET请求的响应,并在后续请求中直接使用缓存的数据。然而,对于注册等提交表单的POST请求,由于每次提交的数据都可能不同,因此不应该被缓存。
- 语义明确性:HTTP协议中定义了GET和POST的不同语义。GET请求通常用于请求服务器发送资源,而POST请求则用于向服务器提交数据以进行处理(如提交表单数据)。因此,在注册用户时,使用POST请求更符合HTTP协议的设计初衷。
综上所述,注册时采用POST请求是为了保护用户数据的安全性、传输大量数据、避免敏感信息泄露、避免不必要的缓存,以及符合HTTP协议的语义。
7、创建三层中的类
- 在controller包下new一个类"UserController"。
- new一个"UserService"(接口)。然后在impl包下提供一个实现类"UserServiceImpl"去实现接口"UserService"就行了。
- mapper包下new一个"UserMapper"(接口)。
8、每个类依次开始编写代码。
UserController类。
(1)首先在类上面添加注解@ResultController与注解@RequestMapping,并且需求指定请求的映射路径"/user"。
@RestController @RequestMapping("/user")
- 将来要用到UserService。就要去类里面注入service对象。
@Autowired private UserService userService;
(2)其次在register()方法上添加注解@PostMapping(因为注册接口的请求方式是post)并且添加映射地址"/register"。
@PostMapping("/register")
- 查询用户(根据用户名查询,查看是否被占用已注册)
- 注册用户
import com.feisi.pojo.Result; import com.feisi.pojo.User; import com.feisi.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Title: UserController * @Author HeYouLong * @Package com.feisi.controller * @Date 2024/9/17 下午1:51 * @description: */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @PostMapping("/register") public Result register(String username, String password) { //查询用户 User user = userService.findByName(username); if(user==null){ //没有占用 //注册 userService.register(username,password); return Result.success(); }else { //被占用 return Result.error("用户名已被占用"); } } }
UserService接口。
(1)在"UserController"类里面注入service对象报错原因。解决:去实现类"UserControllerImpl"添加注解@Service。
package com.feisi.service; import com.feisi.pojo.User; /** * @Title: UserService * @Author HeYouLong * @Package com.feisi.service * @Date 2024/9/17 下午2:00 * @description: */ public interface UserService { //根据用户名查询用户 User findByName(String username); //注册 void register(String username, String password); }
UserServiceImpl实现类。
(1)在这个类上面添加注解@Service,将当前这个类的对象注入到容器里面。
@Service
(2)在实现类"UserServiceImpl"里面调用mapper层里的方法,进行数据的处理(sql语句)那么就要在实现类"UserServiceImpl"里面注入"UserMapper"对象。
@Autowired private UserMapper userMapper;
(3)在处理注册时,要进行数据库加密,因为不能直接看到数据表里数据。加密完成之后,就可以调用mapper层的方法去注册添加用户。
- 加密一般使用MD5加密算法完成。使用一个工具类"Md5Util"。这个类里面提供了一个方法getMD5String():它能够传进一个明文的密码,返回一个密文的密码。
package com.feisi.service.impl; import com.feisi.mapper.UserMapper; import com.feisi.pojo.User; import com.feisi.service.UserService; import com.feisi.utils.Md5Util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @Title: UserServiceImpl * @Author HeYouLong * @Package com.feisi.service.impl * @Date 2024/9/17 下午2:03 * @description: */ @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User findByName(String username) { User user = userMapper.findByUserName(username); return user; } @Override public void register(String username, String password) { //加密处理 //传入明文密码,返回一个密文密码 String md5String = Md5Util.getMD5String(password); //添加,注意传入的密码是加密后的字符串 userMapper.add(username,md5String); } }
UserMapper接口。
(1)在里面添加两个抽象方法(findByUserName()、add())
(2)在每个方法上添加注解@Select("")、@Insert(""),书写sql语句。
(3)记得在接口上添加注解@Mapper。
@Mapper
package com.feisi.mapper; import com.feisi.pojo.User; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; /** * @Title: UserMapper * @Author HeYouLong * @Package com.feisi.mapper * @Date 2024/9/17 下午2:10 * @description: */ @Mapper public interface UserMapper { //根据用户名查询用户 @Select("select * from user where username = #{username}") User findByUserName(String username); //添加 //注意换行后,values前面有一个空格 //now函数,获取当前数据库的时间 @Insert("insert into user(username,password,create_time,update_time)" + " values(#{username},#{password},now(),now())") void add(String username, String password); }
9、接口测试(因为还没有写好前端,就要借助一个测试接口工具postman)
- 中文翻译版的postman官网。
PostMan中文文档https://postman.org.cn/
- 官网中注册一个免费的账号(用户)。
- 安装并打开该软件工具。
- 打开后第一件事情就是创建工作空间。点击左上角的workspaces —>createWorkspace——>点击next——>起一个名字Name——>点击create。
- 第二件事情就是创建一个工作集(Collections)。这里直接导入已经创建好的file。
- 可以看到所有后面要测试目录的用例
- 这里只写了注册的相关接口操作。所以只看注册的先。
- 出现报错("500"可能是sql语句写错了的原因)
出现"406"。解决:因为在前面的Result类里面没有添加注解@Data,然后编译的时候,就不能自动的提供getter()、setter()等等方法,不然框架就没法把Result对象转换JSON字符串。
- 修改好之后,重新启动springboot工程。再次在postman中测试接口。操作成功后,检查数据库是否有数据!
- 再添加进去一次试试,看看返回的结果。
10、IDEA中如何让包的层次结构(树),一层一层显示。
- 不勾选压缩空的中间软件包(英文:"Compact Middle Packages")
- 若勾选了它,就会如下显示,本人觉得不方便。
11、在IDEA中如何显示包中的接口的方法。
- 勾选显示成员(英文:"Show Members")
12、Md5Util密文工具类代码。
package com.feisi.utils; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class Md5Util { /** * 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合 */ protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; protected static MessageDigest messagedigest = null; static { try { messagedigest = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException nsaex) { System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。"); nsaex.printStackTrace(); } } /** * 生成字符串的md5校验值 * * @param s * @return */ public static String getMD5String(String s) { return getMD5String(s.getBytes()); } /** * 判断字符串的md5校验码是否与一个已知的md5码相匹配 * * @param password 要校验的字符串 * @param md5PwdStr 已知的md5校验码 * @return */ public static boolean checkPassword(String password, String md5PwdStr) { String s = getMD5String(password); return s.equals(md5PwdStr); } public static String getMD5String(byte[] bytes) { messagedigest.update(bytes); return bufferToHex(messagedigest.digest()); } private static String bufferToHex(byte bytes[]) { return bufferToHex(bytes, 0, bytes.length); } private static String bufferToHex(byte bytes[], int m, int n) { StringBuffer stringbuffer = new StringBuffer(2 * n); int k = m + n; for (int l = m; l < k; l++) { appendHexPair(bytes[l], stringbuffer); } return stringbuffer.toString(); } private static void appendHexPair(byte bt, StringBuffer stringbuffer) { char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>> // 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同 char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换 stringbuffer.append(c0); stringbuffer.append(c1); } }