基于SpringBoot+Vue开发的前后端分离博客项目-Java后端接口开发
文章目錄
- 1. 前言
- 2. 新建Springboot項目
- 3. 整合mybatis plus
- 第一步:導依賴
- 第二步:寫配置文件
- 第三步:mapper掃描+分頁插件
- 第四步:代碼生成配置
- 第五步:執(zhí)行代碼生成
- 4. 統(tǒng)一結果封裝
- 5. 邏輯整理
- 5.1. 登錄邏輯
- 5.2. 用戶訪問后端邏輯
- 1.6. 整合shiro+jwt+會話共享
- 6.1. 導入pom依賴
- 6.2. 編寫配置
- 6.4. AccountRealm
- 6.5. JwtToken
- 6.6. JwtUtils
- 6.7. AccountProfile
- 6.8. shiro redis配置
- 6.9. JwtFilter
- 6.10. 權限注解測試
- 7. 全局異常處理
- 8. 實體校驗
- 9. 跨域問題
- 9.1.全局跨域處理
- 9.2. jwtfliter 之前跨域
- 10.登錄接口開發(fā)
- 10.1. 登錄對象封裝
- 10.2. 用戶接口
- 10.3. 全局異常豐富
- 11.博客接口開發(fā)
- 11.1. 接口開發(fā)
- 11.2.實體類校驗
- 11.3. 博文接口測試
- 12. 后端接口開發(fā)總結
作者:呂一明
項目代碼:https://github.com/MarkerHub/vueblog
項目視頻:https://www.bilibili.com/video/BV1PQ4y1P7hZ/
1. 前言
從零開始搭建一個項目骨架,最好選擇合適,熟悉的技術,并且在未來易拓展,適合微服務化體系等。所以一般以Springboot作為我們的框架基礎,這是離不開的了。
然后數(shù)據(jù)層,我們常用的是Mybatis,易上手,方便維護。但是單表操作比較困難,特別是添加字段或減少字段的時候,比較繁瑣,所以這里我推薦使用Mybatis Plus為簡化開發(fā)而生,為簡化開發(fā)而生,只… CRUD 操作,從而節(jié)省大量時間。
作為一個項目骨架,權限也是我們不能忽略的,Shiro配置簡單,使用也簡單,所以使用Shiro作為我們的的權限。
考慮到項目可能需要部署多臺,這時候我們的會話等信息需要共享,Redis是現(xiàn)在主流的緩存中間件,也適合我們的項目。
然后因為前后端分離,所以我們使用jwt作為我們用戶身份憑證。
ok,我們現(xiàn)在就開始搭建我們的項目腳手架!
技術棧:
- SpringBoot
- mybatis plus
- shiro
- lombok
- redis
- hibernate validatior
- jwt
導圖:http://www.markerhub.com/map/131
2. 新建Springboot項目
這里,我們使用IDEA來開發(fā)我們項目,新建步驟比較簡單,我們就不截圖了。
開發(fā)工具與環(huán)境:
- idea
- mysql
- jdk 8
- maven3.3.9
新建好的項目結構如下,SpringBoot版本使用的目前最新的2.2.6.RELEASE版本
pom.xml
- devtools:項目的熱加載重啟插件
- lombok:簡化代碼的工具
3. 整合mybatis plus
接下來,我們來整合mybatis plus,讓項目能完成基本的增刪改查操作。步驟很簡單:可以去官網(wǎng)看看:https://mp.baomidou.com/guide/install.html
第一步:導依賴
pom中導入mybatis plus的jar包,因為后面會涉及到代碼生成,所以我們還需要導入頁面模板引擎,這里我們用的是freemarker。
<!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version></dependency><!--mp代碼生成器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.2.0</version></dependency><!--模板引擎--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency>第二步:寫配置文件
# DataSource Config spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: root mybatis-plus:mapper-locations: classpath*:/mapper/**Mapper.xml上面除了配置數(shù)據(jù)庫的信息,還配置了myabtis plus的mapper的xml文件的掃描路徑,這一步不要忘記了。
第三步:mapper掃描+分頁插件
開啟mapper接口掃描,添加分頁插件
新建一個config包:通過@mapperScan注解指定要變成實現(xiàn)類的接口所在的包,然后包下面的所有接口在編譯之后都會生成相應的實現(xiàn)類。PaginationInterceptor是一個分頁插件。
- com.gblfy.config.MybatisPlusConfig
第四步:代碼生成配置
如果你沒再用其他插件,那么現(xiàn)在就已經(jīng)可以使用mybatis plus了,官方給我們提供了一個代碼生成器,然后我寫上自己的參數(shù)之后,就可以直接根據(jù)數(shù)據(jù)庫表信息生成entity、service、mapper等接口和實現(xiàn)類。
- com.gblfy.CodeGenerator
首先我在數(shù)據(jù)庫中新建了一個user表:
CREATE TABLE `m_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`username` varchar(64) DEFAULT NULL,`avatar` varchar(255) DEFAULT NULL,`email` varchar(64) DEFAULT NULL,`password` varchar(64) DEFAULT NULL,`status` int(5) NOT NULL,`created` datetime DEFAULT NULL,`last_login` datetime DEFAULT NULL,PRIMARY KEY (`id`),KEY `UK_USERNAME` (`username`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `m_blog` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`user_id` bigint(20) NOT NULL,`title` varchar(255) NOT NULL,`description` varchar(255) NOT NULL,`content` longtext,`created` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,`status` tinyint(4) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4; INSERT INTO `vueblog`.`m_user` (`id`, `username`, `avatar`, `email`, `password`, `status`, `created`, `last_login`) VALUES ('1', 'markerhub', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', NULL, '96e79218965eb72c92a549dd5a330112', '0', '2020-04-20 10:44:01', NULL);第五步:執(zhí)行代碼生成
運行CodeGenerator的main方法,輸入表名:m_user,m_blog生成結果如下:
簡潔!方便!經(jīng)過上面的步驟,基本上我們已經(jīng)把mybatis plus框架集成到項目中了。
在UserController中寫個測試:
@RestController @RequestMapping("/user") public class UserController {@Autowiredprivate UserService userService;@GetMapping("/{id}")public Object test(@PathVariable("id") Long id) {return userService.getById(id);}}訪問:http://localhost:8080/user/1 獲得結果如下,整合成功!
4. 統(tǒng)一結果封裝
這里我們用到了一個Result的類,這個用于我們的異步統(tǒng)一返回的結果封裝。一般來說,結果里面有幾個要素必要的
- 是否成功,可用code表示(如200表示成功,400表示異常)
- 結果消息
- 結果數(shù)據(jù)
所以可得到封裝如下:
- com.gblfy.common.lang.Result
test方法改造
/*** 測試地址:http://localhost:8080/user/1* @param id* @return*/@GetMapping("/{id}")public Result test(@PathVariable("id") Long id) {User user = userService.getById(id);return Result.succ(user);}5. 邏輯整理
5.1. 登錄邏輯
-
1.用戶輸入用戶名密碼登錄
-
2.登錄請求后端,會對前端傳過來的用戶名密碼進行校驗,
-
1>用戶名或密碼錯誤就拋出異常,全局異常類就會捕獲此異常,進行統(tǒng)一異常處理
-
2>用戶名或密碼正確,就生成jwt用戶身份憑證
-
-
3.后端生成的jwt用戶身份憑證返回前端,訪問后端資源接口時,用戶攜帶者jwt用戶身份憑證
5.2. 用戶訪問后端邏輯
-
1.用戶訪問后端資源接口,首先被JwtFilter統(tǒng)一攔截
-
2.JwtFilter處理
-
1>攜帶jwt用戶身份憑證,shiro登錄處理
-
①攜帶jwt過期
-
? ②jwt不正確
-
③jwt用戶身份憑證正確,訪問某資源接口
-
-
? 以上不正常場景,都會拋出異常,由全局異常處理類捕獲處理
-
2>無jwt用戶身份憑證,訪問某資源接口
-
3.當訪問某一資源接口之前,接口方法上會有角色或者資源權限的注解過濾
-
? 1>校驗通過,正常獲取資源,返回后端數(shù)據(jù)給用戶
-
? 2>校驗不通過,拋出異常,由全局異常處理類捕獲處理
-
1.6. 整合shiro+jwt+會話共享
考慮到后面可能需要做集群、負載均衡等,所以就需要會話共享,而shiro的緩存和會話信息,我們一般考慮使用redis來存儲這些數(shù)據(jù),所以,我們不僅僅需要整合shiro,同時也需要整合redis。在開源的項目中,我們找到了一個starter可以快速整合shiro-redis,配置簡單,這里也推薦大家使用。
而因為我們需要做的是前后端分離項目的骨架,所以一般我們會采用token或者jwt作為跨域身份驗證解決方案。所以整合shiro的過程中,我們需要引入jwt的身份驗證過程。
那么我們就開始整合:
我們使用一個shiro-redis-spring-boot-starter的jar包,具體教程可以看官方文檔:
https://github.com/alexxiyang/shiro-redis/blob/master/docs/README.md#spring-boot-starter
6.1. 導入pom依賴
導入shiro-redis的starter包:還有jwt的工具包,以及為了簡化開發(fā),我引入了hutool工具包。
<!--shiro-redis--><dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis-spring-boot-starter</artifactId><version>3.2.1</version></dependency><!-- hutool工具類--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.3</version></dependency><!-- jwt --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>6.2. 編寫配置
- com.gblfy.config.ShiroConfig
上面ShiroConfig,我們主要做了幾件事情:
引入RedisSessionDAO和RedisCacheManager,為了解決shiro的權限數(shù)據(jù)和會話信息能保存到redis中,實現(xiàn)會話共享。
重寫了SessionManager和DefaultWebSecurityManager,同時在DefaultWebSecurityManager中為了關閉shiro自帶的session方式,我們需要設置為false,這樣用戶就不再能通過session方式登錄shiro。后面將采用jwt憑證登錄。
在ShiroFilterChainDefinition中,我們不再通過編碼形式攔截Controller訪問路徑,而是所有的路由都需要經(jīng)過JwtFilter這個過濾器,然后判斷請求頭中是否含有jwt的信息,有就登錄,沒有就跳過。跳過之后,有Controller中的shiro注解進行再次攔截,比如@RequiresAuthentication,這樣控制權限訪問。
那么,接下來,我們聊聊ShiroConfig中出現(xiàn)的AccountRealm,還有JwtFilter。
6.4. AccountRealm
AccountRealm是shiro進行登錄或者權限校驗的邏輯所在,算是核心了,我們需要重寫3個方法,分別是
- supports:為了讓realm支持jwt的憑證校驗
- doGetAuthorizationInfo:權限校驗
- doGetAuthenticationInfo:登錄認證校驗
我們先來總體看看AccountRealm的代碼,然后逐個分析:
- com.gblfy.shiro.AccountRealm
其實主要就是doGetAuthenticationInfo登錄認證這個方法,可以看到我們通過jwt獲取到用戶信息,判斷用戶的狀態(tài),最后異常就拋出對應的異常信息,否者封裝成SimpleAuthenticationInfo返回給shiro。 接下來我們逐步分析里面出現(xiàn)的新類:
1、shiro默認supports的是UsernamePasswordToken,而我們現(xiàn)在采用了jwt的方式,所以這里我們自定義一個JwtToken,來完成shiro的supports方法。
6.5. JwtToken
com.gblfy.shiro.JwtToken
package com.gblfy.shiro;import org.apache.shiro.authc.AuthenticationToken;public class JwtToken implements AuthenticationToken {private String token;public JwtToken(String jwt) {this.token = jwt;}@Overridepublic Object getPrincipal() {return token;}@Overridepublic Object getCredentials() {return token;} }6.6. JwtUtils
是個生成和校驗jwt的工具類,其中有些jwt相關的密鑰信息是從項目配置文件中配置的:
package com.gblfy.util;import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;import java.util.Date;/*** jwt工具類*/ @Slf4j @Data @Component @ConfigurationProperties(prefix = "gblfy.jwt") public class JwtUtils {private String secret;private long expire;private String header;/*** 生成jwt token*/public String generateToken(long userId) {Date nowDate = new Date();//過期時間Date expireDate = new Date(nowDate.getTime() + expire * 1000);return Jwts.builder().setHeaderParam("typ", "JWT").setSubject(userId + "").setIssuedAt(nowDate).setExpiration(expireDate).signWith(SignatureAlgorithm.HS512, secret).compact();}/*** 校驗jwt是否合法** @param token* @return*/public Claims getClaimByToken(String token) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();} catch (Exception e) {log.debug("validate is token error ", e);return null;}}/*** token是否過期** @return true:過期*/public boolean isTokenExpired(Date expiration) {return expiration.before(new Date());} }6.7. AccountProfile
而在AccountRealm我們還用到了AccountProfile,這是為了登錄成功之后返回的一個用戶信息的載體,AccountProfile
- com.gblfy.shiro.AccountProfile
6.8. shiro redis配置
第三步,ok,基本的校驗的路線完成之后,我們需要少量的基本信息配置:
shiro-redis:enabled: trueredis-manager:host: 127.0.0.1:6379 markerhub:jwt:# 加密秘鑰secret: f4e2e52034348f86b67cde581c0f9eb5# token有效時長,7天,單位秒expire: 604800header: token第四步:另外,如果你項目有使用spring-boot-devtools,需要添加一個配置文件,在resources目錄下新建文件夾META-INF,然后新建文件spring-devtools.properties,這樣熱重啟時候才不會報錯。
- resources/META-INF/spring-devtools.properties
6.9. JwtFilter
第五步:定義jwt的過濾器JwtFilter。
這個過濾器是我們的重點,這里我們繼承的是Shiro內(nèi)置的AuthenticatingFilter,一個可以內(nèi)置了可以自動登錄方法的的過濾器,有些同學繼承BasicHttpAuthenticationFilter也是可以的。
我們需要重寫幾個方法:
下面我們看看總體的代碼:
- com.gblfy.shiro.JwtFilter
那么到這里,我們的shiro就已經(jīng)完成整合進來了,并且使用了jwt進行身份校驗。
6.10. 權限注解測試
/*** 測試地址:http://localhost:8080/user/1* @param id* @return*/@RequiresAuthentication@GetMapping("/{id}")public Result test(@PathVariable("id") Long id) {User user = userService.getById(id);return Result.succ(user);}7. 全局異常處理
有時候不可避免服務器報錯的情況,如果不配置異常處理機制,就會默認返回tomcat或者nginx的5XX頁面,對普通用戶來說,不太友好,用戶也不懂什么情況。這時候需要我們程序員設計返回一個友好簡單的格式給前端。
處理辦法如下:通過使用@ControllerAdvice來進行統(tǒng)一異常處理,@ExceptionHandler(value = RuntimeException.class)來指定捕獲的Exception各個類型異常 ,這個異常的處理,是全局的,所有類似的異常,都會跑到這個地方處理。
- com.gblfy.common.exception.GlobalExceptionHandler
步驟二、定義全局異常處理,@ControllerAdvice表示定義全局控制器異常處理,@ExceptionHandler表示針對性異常處理,可對每種異常針對性處理。
package com.gblfy.common.exception;import com.gblfy.common.lang.Result; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.ShiroException; import org.springframework.http.HttpStatus; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice;import java.io.IOException;@Slf4j @RestControllerAdvice public class GlobalExceptionHandler {/*** 處理Assert斷言的異常*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = IllegalArgumentException.class)public Result handler(IllegalArgumentException e) throws IOException {log.error("Assert異常:-------------->{}", e.getMessage());return Result.fail(e.getMessage());}/*** @Validated 校驗錯誤異常處理*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = MethodArgumentNotValidException.class)public Result handler(MethodArgumentNotValidException e) throws IOException {log.error("運行時異常:-------------->", e);BindingResult bindingResult = e.getBindingResult();ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();return Result.fail(objectError.getDefaultMessage());}/*** Shiro異常捕獲** @param e* @return*/@ResponseStatus(HttpStatus.UNAUTHORIZED)@ExceptionHandler(value = ShiroException.class)public Result handler(ShiroException e) {log.error("運行時異常:----------------{}", e);return Result.fail(401, e.getMessage(), null);}/*** 運行時異常捕獲** @param e* @return*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = RuntimeException.class)public Result handler(RuntimeException e) {log.error("運行時異常:----------------{}", e);return Result.fail(e.getMessage());}}上面我們捕捉了幾個異常:
- ShiroException:shiro拋出的異常,比如沒有權限,用戶登錄異常
- IllegalArgumentException:處理Assert的異常
- MethodArgumentNotValidException:處理實體校驗的異常
- RuntimeException:捕捉其他異常
8. 實體校驗
當我們表單數(shù)據(jù)提交的時候,前端的校驗我們可以使用一些類似于jQuery Validate等js插件實現(xiàn),而后端我們可以使用Hibernate validatior來做校驗。
我們使用springboot框架作為基礎,那么就已經(jīng)自動集成了Hibernate validatior。
那么用起來啥樣子的呢?
第一步:首先在實體的屬性上添加對應的校驗規(guī)則,比如:
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("m_user") public class User implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Long id;@NotBlank(message = "昵稱不能為空")private String username;private String avatar;@NotBlank(message = "郵箱不能為空")@Email(message = "郵箱格式不正確")private String email;private String password;private Integer status;private LocalDateTime created;private LocalDateTime lastLogin; }測試方法
/*** 測試實體校驗** @param user* @return*/@PostMapping("save")public Result save(@Validated @RequestBody User user) {return Result.succ(user);}postman 測試
http://localhost:8080/user/save
{ "username":"gblfy","email": "xxxx" }9. 跨域問題
因為是前后端分析,所以跨域問題是避免不了的,我們直接在后臺進行全局跨域處理:
9.1.全局跨域處理
- com.gblfy.config.CorsConfig
9.2. jwtfliter 之前跨域
在JwtFilter勒種添加此方法,解決跨域處理
/*** 在JwtFilter處理邏輯之前,進行跨域處理** @param request* @param response* @return* @throws Exception*/@Overrideprotected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpServletRequest = WebUtils.toHttp(request);HttpServletResponse httpServletResponse = WebUtils.toHttp(response);httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));// 跨域時會首先發(fā)送一個OPTIONS請求,這里我們給OPTIONS請求直接返回正常狀態(tài)if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value());return false;}return super.preHandle(request, response);}ok,因為我們系統(tǒng)開發(fā)的接口比較簡單,所以我就不集成swagger2啦,也比較簡單而已。下面我們就直接進入我們的正題,進行編寫登錄接口。
10.登錄接口開發(fā)
10.1. 登錄對象封裝
登錄的邏輯其實很簡答,只需要接受賬號密碼,然后把用戶的id生成jwt,返回給前段,為了后續(xù)的jwt的延期,所以我們把jwt放在header上。具體代碼如下:
- com.gblfy.controller.AccountController
10.2. 用戶接口
package com.gblfy.controller;import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; import cn.hutool.crypto.SecureUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.gblfy.common.dto.LoginDto; import com.gblfy.common.lang.Result; import com.gblfy.entity.User; import com.gblfy.service.UserService; import com.gblfy.util.JwtUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;@RestController public class AccountController {@AutowiredUserService userService;@AutowiredJwtUtils jwtUtils;/*** 用戶登錄接口** @param loginDto* @param response* @return*/@PostMapping("/login")public Result login(@Validated @RequestBody LoginDto loginDto, HttpServletResponse response) {User user = userService.getOne(new QueryWrapper<User>().eq("username", loginDto.getUsername()));//使用Assert斷言判斷用戶存不存在 會拋出IllegalArgumentException,需要在全局異常捕獲處理Assert.notNull(user, "用戶不存在!");if (!user.getPassword().equals(SecureUtil.md5(loginDto.getPassword()))) {return Result.fail("密碼不正確!");}//校驗通過生成jwtString jwt = jwtUtils.generateToken(user.getId());//將jwt放入headerresponse.setHeader("Authorization", jwt);response.setHeader("Access-control-Expose-Headers", "Authorization");return Result.succ(MapUtil.builder().put("id", user.getId()).put("username", user.getUsername()).put("aAvatar", user.getAvatar()).put("email", user.getEmail()).map());}/*** 用戶登出接口** @return*/@RequiresAuthentication@PostMapping("/logout")public Result logout() {SecurityUtils.getSubject().logout();return Result.succ(null);} }注意@RequiresAuthentication說明需要登錄之后才能訪問的接口,其他需要權限的接口可以添加shiro的相關注解。 接口比較簡單,我們就不多說了,基本增刪改查而已。注意的是edit方法是需要登錄才能操作的受限資源。
10.3. 全局異常豐富
在GlobalExceptionHandler類中添加校驗異常和斷言異常處理
/*** 處理Assert斷言的異常*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = IllegalArgumentException.class)public Result handler(IllegalArgumentException e) throws IOException {log.error("Assert異常:-------------->{}", e.getMessage());return Result.fail(e.getMessage());}/*** @Validated 校驗錯誤異常處理*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(value = MethodArgumentNotValidException.class)public Result handler(MethodArgumentNotValidException e) throws IOException {log.error("運行時異常:-------------->", e);BindingResult bindingResult = e.getBindingResult();ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();return Result.fail(objectError.getDefaultMessage());}11.博客接口開發(fā)
11.1. 接口開發(fā)
package com.gblfy.controller;import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.gblfy.common.lang.Result; import com.gblfy.entity.Blog; import com.gblfy.service.BlogService; import com.gblfy.util.ShiroUtil; import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.Assert; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*;import java.time.LocalDateTime;/*** <p>* 前端控制器* </p>** @author gblfy* @since 2020-06-02*/ @RestController public class BlogController {@AutowiredBlogService blogService;/*** 博文列表** @param currentPage* @return*/@GetMapping("/blogs")public Result list(@RequestParam(defaultValue = "1") Integer currentPage) {//當前第幾頁 一頁顯示幾條 默認5條Page page = new Page(currentPage, 5);//設置分頁顯示的條件 例如:邏輯刪除的 排序等 都可以在這里面追加IPage pageData = blogService.page(page, new QueryWrapper<Blog>().orderByDesc("created"));return Result.succ(pageData);}/*** 博文查詢** @param id* @return*/@GetMapping("/blog/{id}")public Result detail(@PathVariable(name = "id") Long id) {Blog blog = blogService.getById(id);Assert.notNull(blog, "該博客已被刪除");return Result.succ(blog);}/*** 博文添加和編輯** @param blog* @return*/@RequiresAuthentication@PostMapping("/blog/edit")public Result edit(@Validated @RequestBody Blog blog) {Blog temp = null;if (blog.getId() != null) {temp = blogService.getById(blog.getId());// 只能編輯自己的文章System.out.println(ShiroUtil.getProfile().getId());Assert.isTrue(temp.getUserId().longValue() == ShiroUtil.getProfile().getId().longValue(), "沒有權限編輯");} else {temp = new Blog();temp.setUserId(ShiroUtil.getProfile().getId());temp.setCreated(LocalDateTime.now());temp.setStatus(0);}BeanUtil.copyProperties(blog, temp, "id", "userId", "created", "status");blogService.saveOrUpdate(temp);return Result.succ(null);}}11.2.實體類校驗
package com.gblfy.entity;import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; import java.io.Serializable;import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;import javax.validation.constraints.NotBlank;/*** <p>** </p>** @author gblfy* @since 2020-06-02*/ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("m_blog") public class Blog implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Long id;private Long userId;@NotBlank(message = "標題不能為空")private String title;@NotBlank(message = "摘要不能為空")private String description;@NotBlank(message = "內(nèi)容不能為空")private String content;@JsonFormat(pattern="yyyy-MM-dd")private LocalDateTime created;private Integer status;}11.3. 博文接口測試
GET http://localhost:8080/blogs
GET http://localhost:8080/blog/10
POST http://localhost:8080/blog/edit
從上面測試得出@Validated注解的優(yōu)先級高于@RequiresAuthentication注解
2.測試請求頭中不攜帶token
3.測試請求頭中攜帶token測試
注:這個Authorization 的值是通過登錄接口返回給用戶的,簡言之,要先訪問http://localhost:8080/login接口獲取token的值
4.攜帶token添加博文測試
無id即可
5.攜帶token編輯博文測試
特別注意:Authorization后面不能有空格,token的值要復制全,這個很重要,這個錯誤不好定位!
12. 后端接口開發(fā)總結
總結
以上是生活随笔為你收集整理的基于SpringBoot+Vue开发的前后端分离博客项目-Java后端接口开发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot3.x 集成持久层框
- 下一篇: vue-cli多环境配置