切换主题
一、项目实战
1、环境搭建
准备:
- 依赖(web、mybatis、mysql驱动)
- 配置application.yml的mybatis配置信息
2、数据库创建
sql
sql
-- 创建数据库
create database big_event;
-- 使用数据库
use big_event;
-- 用户表
create table user (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) comment '密码',
nickname varchar(10) default '' comment '昵称',
email varchar(128) default '' comment '邮箱',
user_pic varchar(128) default '' comment '头像',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '用户表';
-- 分类表
create table category(
id int unsigned primary key auto_increment comment 'ID',
category_name varchar(32) not null comment '分类名称',
category_alias varchar(32) not null comment '分类别名',
create_user int unsigned not null comment '创建人ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间',
constraint fk_category_user foreign key (create_user) references user(id) -- 外键约束
);
-- 文章表
create table article(
id int unsigned primary key auto_increment comment 'ID',
title varchar(30) not null comment '文章标题',
content varchar(10000) not null comment '文章内容',
cover_img varchar(128) not null comment '文章封面',
state varchar(3) default '草稿' comment '文章状态: 只能是[已发布] 或者 [草稿]',
category_id int unsigned comment '文章分类ID',
create_user int unsigned not null comment '创建人ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间',
constraint fk_article_category foreign key (category_id) references category(id),-- 外键约束
constraint fk_article_user foreign key (create_user) references user(id) -- 外键约束
)
3、请求参数校验
1、引入validation
xml
<!-- vaildation参数校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2、加入注解
@Validated和@Pattern
java
@RequestMapping("/users")
@RestController
@Validated
public class UserController {
@Autowired
private UserService userService;
// 用户注册
@PostMapping("/register")
public Result register(@Pattern(regexp = "^\\s{5,16}$") String username, @Pattern(regexp = "^\\s{5,16}$") String password) {
// 先判断是否存在此用户
User user = userService.findByUsername(username);
// 注册用户
if (user == null) {
userService.register(username, password);
return Result.success();
} else {
// 返回错误信息
return Result.error("用户名已存在");
}
}
}
3、设置全局异常
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e){
e.printStackTrace();
return Result.error(StringUtils.hasLength(e.getMessage())?e.getMessage():"操作失败");
}
}
4、JWT的引入
jwt的版本一定要一致
xml
<!-- java-jwt依赖-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
1、生成token
java
@Test
public void TestCreateJwt(){
Map<String,Object> claims=new HashMap<>();
claims.put("id",1);
claims.put("username","黄达全");
String token = JWT.create()
.withClaim("user", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
.sign(Algorithm.HMAC256("huangdaquan"));
System.out.println(token);
}
结果
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6Ium7hOi-vuWFqCJ9LCJleHAiOjE3MDExMzkzNTl9.dJRvMnQ1pe9JYdgF6t5nxmwl7EoP8qP4kpZMa2wBmGY
2、校验token
java
@Test
public void TestParseToken(){
String token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6Ium7hOi-vuWFqCJ9LCJleHAiOjE3MDExMzg3NDF9.Mcyx4V2GibjMk0FrtU1lJX0--02JrdGmsvZXVoweQH8";
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("huangdaquan")).build();
DecodedJWT verify = jwtVerifier.verify(token);
Map<String, Claim> claims = verify.getClaims();
Claim user = claims.get("user");
System.out.println(user);
}
结果
json
{"id":1,"username":"黄达全"}
5、拦截器的使用
1、单个使用(不推荐)
controller类
java
@RequestMapping("/articles")
@RestController
public class ArticleController {
// 获取所有文章数据
@GetMapping
public Result getAll(@RequestHeader(name = "Authorization") String token, HttpServletResponse response){
try {
Map<String, Object> parseToken = JwtUtil.parseToken(token);
return Result.success("获取数据");
} catch (Exception e) {
response.setStatus(401);
return Result.error("未登录");
}
}
}
2、注册拦截器
1、创建interceptors -> LoginInterceptor 编写拦截器
java
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 请求头解析出token
String token = request.getHeader("Authorization");
// 验证token
try {
Map<String, Object> parseToken = JwtUtil.parseToken(token);
return true;
} catch (Exception e) {
response.setStatus(401);
return false;
}
}
}
2、创建config -> WebCogfig 注册拦截器
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
}
}
6、请求响应忽略某属性(如密码)
@JsonIgnore
注解,这是一种用于在Java对象序列化为JSON格式时控制某些字段不被包括在序列化结果中的注解。
java
@JsonIgnore
private String password;//密码
7、驼峰命名映射不到数据库(mybatis)
问题:createTime和updateTime为null,映射不出数据库的数据
需要修改applicatiion.yml文件的配置
设置MyBatis是否要将数据库字段的下划线命名风格自动映射为Java对象的驼峰命名风格。
将数据库字段的下划线命名风格自动映射为Java对象的驼峰命名
yaml
mybatis:
configuration:
map-underscore-to-camel-case: true
8、ThreadLocal优化拦截器
使用ThreadLocal保存用户关键信息,减少重复的获取用户信息代码
1、在请求拦截器里,保存token
java
try {
Map<String, Object> parseToken = JwtUtil.parseToken(token);
ThreadLocalUtil.set(parseToken);
return true;
} catch (Exception e) {
response.setStatus(401);
return false;
}
2、在需要用户信息来进行请求的接口
java
@GetMapping("/userInfo")
public Result<User> getUserInfo(){
Map<String,Object> parseToken=ThreadLocalUtil.get();
String username=(String)parseToken.get("username");
User user = userService.findByUsername(username);
return Result.success(user);
}
3、请求结束后,移除ThreadLocal
使用响应拦截器
java
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ThreadLocalUtil.remove();
}
这样子减少了重复从请求头获取token的代码
9、自定义校验注解
例如:需要给state(上架状态)设置校验规则,需要自定义
1、anno -> State 创建一个@interface State 注解
java
import com.hdq.validation.StateValidation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {StateValidation.class})
@Retention(RUNTIME)
@Target({FIELD})
public @interface State {
String message() default "state的值只能是'已发布'或'草稿'";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2、validiation -> StateValidation 为注解State设置校验规则
java
import com.hdq.anno.State;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class StateValidation implements ConstraintValidator<State,String> {
/**
*
* @param value 校验的数据
* @param context
* @return
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
System.out.println(value);
if(value==null) return false;
if(value.equals("已发布")||value.equals("草稿")) return true;
return false;
}
}
3、添加@Validate
java
@PostMapping
public Result insert(@RequestBody @Validated Article article){
articleService.insert(article);
return Result.success();
}
10、使用pageHelper实现分页查询功能
1、引入pageHelper
xml
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
2、准备一个实体类PageBean
PageBean用来存放分页查询的结果
java
package com.hdq.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
//分页返回结果对象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean <T>{
private Long total;//总条数
private List<T> items;//当前页数据集合
}
3、编写controller层
java
@GetMapping("/page")
// 分页查询文章
public Result<PageBean<Article>> getPageList(
Integer current,
Integer size,
@RequestParam(required = false) Integer categoryId,
@RequestParam(required = false) String state
){
try {
PageBean<Article> pb=articleService.getPageList(current,size,categoryId,state);
return Result.success(pb);
} catch (Exception e) {
return Result.error("查询失败");
}
}
4、编写service -> impl 的分页功能
java
@Override
public PageBean<Article> getPageList(Integer current, Integer size, Integer categoryId, String state) {
// 数据存放容器
PageBean<Article> pb=new PageBean<>();
// 分页查询
PageHelper.startPage(current,size);
// 获取user_id
Map<String,Object> claims=ThreadLocalUtil.get();
Integer id=(Integer) claims.get("id");
// 传递参数mapper,返回数据
List<Article> as=articleMapper.getPageList(id,categoryId,state);
// 强转
Page<Article> p=(Page<Article>) as;
pb.setTotal(p.getTotal());
pb.setItems(p.getResult());
return pb;
}
5、mapper
java
List<Article> getPageList(Integer id, Integer categoryId, String state);
6、映射到resource -> ...对应的目录下的文件
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hdq.mapper.ArticleMapper">
<select id="getPageList" resultType="com.hdq.pojo.Article">
select * from article
<where>
<if test="categoryId!=null">
category_id=#{categoryId}
</if>
<if test="state!=null">
and state=#{state}
</if>
and create_user=#{id}
</where>
</select>
</mapper>
11、本地存储复制文件
1、新建FileUploadController类
java
@PostMapping("/upload")
public Result<String> upload(MultipartFile file) throws IOException {
// 获取文件名
String originalFilename = file.getOriginalFilename();
// 生成唯一文件名
String name = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
System.out.println(name);
// 本地存储
file.transferTo(new File("E:\\图片\\"+name));
return Result.success("ss");
}