跟黑马程序员学pringboot3+vue3,代码都是黑马程序员视频里的代码
实战篇-03_注册接口_哔哩哔哩_bilibili
本文仅仅用于学习记录
开发用户接口
注册接口
开发流程:明确需求>>阅读接口文档>>思路分析>>开发>>测试
分析:
这个图是截黑马ppt的图:
问题(今天在注册这个接口这里都搞不定!!!遇到很多问题!!!)
问题1:
因为有写依赖与springboot项目依赖版本号不兼容
快速的解决方法:
直接新建springboot项目,并且勾选这些需要用到的依赖,就是自动生成兼容的依赖,复制生成的pom.xml文件替换原来的pom.xml文件,修改一下pom的名字即可。
所以本菜鸟更喜欢一开始就建立springboot项目,哭了。。。。。。。
新建项目
创建实体类
首先先在启动类的包下创建以下几个包:(项目结构)
controller包
service包
(service包下再新建一个Impl包)
mapper包
pojo包
utils包
根据数据库表结构,在 pojo
包下创建 User.java
实体类
package com.example.demo.pojo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
private Integer id;//主键ID
private String username;//用户名
private String password;//密码
private String nickname;//昵称
private String email;//邮箱
private String userPic;//用户头像地址
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
}
自动给实体类生成Geeter和Setter方法
-
在pom.xml中添加lombok依赖
2.在实体类中添加@Data
在application.yml文件配置配置数据库连接信息
废话来了:我们开发的是注册接口,需要往数据库里添加用户数据,所以一开始就把数据库连接信息配置好
路径:src/main/resources/application.properties
先把application.properties文件改造为application.yml
(如何修改 ,这里有:配置文件application.properties-CSDN博客 中的yml部分)
配置数据库信息:
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/big_event
username: root
password: 123456
查看接口文档,了解注册接口post请求长这样
http://localhost:8080/user/register?username=user1&password=123456
创建控制器
在 src/main/java/com/example/demo/controller
包下创建 UserController.java
控制器。
定义注册接口 /register
并实现注册逻辑
package com.example.demo.controller;
import com.example.demo.pojo.Result;
import com.example.demo.pojo.User;
import com.example.demo.service.UserService;
import jakarta.annotation.Resource;
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;
/*
@Resource注解用于自动注入依赖的 Bean。它是一个泛型注解,可以自动按类型注入,
也可以通过指定 name 属性来注入特定的 Bean。
*/
@Resource
/*@RestController注解用于将类标记为控制器,主要用来处理 HTTP 请求。
它相当于 @Controller 和 @ResponseBody 注解的组合,表示类中的方法返回的数据直接写入 HTTP 响应体中,而不是返回一个视图名称。
*/
@RestController
/*
@RequestMapping("/user")注解用于映射 HTTP 请求到控制器的处理方法。
这里指定了所有以 "/user" 开头的请求都会被这个控制器处理。
*/
@RequestMapping("/user")
public class UserController {
/*
@Autowired注解也是用于自动注入依赖的 Bean,
通常用于字段、构造函数、设置方法和普通方法参数。
它通过类型来自动注入 Bean,如果存在多个同类型的 Bean,
则需要通过 @Qualifier 注解指定具体的 Bean 名称
*/
@Autowired
private UserService userService;
/*
@PostMapping("/register")注解用于映射 HTTP POST 请求到特定的处理方法。
这里指定了当接收到 "/user/register" 的 POST 请求时,会调用 register 方法
*/
@PostMapping("/register")
public Result register(String username, String password){
//查询用户名是否已存在
User user =userService.findByUserName(username);
if(user == null){
userService.register(username,password);
return Result.success();
}else{
//占用
return Result.error("用户名已经被占用");
}
}
}
“不知道Bean注入的,看一下Bean部分的内容”
创建服务层接口和实现(实验查询用户是否存在和注册用户的业务逻辑)
在 src/main/java/com/example/demo/service
包下创建 UserService.java
接口
package com.example.demo.service;
import com.example.demo.pojo.User;
public interface UserService {
// 根据用户名查询用户
User findByUserName(String username) ;
//注册
void register(String username, String password) ;
}
在 src/main/java/com/example/demo/service
/Impl包下创建 UserServiceImpl.java
实现 UserService
接口
package com.example.demo.service.impl;
import com.example.demo.mapper.UserMapper;
import com.example.demo.pojo.User;
import com.example.demo.service.UserService;
import com.example.demo.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/*
@Service注解用于标记服务层的组件,它指示 Spring 容器将这个类作为一个 Bean 进行管理。
通常用于业务逻辑层,但不限于此
*/
@Service
public class UserServiceImpl implements UserService {
/*
@Autowired注解用于自动注入依赖的 Bean。
在这个例子中,它被用来自动注入 UserMapper 类的实例,
这是数据访问层的一个组件
*/
@Autowired
private UserMapper usermapper;
@Override
public User findByUserName(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);
}
}
创建 MyBatis Mapper 接口(与数据库交互,进行数据库操作就写在这里)
在 src/main/java/com/example/demo/mapper
包下创建 UserMapper.java
接口
package com.example.demo.mapper;
import com.example.demo.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
//根据用户名查询添加
@Select("select * from user where username = #{username}")
User findByUserName(String username) ;
//添加用户
@Insert("insert into user(username,password,create_time,update_time) values(#{username},#{password},now(),now())")
void add(String username, String password);
}
/*
@Mapper这是 MyBatis 提供的一个注解,用于标记 Mapper 接口。
当 Spring 扫描到这个注解时,它会为这个接口创建一个代理对象,
这个代理对象会实现接口中定义的所有方法,并处理 SQL 映射。
@Select这是 MyBatis 的注解,用于标记执行查询操作的方法。
在这个例子中,@Select 用于定义一个 SQL 查询语句,该语句根据提供的用户名查询用户。
@Insert这是 MyBatis 的注解,用于标记执行插入操作的方法。
在这个例子中,@Insert 用于定义一个 SQL 插入语句,该语句将新用户的信息插入到数据库中。
*/
/*
@Select("select * from user where username = #{username}")
User findByUserName(String username) ;
当 Spring 容器中的 MyBatis 代理对象调用 findByUserName 方法时,
MyBatis 框架会执行以下步骤:
将方法的 username 参数替换 SQL 语句中的 #{username} 占位符。
执行生成的 SQL 查询。
将查询结果(如果有的话)映射到 User 类的实例上。
返回 User 对象给调用者。
*/
创建 MD5 工具类
数据库密码一般都不使用明文,要进行加密
在 src/main/java/com/example/demo/utils
包下创建 Md5Util.java
工具类,用于密码加密。
package com.example.demo.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);
}
}
创建统一响应结果类
查看接口文档:得到响应的数据类型
在 src/main/java/com/example/demo/pojo
包下创建 Result.java
类,用于封装 HTTP 响应
package com.example.demo.pojo;
import lombok.AllArgsConstructor;
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是Lombok 库提供的注解,用于自动为类生成无参构造函数。
如果类中没有其他构造函数,使用这个注解可以避免手动编写无参构造函数。
@AllArgsConstructor是 Lombok 库提供的注解,用于自动为类生成包含所有字段作为参数的构造函数。
这在需要通过构造函数初始化所有属性时非常有用。
@Data是 Lombok 库提供的注解,用于自动为类生成 getters、setters、
equals()、hashCode() 和 toString() 方法。这可以显著减少样板代码。
*/
- 当调用
success(E data)
方法时,会创建一个新的Result
对象,其code
设置为0
,message
设置为"操作成功"
,data
设置为传入的data
参数。- 当调用
success()
方法时,会创建一个新的Result
对象,其code
和message
与success(E data)
方法相同,但data
设置为null
。- 当调用
error(String message)
方法时,会创建一个新的Result
对象,其code
设置为1
,message
设置为传入的错误信息,data
设置为null
。
Result<T> 是一个使用了泛型的Java类。让我们逐步解析一下:
Result: 这是一个类名,它被设计为一个容器或载体,用于封装从后端服务到前端或其他服务的数据传输结果。它通常包含了业务操作的状态、错误信息以及实际的数据。
<T>: 这是泛型的标记。T 是 Type 的缩写,它是一个占位符,代表任何类型的对象。当我们在定义 Result 类时,我们并不知道将来会存储哪种类型的数据,因此使用泛型 T 来表示可以存储任意类型的数据。
Result<T>: 当你看到 Result<T>,这意味着你可以创建一个 Result 类的对象,其中的 data 属性可以是任何类型的数据。例如,你可以有 Result<String>、Result<Integer> 或者 Result<List<User>> 等等。
实例化与使用:
如果你创建一个 Result<String> 对象,那么 T 就会被替换为 String 类型,意味着 data 属性将只能存储字符串。
如果你创建一个 Result<List<User>> 对象,T 将被替换为 List<User> 类型,意味着 data 属性将只能存储用户列表。
泛型的好处:
类型安全:编译器会在编译时检查类型,确保你不会把错误类型的对象放入 Result 中。
重用性:Result<T> 可以被用于多种类型的数据,提高了代码的灵活性和重用性。
所以,当你在代码中看到 Result<T>,它表示的是一个可以携带任意类型数据的结果对象。
在Java中,<E> Result<E> 是一个泛型方法的声明,它与泛型类的使用方式类似,但作用于方法级别。让我们详细解释一下这个声明:
<E>: 这个尖括号内的 E 定义了一个类型参数,它可以在方法内部作为类型占位符使用。E 通常代表“Element”或“Entity”,但它可以是任何合法的标识符。
Result<E>: 这是方法的返回类型,其中 <E> 表示返回的 Result 对象将具有一个名为 data 的字段,该字段的类型将由调用此方法时传入的实际类型决定。这里的 E 必须与前面的 <E> 相匹配,确保方法返回的 Result 对象的 data 字段类型与方法参数 data 的类型相同。
public static <E> Result<E> success(E data): 整体来看,这是一个静态泛型方法,它接收一个类型为 E 的参数 data,并返回一个类型为 Result<E> 的对象。这意味着无论你传递什么类型的参数给这个方法,返回的 Result 对象的 data 字段都将具有相同的类型。
例如,如果你这样调用该方法:
Result<String> result = Result.success("Hello, World!");
这里,E 被实例化为 String 类型,所以 result 将是一个 Result<String> 类型的对象,它的 data 字段将存储一个字符串 "Hello, World!"。
再比如
Result<Integer> result = Result.success(42);
这次,E 被实例化为 Integer 类型,所以 result 将是一个 Result<Integer> 类型的对象,它的 data 字段将存储整数 42。
通过使用泛型方法,你可以编写更加灵活且类型安全的代码,而无需为每种数据类型都编写一个专门的方法。
写在最后:搞了好久还遇到问题,难鼠了
出现HTTP 406 错误
原因:服务器无法满足请求者(客户端)所接受的响应内容类型。在这个情况下,客户端发送了一个 POST
请求到 /user/register
接口,期望接收 application/json
类型的数据,但是服务器没有正确处理这个请求,或者没有返回正确的 Content-Type
。
解决方法:在返回结果的实体类里添加@Data
最后的最后,成功了
使用 Lombok 的 @Data
注解可以自动生成必要的 getter 和 setter 方法,从而解决因缺少访问器方法而导致的 JSON 序列化问题。
为什么使用 @Data
注解可以解决问题?
-
对象序列化:在 Spring Boot 应用中,返回的对象通常需要被序列化为 JSON 格式。这通常是通过 Spring Boot 默认的 HttpMessageConverter 实现的,比如
MappingJackson2HttpMessageConverter
。 -
Lombok 注解:Lombok 是一个 Java 库,它通过注解处理器在编译时自动生成常见的 Java 代码,如 getter、setter、构造函数、
toString
方法等。@Data
注解是 Lombok 提供的一个强大注解,它等价于同时使用@Getter
、@Setter
、@ToString
、@EqualsAndHashCode
和@RequiredArgsConstructor
。 -
自动生成访问器:当你在一个类上使用
@Data
注解时,Lombok 会自动为该类的所有字段生成 getter 和 setter 方法。这对于 JSON 序列化非常重要,因为 JSON 库(如 Jackson)需要通过这些访问器方法来访问对象的属性。 -
解决序列化问题:如果没有 getter 方法,JSON 序列化库可能无法访问对象的属性,从而导致序列化失败。这可能间接导致 Spring 返回 406 错误,因为客户端期望的是 JSON 响应,但服务器没有正确生成。
-
简化代码:使用 Lombok 可以大大减少样板代码,使开发者能够专注于业务逻辑。
参数校验
上面的代码并没有进行参数校验
一般使用Spring Validation
Spring Validation是Spring提供的一个参数校验框架,使用预定义的注解完成参数校验
1.引入Spring Validation起步依赖
记得刷新!!!
2.在需要校验的参数前面添加@Pattern注解
5~16位非空字符用正则表达式为 \S{5,16},由于"\"是转义字符,在前面再加一个\,如图:
3.在类上加上@Validated注解
不符合参数要求的请求失败了,但是返回结果格式不符合要求
解决方法:可以使用全局异常处理器处理参数校验的异常
在启动类下新建一个exception包,定义一个类GlobalExceptionHandler
这个处理器使用
@RestControllerAdvice
注解来声明它将作为全局的异常处理控制器,并且@ExceptionHandler(Exception.class)
注解表明它将处理所有类型的Exception
。在
handleException
方法中,首先调用e.printStackTrace()
来打印堆栈跟踪信息,这有助于开发过程中定位问题。然后,使用StringUtil.notNullNorEmpty
方法检查异常消息是否为空或非空,如果非空则直接返回异常消息,否则返回默认的错误信息“操作失败”。最后,返回一个Result
对象,用于封装响应体和状态码
结果: