目錄
用戶登錄
添加圖書
圖書列表
修改圖書
刪除圖書
批量刪除
攔截器
🍃前言
什么是攔截器?
攔截器的基本使用
自定義攔截器
注冊配置攔截器
攔截路徑
攔截器執行流程
項目實現統一攔截
定義攔截器
注冊配置攔截器

這里沒圖書管理系統之前模板的可以參考我之前博客[SpringBoot]Spring MVC(6.0)----圖書管理系統(初)-CSDN博客
在那基礎上,我們繼續進行代碼書寫
用戶登錄
第一步引入依賴,以及配置yml文件
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration: # ???? MyBatis ??? SQLlog-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true #??????
# ???? MyBatis ??? SQL
logging:file:name: logs/springboot.log
引入依賴需要我們加上一個插件
然后在pom.xml文件中右鍵
?然后進行選擇即可,這里我們需要選擇我右面的依賴
其次,我們書寫Controller--Service--Mapper結構
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public boolean login(String userName, String password, HttpSession session) {//校驗用戶信息是否合法.if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {return false;}UserInfo userInfo=userService.queryUserByName(userName);if(userInfo==null){return false;}//判斷用戶名和密碼是否正確//理論上應該從數據庫中獲取, 但是目前還沒學習 mybatis, 所以先這么寫.if(userInfo!=null&&password.equals(userInfo.getPassword())) {return true;}userInfo.setPassword("");session.setAttribute("userName",userName);return false;}
}
為什么要把userInfo的密碼設置為空再設置session呢?
因為存儲到session中了.你如果不將密碼設置為空的話,在獲取到session就能看到密碼
import com.example.demo.model.UserInfo;
import com.example.demo.model.UserInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;@Service
public class UserService{@Autowiredprivate UserInfoMapper userInfoMapper;public UserInfo queryUserByName(String name) {return userInfoMapper.queryUserByName(name);}
}
package com.example.demo.model;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface UserInfoMapper {@Select("select * from user_info where delete_flag=0 and user_name = #{name}")UserInfo queryUserByName(String name);
}
這樣我們就可以完成一個登錄,數據庫中存儲信息
同時我們需要修改前端代碼(因為我們將返回類型改變了)
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="css/bootstrap.min.css"><link rel="stylesheet" href="css/login.css"><script type="text/javascript" src="js/jquery.min.js"></script>
</head><body><div class="container-login"><div class="container-pic"><img src="pic/computer.png" width="350px"></div><div class="login-dialog"><h3>登陸</h3><div class="row"><span>用戶名</span><input type="text" name="userName" id="userName" class="form-control"></div><div class="row"><span>密碼</span><input type="password" name="password" id="password" class="form-control"></div><div class="row"><button type="button" class="btn btn-info btn-lg" onclick="login()">登錄</button></div></div></div><script src="js/jquery.min.js"></script><script>function login() {$.ajax({url : "/user/login",type : "post",data : {userName : $("#userName").val(),password : $("#password").val(),},success:function(result) {if(result==false) {alert("用戶名或密碼錯誤,請重新輸入");}else {location.href = "book_list.html";}}});}</script>
</body></html>
添加圖書
@RequestMapping("/addBook")public String addBook(BookInfo bookInfo){System.out.println("添加圖書"+bookInfo);if (!StringUtils.hasLength(bookInfo.getBookName())|| !StringUtils.hasLength(bookInfo.getAuthor())|| bookInfo.getCount()==null|| bookInfo.getPrice()==null|| !StringUtils.hasLength(bookInfo.getPublish())|| bookInfo.getStatus() ==null){return "輸?參數不合法, 請檢查?參!";}try {bookService.addBook(bookInfo);return "";}catch (Exception e){System.out.println("增添錯誤");return e.getMessage();}}
業務層
public void addBook(BookInfo bookInfo) {bookInfoMapper.insertBook(bookInfo);}
數據層:
@Mapper
public interface BookInfoMapper {@Insert("insert into book_info (book_name,author,count,price,publish,status) " + "values (#{bookName},#{author},#{count},#{price},#{publish},#{status})")Integer insertBook(BookInfo bookInfo);
}
前端修改代碼
function add() {$.ajax({type: "post",url: "/book/addBook",data: $("#addBook").serialize(),success: function (result) {if (result == "") {location.href = "book_list.html"} else {console.log(result);alert("添加失敗:" + result);}},error: function (error) {console.log(error);}});}
然后頁面就可以正常訪問了
?前后對比:
圖書列表
1.添加數據
INSERT INTO `book_info` ( book_name, author, count, price, publish )
VALUES
( '圖書2', '作者2', 29, 22.00, '出版社2' ),( '圖書3', '作者2', 29, 22.00, '出版社
3' ),
( '圖書4', '作者2', 29, 22.00, '出版社1' ),( '圖書5', '作者2', 29, 22.00, '出版社
1' ),
( '圖書6', '作者2', 29, 22.00, '出版社1' ),( '圖書7', '作者2', 29, 22.00, '出版社
1' ),
( '圖書8', '作者2', 29, 22.00, '出版社1' ),( '圖書9', '作者2', 29, 22.00, '出版社
1' ),
( '圖書10', '作者2', 29, 22.00, '出版社1'),( '圖書11', '作者2', 29, 22.00, '出版 社1'),
( '圖書12', '作者2', 29, 22.00, '出版社1'),( '圖書13', '作者2', 29, 22.00, '出版 社1'),
( '圖書14', '作者2', 29, 22.00, '出版社1'),( '圖書15', '作者2', 29, 22.00, '出版 社1'),
( '圖書16', '作者2', 29, 22.00, '出版社1'),( '圖書17', '作者2', 29, 22.00, '出版 社1'),
( '圖書18', '作者2', 29, 22.00, '出版社1'),( '圖書19', '作者2', 29, 22.00, '出版 社1'),
( '圖書20', '作者2', 29, 22.00, '出版社1'),( '圖書21', '作者2', 29, 22.00, '出版 社1');
2.了解分頁的sql原理
查找第1到10條
SELECT * FROM book_info LIMIT 0,10
3.算法可知:開始索引 = (當前?碼 - 1) * 每?顯?條數
4.前端在發起查詢請求時,需要向服務端傳遞的參數
????????currentPage 當前?碼 //默認值為1? ? ? ??pageSize 每?顯?條數 //默認值為10
records 所查詢到的數據列表(存儲到List 集合中)?total 總記錄數 (?于告訴前端顯?多少?, 顯??數為: (total + pageSize -1)/pageSize顯??數totalPage 計算公式為 : total % pagesize == 0 ? total / pagesize : (total /pagesize)+1;)
?6.翻?請求和響應部分, 我們通常封裝在兩個對象中
翻頁請求對象
@Data
public class PageRequest {private int currentPage = 1; // 當前?private int pageSize = 10; // 每?中的記錄數 }
我們需要根據currentPage 和pageSize ,計算出來開始索引
所以PageRequest修改為
package com.example.demo.model;import lombok.Data;@Data
public class PageRequest {private int currentPage = 1; // 當前?private int pageSize = 10; // 每?中的記錄數private int offset=1;public int getOffset() {return (currentPage-1) * pageSize;}
}
翻?列表結果類:
import lombok.Data;
import java.util.List;
@Data
public class PageResult<T> {private int total;//所有記錄數private List<T> records; // 當前?數據public PageResult(Integer total, List<T> records) {this.total = total;this.records = records;}
}
7.約定前后端交互接?
package com.example.demo.model;public enum BookStatus {DELETED(0,"?效"),NORMAL(1,"可借閱"),FORBIDDEN(2,"不可借閱");private Integer code;private String name;BookStatus(int code, String name) {this.code = code;this.name = name;}public static BookStatus getNameByCode(Integer code){switch (code){case 0: return DELETED;case 1: return NORMAL;case 2: return FORBIDDEN;}return null;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
8.完善PageResult類
package com.example.demo.model;import lombok.Data;
import java.util.List;
@Data
public class PageResult<T> {public int total;//所有記錄數private List<T> records; // 當前?數據private PageRequest pageRequest;public PageResult(Integer total, PageRequest pageRequest, List<T> records){this.total = total;this.pageRequest = pageRequest;this.records = records;}
}
將PageRequest類引入,便于后面的分頁插件獲取信息
分?組件需要提供?些信息: totalCounts: 總記錄數, pageSize: 每?的個數, visiblePages: 可視?數 ,currentPage: 當前?碼這些信息中, pageSize 和 visiblePages 前端直接設置即可. totalCounts 后端已經提供, currentPage 也 可以從參數中取到, 但太復雜了, 咱們直接由后端返回即可.
9.完善 BookController,BookService,BookInfoMapper
@RequestMapping("/getListByPage")public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {log.info("獲取圖書列表{}",pageRequest);PageResult<BookInfo> pageResult = bookService.getBookListByPage(pageRequest);return pageResult;}
public PageResult<BookInfo> getBookListByPage(PageRequest pageRequest) {Integer count = bookInfoMapper.count();List<BookInfo> books = bookInfoMapper.queryBookListByPage(pageRequest);for (BookInfo book:books){book.setStatusCN(BookStatus.getNameByCode(book.getStatus()).getName());}return new PageResult<>(count,pageRequest, books);}
@Mapper
public interface BookInfoMapper {@Insert("insert into book_info (book_name,author,count,price,publish,status) " + "values (#{bookName},#{author},#{count},#{price},#{publish},#{status})")Integer insertBook(BookInfo bookInfo);@Select("select count(1) from book_info where status<>0")Integer count();@Select("select * from book_info where status !=0 order by id desc limit #{offset}, #{pageSize}")List<BookInfo> queryBookListByPage(PageRequest pageRequest);
}
10.接下來處理分?信息
jQuery 分頁插件 : jqPaginator | 菜鳥教程
使用教程,修改里面的部分值即可
11.編寫前端代碼(前后端交互,個人認為最為難人的部分,需要重點理解)
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>圖書列表展示</title><link rel="stylesheet" href="css/bootstrap.min.css"><link rel="stylesheet" href="css/list.css"><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/bootstrap.min.js"></script><script src="js/jq-paginator.js"></script></head><body><div class="bookContainer"><h2>圖書列表展示</h2><div class="navbar-justify-between"><div><button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加圖書</button><button class="btn btn-outline-info" type="button" onclick="batchDelete()">批量刪除</button></div></div><table><thead><tr><td>選擇</td><td class="width100">圖書ID</td><td>書名</td><td>作者</td><td>數量</td><td>定價</td><td>出版社</td><td>狀態</td><td class="width200">操作</td></tr></thead><tbody></tbody></table><div class="demo"><ul id="pageContainer" class="pagination justify-content-center"></ul></div><script>function getQueryParam(name) {const urlParams = new URLSearchParams(window.location.search);return urlParams.get(name);}// 修復:設置默認參數currentPage = 1,避免初始調用時參數缺失function getBookList(currentPage=1) {$.ajax({type: "get",// 修復:確保參數正確拼接,避免出現undefinedurl: "/book/getListByPage?currentPage=" + currentPage,success: function (result) {console.log(result);if (result != null && result.records) { // 修復:字段名records(原resords拼寫錯誤)var finalHtml = "";// 遍歷圖書數據(修復字段名resords -> records)for (var book of result.records) {finalHtml += '<tr>';finalHtml += '<td><input type="checkbox" name="selectBook" value="' + book.id + '" class="book-select"></td>';finalHtml += '<td>' + book.id + '</td>';finalHtml += '<td>' + book.bookName + '</td>';finalHtml += '<td>' + book.author + '</td>';finalHtml += '<td>' + book.count + '</td>';finalHtml += '<td>' + book.price + '</td>';finalHtml += '<td>' + book.publish + '</td>';finalHtml += '<td>' + book.statusCN + '</td>';finalHtml += '<td><div class="op">';finalHtml += '<a href="book_update.html?bookId=' + book.id + '">修改</a>'; // 修復:鏈接中去除空格(bookId后無空格)finalHtml += '<a href="javascript:void(0)" onclick="deleteBook(' + book.id + ')">刪除</a>';finalHtml += '</div></td>';finalHtml += "</tr>";}$("tbody").html(finalHtml);// 初始化分頁插件$("#pageContainer").jqPaginator({totalCounts: result.total, // 總記錄數(需與后端返回字段一致)pageSize: 10, // 每頁條數visiblePages: 5, // 可見頁碼數currentPage: result.pageRequest.currentPage, // 修復:從返回結果中獲取當前頁碼(根據后端實際字段名調整)first: '<li class="page-item"><a class="page-link">首頁</a></li>',prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一頁</a></li>',next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一頁</a></li>',last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一頁</a></li>',page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}</a></li>',// 修復:分頁切換時直接調用getBookList加載數據,而非跳轉頁面onPageChange: function (Page, type) {if (type != 'init') {location.href = "book_list.html?currentPage=" + Page;// getBookList(Page);}}});}},error: function (xhr) {console.error("請求失敗:", xhr.responseText); // 增加錯誤日志,便于調試}});}// 初始調用時無需傳參(函數已設置默認值)const initialPage = parseInt(getQueryParam("currentPage")) || 1;getBookList(initialPage);// getBookList();function deleteBook(id) {var isDelete = confirm("確認刪除?");if (isDelete) {//刪除圖書alert("刪除成功");}}function batchDelete() {var isDelete = confirm("確認批量刪除?");if (isDelete) {//獲取復選框的idvar ids = [];$("input:checkbox[name='selectBook']:checked").each(function () {ids.push($(this).val());});console.log(ids);alert("批量刪除成功");}}</script></div>
</body></html>
修改圖書
進?修改??, 需要顯?當前圖書的信息
[請求]
/book/queryBookById?bookId=25
[參數] ?[響應] {"id": 25,"bookName": "圖書21","author": "作者2","count": 999,"price": 222.00,"publish": "出版社1","status": 2,"statusCN": null,"createTime": "2023-09-04T04:01:27.000+00:00","updateTime": "2023-09-05T03:37:03.000+00:00"
}
點擊修改按鈕, 修改圖書信息
[請求]
/book/updateBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[參數]
id=1&bookName=圖書1&author=作者1&count=23&price=36&publish=出版社1&status=1
[響應]
"" //失敗信息, 成功時返回空字符串
我們約定, 瀏覽器給服務器發送?個 /book/updateBook 這樣的 HTTP 請求, form表單的形式來 提交數據服務器返回處理結果, 返回""表?添加圖書成功, 否則, 返回失敗信息
實現服務器代碼
BookController代碼
@RequestMapping("/queryBookById")public BookInfo queryBookById(Integer bookId) {if (bookId == null || bookId <= 0) {return new BookInfo();}BookInfo bookInfo = bookService.queryBookById(bookId);return bookInfo;}@RequestMapping("/updateBook")public String updateBook(BookInfo bookInfo) {log.info("修改圖書:{}", bookInfo);try {bookService.updateBook(bookInfo);return "";} catch (Exception e) {log.error("修改圖書失敗", e);return e.getMessage();}}
BookService代碼
public BookInfo queryBookById(Integer bookId) {return bookInfoMapper.queryBookById(bookId);}public void updateBook(BookInfo bookInfo) {bookInfoMapper.updateBook(bookInfo);}
BookInfoMapper代碼
@Select("select id, book_name, author, count, price, publish, `status`,
create_time, update_time " +"from book_info where id=#{bookId} and status<>0")
BookInfo queryBookById(Integer bookId);
前端代碼
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>修改圖書</title><link rel="stylesheet" href="css/bootstrap.min.css"><link rel="stylesheet" href="css/add.css">
</head><body><div class="container"><div class="form-inline"><h2 style="text-align: left; margin-left: 10px;"><svg xmlns="http://www.w3.org/2000/svg" width="40"fill="#17a2b8" class="bi bi-book-half" viewBox="0 0 16 16"><pathd="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z" /></svg><span>修改圖書</span></h2></div><form id="updateBook"><input type="hidden" class="form-control" id="bookId" name="id"><div class="form-group"><label for="bookName">圖書名稱:</label><input type="text" class="form-control" id="bookName" name="bookName"></div><div class="form-group"><label for="bookAuthor">圖書作者</label><input type="text" class="form-control" id="bookAuthor" name="author" /></div><div class="form-group"><label for="bookStock">圖書庫存</label><input type="text" class="form-control" id="bookStock" name="count" /></div><div class="form-group"><label for="bookPrice">圖書定價:</label><input type="number" class="form-control" id="bookPrice" name="price"></div><div class="form-group"><label for="bookPublisher">出版社</label><input type="text" id="bookPublisher" class="form-control" name="publish" /></div><div class="form-group"><label for="bookStatus">圖書狀態</label><select class="custom-select" id="bookStatus" name="status"><option value="1" selected>可借閱</option><option value="2">不可借閱</option></select></div><div class="form-group" style="text-align: right"><button type="button" class="btn btn-info btn-lg" onclick="update()">確定</button><button type="button" class="btn btn-secondary btn-lg" onclick="javascript:history.back()">返回</button></div></form></div><script type="text/javascript" src="js/jquery.min.js"></script><script>$.ajax({type: "get",url: "/book/queryBookById" + location.search,success: function (book) {if (book != null) {$("#bookId").val(book.id);$("#bookName").val(book.bookName);$("#bookAuthor").val(book.author);$("#bookStock").val(book.count);$("#bookPrice").val(book.price);$("#bookPublisher").val(book.publish);$("#bookStatus").val(book.status);}}});function update() {$.ajax({type: "post",url: "/book/updateBook",data: $("#updateBook").serialize(),success: function (result) {if (result == "") {location.href = "book_list.html"} else {console.log(result);alert("修改失敗:" + result);}},error: function (error) {console.log(error);}});}</script>
</body></html>
1. 獲取url中參數的值(?較復雜, 需要拆分url)2. 在form表單中, 再增加?個隱藏輸?框, 存儲圖書ID, 隨 $("#updateBook").serialize()
<form id="updateBook"><input type="hidden" class="form-control" id="bookId" name="id"><div class="form-group"><label for="bookName">圖書名稱:</label><input type="text" class="form-control" id="bookName" name="bookName"></div>
hidden 類型的 <input> 元素隱藏表單, ??不可?、不可改的數據,在??提交表單時,這些數據會?并發送出使?場景: 正被請求或編輯的內容的 ID. 這些隱藏的 input 元素在渲染完成的??中完全不可?,? 且沒有?法可以使它重新變為可?
$("#bookId").val(book.id);
刪除圖書
刪除分為 邏輯刪除 和物理刪除邏輯刪除邏輯刪除也稱為軟刪除、假刪除、Soft Delete,即不真正刪除數據,?在某?數據上增加類型 is_deleted的刪除標識,?般使?UPDATE語句物理刪除物理刪除也稱為硬刪除,從數據庫表中刪除某??或某?集合數據,一般用DELETE語句
function deleteBook(id) {var isDelete = confirm("確認刪除?");if (isDelete) {//刪除圖書$.ajax({type: "post",url: "/book/updateBook",data: {id: id,status: 0},success: function () {//重新刷新??location.href = "book_list.html"}});}}
批量刪除
點擊[批量刪除]按鈕時, 只需要把復選框選中的圖書的ID,發送到后端即可多個id, 我們使?List的形式來傳遞參數
@RequestMapping("/batchDeleteBook")public boolean batchDeleteBook(@RequestParam List<Integer> ids){log.info("批量刪除圖書, ids:{}",ids);try{bookService.batchDeleteBook(ids);}catch (Exception e){log.error("批量刪除異常,e:",e);return false;}return true;}public void batchDeleteBook(List<Integer> ids) {bookInfoMapper.batchDeleteBook(ids);
void batchDeleteBook(List<Integer> ids);
xml
<update id="batchDeleteBook">update book_info set status=0 where id in<foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach></update>
攔截器
🍃前言
前面我們基本實現了圖書股管理系統的功能,但是我們依舊存在一個問題。
就是我們不論是否登錄,我們直接訪問圖書列表。也可以進行訪問及修改
而我們希望達到的效果是,必須要進行登錄后才能進行一系列操作
這里我們使用攔截器來完成著一系列操作
什么是攔截器?
攔截器是Spring框架提供的核?功能之?,主要?來攔截??的請求,在指定?法前后,根據業務需要執?預先設定的代碼.
也就是說,允許開發?員提前預定義?些邏輯,在??的請求響應前后執?.也可以在??請求前阻?其執?.
在攔截器當中,開發?員可以在應?程序中做?些通?性的操作,?如通過攔截器來攔截前端發來的請求,判斷Session中是否有登錄??的信息.如果有就可以放?,如果沒有就進?攔截.
就?如我們去銀?辦理業務,在辦理業務前后,就可以加?些攔截操作,辦理業務之前,先取號,如果帶?份證了就取號成功。業務辦理結束,給業務辦理?員的服務進?評價.這些就是"攔截器"做的?作
攔截器的基本使用
攔截器的使?步驟分為兩步:
- 定義攔截器
- 注冊配置攔截器
自定義攔截器
?定義攔截器:實現HandlerInterceptor接?,并重寫其所有?法
package com.example.demo.configuration;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("執行目標方法前的代碼");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("目標執行完后的代碼");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("目標試圖渲染后的代碼");HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}
這里涉及到的三個方法
preHandle()?法:?標?法執?前執?. 返回true:繼續執?后續操作;返回false:中斷后續操作.
postHandle()?法:?標?法執?后執?
afterCompletion()?法:視圖渲染完畢后執?,最后執?(后端開發現在?乎不涉及視圖,暫不了解)
注冊配置攔截器
注冊配置攔截器:實現WebMvcConfigurer接?,并重寫addInterceptors?法
WebMvcConfigurer這個接口并不是只給攔截器使用的,而是WebMVC相關的配置都在這里
package com.example.demo.configuration;import com.example.demo.model.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**");}
}
啟動服務,試試訪問任意請求,觀察后端?志
們把攔截器中preHandle?法的返回值改為false,再觀察運?結果?
運行結果:可以看到,攔截器攔截了請求,沒有進?響應
攔截路徑
關于注冊配置攔截器的攔截路勁,攔截路徑是指我們定義的這個攔截器,對哪些請求?效.我們在注冊配置攔截器的時候,通過 addPathPatterns() ?法指定要攔截哪些請求.也可以通過
excludePathPatterns() 指定不攔截哪些請求.上述代碼中,我們配置的是 /** ,表?攔截所有的請求.
攔截器中除了可以設置 /** 攔截所有資源外,還有?些常?攔截路徑設置,比如在該項目中
攔截器執行流程
我們先來看一下正常的執行流程
當我們有了攔截器以后,我們的執行流程為
1.添加攔截器后,執?Controller的?法之前,請求會先被攔截器攔截住.執? preHandle() ?法,這個?法需要返回?個布爾類型的值.如果返回true,就表?放?本次操作,繼續訪問controller中的?法.如果返回false,則不會放?(controller中的?法也不會執?).
2.controller當中的?法執?完畢后,再回過來執? postHandle() 這個?法以及afterCompletion() ?法,執?完畢之后,最終給瀏覽器響應數據.
項目實現統一攔截
定義攔截器
首先定義一個常量類來存放我們的sessionid常量等等
package com.example.demo.model;public class Constants {public static final String SESSION_USER_KEY="userName";
}
再來就是攔截器(邏輯簡單,查看有沒有現在的這個session即可)?
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("登錄攔截器校驗...");HttpSession session = request.getSession();UserInfo userInfo = (UserInfo) session.getAttribute(Constants.SESSION_USER_KEY);if (userInfo!=null && userInfo.getId()>=0){return true;}response.setStatus(401);//401 表示未認證登錄return false;}
}
代碼解釋如下:
- 對服務中所存在的session進行判斷,如果存在,則返回true,放行
- 若不存在,則使用setStatus()方法設置http狀態碼為401,前端收到響應進行跳轉到登錄頁面
當前按照上述配置攔截器的代碼來看,會攔截所有的路徑,那么此時在沒有登錄的情況下,訪問每個接口都會進行攔截,包括登錄接口
所以我們需要把上述配置攔截器中的攔截路徑重新配置一下
注冊配置攔截器
注意:攔截器也會攔截前端請求
@Configurationpublic class WebConfig implements WebMvcConfigurer {//?定義的攔截器對象 @Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注冊?定義攔截器對象registry.addInterceptor(loginInterceptor).addPathPatterns("/**")//設置攔截器攔截的請求路徑(/**表?攔截所有請求 .excludePathPatterns("/user/login")//設置攔截器排除攔截的路徑.excludePathPatterns("/**/*.js") //排除前端靜態資源.excludePathPatterns("/**/*.css").excludePathPatterns("/**/*.png").excludePathPatterns("/**/*.html");}}
訪問用戶登錄接口:此時就可以訪問了
希望你們可以從這一個簡單項目中學到一個項目的基礎思路知識