写在前面
项目介绍:本项目是一个外卖点餐的系统
使用的主要技术栈 springboot
,mybatis-plus
,redis
,vue2
本项目比较基础,对于熟悉写项目的整体架构会有一个比较清晰的认识。
下面梳理出来的项目中的亮点部分。
具体的项目的源码以及项目的项目详细介绍在gitte仓库中
使用过滤器
过滤器可以对于用户的请求进行拦截,避免了用户在没有登录的情况下访问项目。
如何使用过滤器,通过一个最基础的springboot
项目进行演示,分别为LoginController
类,LoginFilter
类,以及一个启动类,端口号使用的是8081
LoginFilter.java
代码如下
该类实现了Filter
接口,注意是javax.serlet
包下的,该接口中有三个方法,而只有doFilter
是抽象方法,所以需要重写该方法。需要在该类上面加上注解,@WebFilter(filterName = "LoginFilter",urlPatterns = {"/*"})
,表明需要拦截的请求路径,在该演示中使用的是{"/*}
表示拦截的所有的请求,因此用户的任何请求,首先都会进入该类进行处理之后,才会进入到controller
层。要想让该类其作用,并且要让springboot在启动的时候知道该类的存在,需要在启动类中加上注解@ServletComponentScan
package com.wjiangquna.filter;
import com.sun.deploy.net.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* filterName过滤器名字
* urlPatterns拦截的请求,这里是拦截所有的请求
*
*/
@Slf4j
@WebFilter(filterName = "LoginFilter",urlPatterns = {"/*"})
public class LoginFilter implements Filter{
//路径匹配器,支持通配符(spring给我们提供的工具)
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request= (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
// 获取本次请求的URUI
String requestURI = request.getRequestURI(); //获取请求的URI
StringBuffer requestURL = request.getRequestURL(); //获取请求的URL
log.info("拦截到请求:{},请求的URL为:{}",requestURI,requestURL);
// 定义不需要拦截的请求
String[] urls = new String[]{"/user/login", "/user/logout"};
// 检测请求是否需要处理
boolean check = check(urls, requestURI);
if(check){
log.info("本次请求{}不需要处理",requestURI);
// 放行
filterChain.doFilter(request,response);
return;
}
response.getWriter().write("You do not have access rights");
}
/**
* 路径匹配,检查本次请求是否需要放行
* @param requestURI 请求的URI地址
* @return true说明匹配上了
*/
public boolean check(String[] urls,String requestURI){
for (String url:urls){
boolean match = PATH_MATCHER.match(url, requestURI);
if(match){
return true;
}
}
return false;
}
}
启动类的代码
package com.wjiangquna;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan //这个是注解是让过滤器中那些生效的,可以扫描到过滤器的那些,才能扫描WebFilter这些注解
@Slf4j
public class FilterDemoApplication {
public static void main(String[] args) {
SpringApplication.run(FilterDemoApplication.class, args);
log.info("项目启动了");
}
}
LoginController.java
package com.wjiangquna.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author weijiangquan
* @date 2022/8/7 -21:12
* @Description
*/
@RestController
@RequestMapping("/user")
public class LoginController {
@GetMapping("/login")
public String login() {
return "success";
}
@GetMapping("logout")
public String logout() {
return "logout";
}
@GetMapping("/list")
public String list() {
return "error";
}
}
测试过滤器
启动项目之后进行测试,对于Logincontroller
的三个接口进行的测试
在控制台上可以看到该日志信息
对于请求http://localhost:8081/user/logout
和http://localhost:8081/user/login
由在过滤器中的下面的代码匹配上了之后,就会直接返回,这里的返回的意思就是可以访问LoginController
层的相应的接口了
// 定义不需要拦截的请求
String[] urls = new String[]{"/user/login", "/user/logout"};
// 检测请求是否需要处理
boolean check = check(urls, requestURI);
if(check){
log.info("本次请求{}不需要处理",requestURI);
// 放行
filterChain.doFilter(request,response);
return;
}
对于接口http://localhost:8081/user/list
,由于没有匹配上,所以直接通过response
对象返回信息给用户
总结:
在本次过滤器的演示的功能中,或许功能比较简单,没有涉及到复杂的逻辑,而是提供了一种在项目中使用过滤器的思路,在不同的情况下可以根据自己的需要和项目的走向进行拓展。而在瑞吉外卖的项目中基于上面也拓展了一些功能,比如在过滤器中判断用户是否登录,也就是检测session
是否存在唯一的标识,如果没有使用过滤器的化,就需要在controller
每一个接口中进行判断,这样的化,就会让代码量变大,维护性更差。
使用全局异常进行捕获
在本项目中使用到全局异常的处理,
全局异常可以使整个项目更加的完善
package com.wjiangquan.whale.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* @author weijiangquan
* @date 2022/7/23 -23:12
* @Description 全局异常的处理
*/
@ControllerAdvice(annotations = {RestController.class, Controller.class})
//拦截加了这个RestController注解的controller 加到了普通的也会拦截
@ResponseBody //最终的结果返回json
@Slf4j
public class GlobalExceptionHandler {
/**
* 进行异常处理的方法
*
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class) //处理哪些类型的异常
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex) {
log.error(ex.getMessage());
// getMessage的内容为 Duplicate entry 'aaa' for key 'idx_username'
if (ex.getMessage().contains("Duplicate entry")) {
String[] split = ex.getMessage().split(" ");
String msg = split[2] + "已存在";
return R.error(msg);
}
return R.error("未知错误");
}
/**
* 捕获自定义的异常
* @return
*/
@ExceptionHandler(CustomException.class) //处理哪些类型的异常
public R<String> exceptionHandler(CustomException ex) {
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
}
自定义异常类
package com.wjiangquan.whale.common;
/**
* @author weijiangquan
* @date 2022/7/30 -19:43
* @Description 自定义业务异常
*/
public class CustomException extends RuntimeException{
public CustomException(String message){
super(message);
}
}
参考视频
项目笔记和源码地址
项目笔记文档截图
下面是做项目的过程中整理的源码和以及笔记的地址。
https://gitee.com/wei-jiangquan/reggie_take_out/tree/master/
mybatis-plus的官网
总结:
平时在项目中要学会处理异常,而全局异常的处理就是一个很好的选择,就异常进行统一出处理,可以处理系统异常以及自定义的异常。可以给整个项目指定一个标准。