git項目地址
git項目地址
https://github.com/MrITzhongzi/blog-system.git
邏輯梳理
- 每個用戶可以發表多個文章
- 每個文章都有自己的圖片
- 文章有自己的點贊,瀏覽量,評論數
遇到的坑
- 繼承
WebMvcConfigurationSupport
導致springboot靜態資源無法訪問
因爲要設置api的攔截器,所以需要繼承這個類WebMvcConfigurationSupport並重寫裏面的addInterceptors方法,如下所示:
@Configuration
public class IntercepterConfig extends WebMvcConfigurationSupport {
@Resource
private LoginIntercepter loginIntercepter;
/**
* 配置api接口過濾器
* @param registry
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
List<String> excludePath = new ArrayList<>();
excludePath.add("/api/user/register");
excludePath.add("/api/user/login");
//一下是爲了測試臨時改動
excludePath.add("/api/img/**");
List<String> includePath = new ArrayList<>();
includePath.add("/api/**");
registry.addInterceptor(loginIntercepter)
.addPathPatterns(includePath)
.excludePathPatterns(excludePath);
super.addInterceptors(registry);
}
}
繼承這個類會導致springboot內部設置的靜態資源訪問路徑被重製,所以需要重新設置一下,不然靜態資源無法訪問,如下所示:
package com.lhw.blog.config;
import com.lhw.blog.intercepter.LoginIntercepter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @description:
* @author: lihongwei
* @time: 2020/4/10 3:04 下午
*/
@Configuration
public class IntercepterConfig extends WebMvcConfigurationSupport {
@Resource
private LoginIntercepter loginIntercepter;
/**
* 配置api接口過濾器
* @param registry
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
List<String> excludePath = new ArrayList<>();
excludePath.add("/api/user/register");
excludePath.add("/api/user/login");
//一下是爲了測試臨時改動
excludePath.add("/api/img/**");
List<String> includePath = new ArrayList<>();
includePath.add("/api/**");
registry.addInterceptor(loginIntercepter)
.addPathPatterns(includePath)
.excludePathPatterns(excludePath);
super.addInterceptors(registry);
}
/**
* 重新配置可以訪問靜態資源路徑,不配置這個 會導致靜態資源無法訪問
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//重新配置靜態資源
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/resources/")
.addResourceLocations("classpath:/static/")
.addResourceLocations("classpath:/public/");
super.addResourceHandlers(registry);
}
}
- springboot @Value注入失敗問題
- 不能作用於靜態變量(static);
- 不能作用於常量(final);
- 不能在非註冊的類中使用(類需要被註冊在spring上下文中,如用@Service,@RestController,@Component等);
- 使用這個類時,只能通過依賴注入的方式,用new的方式是不會自動注入這些配置的。
參考文章
https://blog.csdn.net/ITzhongzi/article/details/105489035
只要用到@Value的類,只能用註解的方式生成實例。不能用new
pom文件配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lhw</groupId>
<artifactId>blog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>blog</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 引入mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- MySQL的JDBC驅動包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version> 8.0.19</version>
</dependency>
<!-- JWT工具類 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!-- json工具 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
<!--七牛雲-->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.2.0, 7.2.99]</version>
</dependency>
<!-- io 工具包-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
用到的工具類
- MultipartFile和 File類轉換
package com.lhw.blog.tool;
import org.apache.commons.io.IOUtils;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
public class FileUtils {
public static MultipartFile file2MultipartFile(File file){
try {
FileInputStream fileInputStream = new FileInputStream(file);
MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain",IOUtils.toByteArray(fileInputStream));
// InputStream inputStream = multipartFile.getInputStream();
return multipartFile;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static File multipartFile2File(MultipartFile multipartFile){
File file = null;
InputStream inputStream = null;
try {
inputStream = multipartFile.getInputStream();
file = new File(multipartFile.getOriginalFilename());
inputStreamToFile(inputStream, file);
return file;
} catch (Exception e) {}
return null;
}
public static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 七牛圖片服務器工具
package com.lhw.blog.tool;
import com.google.gson.Gson;
import com.lhw.blog.config.QiniuConfig;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.URLEncoder;
/**
* @description:
* @author: lihongwei
* @time: 2020/4/10 4:05 下午
*/
@Component
public class QiniuUtils {
@Autowired
private QiniuConfig qiniuConfig;
/**
*
* 上傳圖片到七牛雲
* @param file
* @return
*/
public DefaultPutRet uploadFile(File file) {
//構造一個帶指定 Region 對象的配置類
Configuration cfg = new Configuration(Region.autoRegion());
//...其他參數參考類註釋
UploadManager uploadManager = new UploadManager(cfg);
//...生成上傳憑證,然後準備上傳
String accessKey = qiniuConfig.accessKey;
String secretKey = qiniuConfig.secretKey;
String bucket = qiniuConfig.bucket;
String suffix = file.getName().substring(file.getName().lastIndexOf("."));
//默認不指定key的情況下,以文件內容的hash值作爲文件名 (這裏的key就是存在七牛雲上的文件名)
String key = UuidUtils.getUUID() + suffix;
try {
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
InputStream is = new FileInputStream(file);
try {
Response response = uploadManager.put(is, key, upToken, null, null);
//解析上傳成功的結果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);
System.out.println(putRet.hash);
return putRet;
} catch (QiniuException ex) {
Response r = ex.response;
System.err.println(r.toString());
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
//ignore
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 獲取七牛雲私有空間的圖片路徑
* @param filename
* @return
*/
public String getPrivateImagePath(String filename){
//"1.png"
String fileName = filename;
String domainOfBucket = "http://q8eezq1qm.bkt.clouddn.com";
String encodedFileName = null;
try {
encodedFileName = URLEncoder.encode(fileName, "utf-8").replace("+", "%20");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String publicUrl = String.format("%s/%s", domainOfBucket, encodedFileName);
String accessKey = qiniuConfig.accessKey;
String secretKey = qiniuConfig.secretKey;
Auth auth = Auth.create(accessKey, secretKey);
//1小時,可以自定義鏈接過期時間
long expireInSeconds = 3600;
String finalUrl = auth.privateDownloadUrl(publicUrl, expireInSeconds);
System.out.println(finalUrl);
return "abc";
}
/**
* 獲取七牛雲共有空間的圖片路徑
* @param filename
* @return
*/
public String getPublicImagePath(String filename){
String fileName = filename;
String domainOfBucket = "http://q8pvrckr7.bkt.clouddn.com";
String finalUrl = String.format("%s/%s", domainOfBucket, fileName);
System.out.println(finalUrl);
return "";
}
}
- 獲取uuid工具
package com.lhw.blog.tool;
import java.util.UUID;
public class UuidUtils {
public static String getUUID(){
return UUID.randomUUID().toString().replaceAll("-", "");
}
}
增加了過濾器 (驗證token)
- 定義過濾器
package com.lhw.blog.intercepter;
import com.google.gson.Gson;
import com.lhw.blog.tool.JWTUtils;
import com.lhw.blog.tool.JsonBuilder;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @description: 進入controller之前進行攔截
* @author: lihongwei
* @time: 2020/4/10 2:45 下午
*/
@Component
public class LoginIntercepter implements HandlerInterceptor {
private static final Gson gson = new Gson();
/**
* 進入controller之前進行攔截,檢查token是否合法
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if(token == null) {
token = request.getParameter("token");
}
if(token != null) {
Claims claims = JWTUtils.checkJWT(token);
if(claims != null) {
Integer userId = (Integer) claims.get("id");
request.setAttribute("user_id", userId);
return true;
}
}
sendJsonMessage(response, JsonBuilder.buildError("請登錄"));
return false;
}
/**
* 響應給前臺數據
* @param response
* @param obj
*/
public static void sendJsonMessage(HttpServletResponse response, Object obj){
response.setContentType("application/json; charset=utf-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.println(gson.toJson(obj));
writer.close();
response.flushBuffer();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 使用過濾器
package com.lhw.blog.config;
import com.lhw.blog.intercepter.LoginIntercepter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @description:
* @author: lihongwei
* @time: 2020/4/10 3:04 下午
*/
@Configuration
public class IntercepterConfig extends WebMvcConfigurationSupport {
@Resource
private LoginIntercepter loginIntercepter;
/**
* 配置api接口過濾器
* @param registry
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
List<String> excludePath = new ArrayList<>();
excludePath.add("/api/user/register");
excludePath.add("/api/user/login");
//一下是爲了測試臨時改動
excludePath.add("/api/img/**");
List<String> includePath = new ArrayList<>();
includePath.add("/api/**");
registry.addInterceptor(loginIntercepter)
.addPathPatterns(includePath)
.excludePathPatterns(excludePath);
super.addInterceptors(registry);
}
/**
* 重新配置可以訪問靜態資源路徑,不配置這個 會導致靜態資源無法訪問
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//重新配置靜態資源
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/resources/")
.addResourceLocations("classpath:/static/")
.addResourceLocations("classpath:/public/");
super.addResourceHandlers(registry);
}
}
增加了跨域配置
package com.lhw.blog.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @description:
* @author: lihongwei
* @time: 2020/4/10 4:54 下午
*/
@Configuration
public class Cors extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //所有路徑 允許跨域
.allowedOrigins("*") // 允許某個域名下
.allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH") // 允許跨域的方法
.allowCredentials(true) //
.maxAge(3600); //有效期
}
}
controller層新增api
- 文章controller
package com.lhw.blog.controller;
import com.lhw.blog.domain.LhwArticles;
import com.lhw.blog.service.ArticleService;
import com.lhw.blog.tool.JsonBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* @description:
* @author: lihongwei
* @time: 2020/4/10 11:13 上午
*/
@RestController
@RequestMapping("/api/article")
public class ArticleController {
@Resource
private ArticleService articleService;
/**
* 發表文章 api
* @return
*/
@RequestMapping(path = "/publish", method = RequestMethod.POST)
public JsonBuilder publishAticle(@RequestParam(value = "title") String title,
@RequestParam(value = "content") String content,
HttpServletRequest request){
//查詢用戶的相關信息
Integer userId = (Integer)request.getAttribute("user_id");
LhwArticles article = new LhwArticles();
article.setUserId(userId);
article.setArticleTitle(title);
article.setArticleContent(content);
try {
int row = articleService.insertArticle(article);
if(row == 0) {
return JsonBuilder.buildError("文章插入失敗");
}
return JsonBuilder.buildSuccess("文章插入成功");
} catch (Exception e){}
return JsonBuilder.buildError("文章插入失敗");
}
}
- 圖片controller
package com.lhw.blog.controller;
import com.lhw.blog.tool.FileUtils;
import com.lhw.blog.tool.JsonBuilder;
import com.lhw.blog.tool.QiniuUtils;
import com.qiniu.storage.model.DefaultPutRet;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
/**
* @description: 圖片上傳 api
* @author: lihongwei
* @time: 2020/4/10 4:05 下午
*/
@RestController
@RequestMapping("/api/img")
public class ImageController {
@Autowired
private QiniuUtils qiniuUtils;
@RequestMapping(path = "/upload_img", method = RequestMethod.POST)
public JsonBuilder uploadFile(@RequestParam("img") MultipartFile file) {
File requestFile = FileUtils.multipartFile2File(file);
DefaultPutRet defaultPutRet = qiniuUtils.uploadFile(requestFile);
if(defaultPutRet != null) {
return JsonBuilder.buildSuccess("上傳成功", defaultPutRet);
}
return JsonBuilder.buildError("上傳失敗");
}
}