🍃前言
前面我们基本实现了图书股管理系统的功能,但是我们依旧存在一个问题。
就是我们不论是否登录,我们直接访问图书列表。也可以进行访问及修改
而我们希望达到的效果是,必须要进行登录后才能进行一系列操作
这里我们使用拦截器来完成着一系列操作
🍀什么是拦截器?
拦截器是Spring框架提供的核⼼功能之⼀,主要⽤来拦截⽤⼾的请求,在指定⽅法前后,根据业务需要执⾏预先设定的代码.
也就是说,允许开发⼈员提前预定义⼀些逻辑,在⽤⼾的请求响应前后执⾏.也可以在⽤⼾请求前阻⽌其执⾏.
在拦截器当中,开发⼈员可以在应⽤程序中做⼀些通⽤性的操作,⽐如通过拦截器来拦截前端发来的请求,判断Session中是否有登录⽤⼾的信息.如果有就可以放⾏,如果没有就进⾏拦截.
就⽐如我们去银⾏办理业务,在办理业务前后,就可以加⼀些拦截操作,办理业务之前,先取号,如果带⾝份证了就取号成功。业务办理结束,给业务办理⼈员的服务进⾏评价.这些就是"拦截器"做的⼯作
🌲拦截器的基本使用
拦截器的使⽤步骤分为两步:
- 定义拦截器
- 注册配置拦截器
🚩自定义拦截器
我们需要实现HandlerInterceptor接⼝,并重写其所有⽅法
@Slf4j @Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("LoginInterceptor ⽬标⽅法执⾏前执⾏.."); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("LoginInterceptor ⽬标⽅法执⾏后执⾏"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("LoginInterceptor 视图渲染完毕后执⾏,最后执⾏"); } }
这里涉及到的三个方法
preHandle()⽅法:⽬标⽅法执⾏前执⾏.返回true:继续执⾏后续操作;返回false:中断后续操作.
postHandle()⽅法:⽬标⽅法执⾏后执⾏
afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏(后端开发现在⼏乎不涉及视图,这里博主就不做讲解了)
🚩注册配置拦截器
实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法
@Configuration public class WebConfig implements WebMvcConfigurer { //⾃定义的拦截器对象 @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //注册⾃定义拦截器对象 registry.addInterceptor(loginInterceptor) .addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表⽰拦截所有请求) } }
至此简单的拦截器就注册完毕
🚩拦截路径
关于注册配置拦截器的拦截路劲,拦截路径是指我们定义的这个拦截器,对哪些请求⽣效.我们在注册配置拦截器的时候,通过 addPathPatterns() ⽅法指定要拦截哪些请求.也可以通过
excludePathPatterns() 指定不拦截哪些请求.上述代码中,我们配置的是 /** ,表⽰拦截所有的请求.
在拦截器中除了可以设置 /** 拦截所有资源外,还有⼀些常⻅拦截路径设置,比如在该项目中
拦截路径 | 含义 | 举例 |
/* | ⼀级路径 | 能匹配/user,/book,/login,不能匹配/user/login |
/** | 任意级路径 | 能匹配/user,/user/login,/user/reg |
/book/* | /book下的⼀级路径 | 能匹配/book/addBook,不能匹配/book/addBook/1,/book |
/book/** | /book下的任意级路径 | 能匹配/book,/book/addBook,/book/addBook/2,不能匹配/user/login |
🚩拦截器执行流程
学会了拦截器的基本使用,那么拦截器的执行流程是什么样的呢?
我们先来看一下正常的执行流程
当我们有了拦截器以后,我们的执行流程为
- 添加拦截器后,执⾏Controller的⽅法之前,请求会先被拦截器拦截住.执⾏ preHandle() ⽅法,这个⽅法需要返回⼀个布尔类型的值.如果返回true,就表⽰放⾏本次操作,继续访问controller中的⽅法.如果返回false,则不会放⾏(controller中的⽅法也不会执⾏).
- controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 这个⽅法以及afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据.
🎄项目实现统一拦截
🚩定义拦截器
首先我们定义拦截器如下:
代码实现如下:
@Slf4j @Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(false); if (session != null && session.getAttribute(Constants.SESSION_USER_KEY) != null) { return true; } response.setStatus(401); return false; } }
代码解释如下:
- 对服务中所存在的session进行判断,如果存在,则返回true,放行
- 若不存在,则使用setStatus()方法设置http状态码为401(Unauthorized)
- 关于Constants.SESSION_USER_KEY是我自定义的一个常量,表示为所存储的session值
public class Constants { public static final String SESSION_USER_KEY = "session_user_key"; }
🚩注册配置拦截器;
@Slf4j @Configuration public class WebConfig implements WebMvcConfigurer { //⾃定义的拦截器对象 @Autowired private LoginInterceptor loginInterceptor; private List<String> excludePaths = Arrays.asList( "/user/login", "/**/*.js", "/**/*.css", "/**/*.png", "/**/*.html" ); @Override public void addInterceptors(InterceptorRegistry registry) { //注册⾃定义拦截器对象 registry.addInterceptor(loginInterceptor) .addPathPatterns("/**")//设置拦截器拦截的请求路径(/** 表⽰拦截所有请求) .excludePathPatterns(excludePaths);//设置拦截器排除拦截的路径 } }
除此之外,博主还对之前的代码进行了一个改变,对返回的数据进行了一个封装,我希望在进行返回数据的时候我们只需要调用相应的方法就会返回我们所需要的相应结果
🚩代码的修改
首先我们准备一个枚举类为ResultStatus,里面有相应的业务状态显示以及相应的状态码设置
public enum ResultStatus { SUCCESS(200), UNLOGIN(-1), FAIL(-2); private Integer code; ResultStatus(int code) { this.code = code; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } }
然后我们再创建一个类Result,用来包装我们的返回结果。
并创建三个方法,进行返回结果的使用
代码实现如下:
@Data public class Result { private ResultStatus status; private String errorMessage; private Object data; /** * 业务执⾏成功时返回的⽅法 * * @param data * @return */ public static Result success(Object data) { Result result = new Result(); result.setStatus(ResultStatus.SUCCESS); result.setErrorMessage(""); result.setData(data); return result; } /** * 业务执⾏失败时返回的⽅法 * * @param * @return */ public static Result fail(String msg) { Result result = new Result(); result.setStatus(ResultStatus.FAIL); result.setErrorMessage(msg); result.setData(""); return result; } /** * 业务执⾏失败时返回的⽅法 * * @param * @return */ public static Result unlogin() { Result result = new Result(); result.setStatus(ResultStatus.UNLOGIN); result.setErrorMessage("⽤⼾未登录"); result.setData(null); return result; } }
🚩重写图书列表接口
由于我们返回的结果格式进行了改变,我们需要对相应的代码进行改变
@RequestMapping("/queryBookById") public Result queryBookById(Integer bookId){ if (bookId==null || bookId<=0){ return Result.unlogin(); } BookInfo bookInfo = bookService.queryBookById(bookId); return Result.success(bookInfo); }
🌳前端代码的实现
前端相应代码实现如下:
function getBookList() { $.ajax({ type: "get", url: "/book/getListByPage"+location.search, success: function (result) { console.log(result); if (result == null || result.data == null) { location.href = "login.html"; return; } if (result != null) { var finalHtml = ""; var data = result.data; for (var book of data.records) { finalHtml += '<tr>'; finalHtml += '<td><input type="checkbox" name="selectBook" value="'+book.id+'" id="selectBook" 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>' finalHtml += '<a href="javascript:void(0)" onclick="deleteBook(' + book.id + ')">删除</a>' finalHtml += '</div></td>'; finalHtml += "</tr>"; } $("tbody").html(finalHtml); //翻页信息 $("#pageContainer").jqPaginator({ totalCounts: data.total, //总记录数 pageSize: 10, //每页的个数 visiblePages: 5, //可视页数 currentPage: data.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>', //页面初始化和页码点击时都会执行 onPageChange: function (page, type) { console.log("第" + page + "⻚, 类型:" + type); if (type != 'init') { location.href = "book_list.html?currentPage=" + page; } } }); } }, error(error) { if(error.status == 401) { location.href = "login.html"; } } }); }
⭕总结
关于《【JavaEE进阶】 图书管理系统开发日记——柒》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!