黑马项目——瑞吉外卖 个人笔记04

本文详细介绍了如何在SpringBoot应用中创建过滤器,用于检查用户登录状态,未登录时重定向至登录页面。此外,还展示了全局异常处理器的编写,用于捕获和处理SQLIntegrityConstraintViolationException等异常,提供统一的错误反馈。文章还涵盖了员工信息的分页查询和启用/禁用员工账号的实现。
摘要由CSDN通过智能技术生成

目录

一、创建过滤器

1.1 前提需求:

1.2 处理逻辑:

1.3 代码实现:

1.4 对代码的细解

1.41  implements Filter,  Spring Boot 自定义过滤器(Filter)

1.42 路径匹配器  AntPathMatcher

二、新增员工_编写全局异常处理器

2.1 前提需求

2.2 代码实现

 三、新增员工

 四、员工信息分页查询

1.配置MP分页插件

 2.代码实现

 五、启用、禁用员工账号

5.1 需求分析

5.2 代码实现


一、创建过滤器

1.1 前提需求:

使用过滤器或者拦截器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面。

1.2 处理逻辑:

1.获取本次请求的URI
2.判断本次请求是否需要处理
3.如果不需要处理,则直接放行
4.判断登录状态,如果已登录,则直接放行
5.如果未登录则返回未登录结果

1.3 代码实现:

package com.itheima.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.itheima.reggie.common.BaseContext;
import com.itheima.reggie.common.R;
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;

/**
 * 检查用户是否完成登录
 */
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {

    //路径匹配器 支持通配符
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();


    /**
     * doFilter方法被容器调用,同时传入分别指向这个请求/响应链中的
     * Servlet Request、Servlet Response 和 Filter Chain 对象的引用。
     * 然后过滤器就有机会处理请求,将处理任务传递给链中的下一个资源
     * (通过调用 Filter Chain 对象引用上的 doFilter方法),之后在处理控制权返回该过滤器时处理响应。
     *
     * @param servletRequest  上一个 Filter传递过来的请求
     * @param servletResponse 上一个 Filter响应对象
     * @param filterChain     当前 Filter 链的对象,
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //为什么要强转:https://blog.csdn.net/weixin_41066584/article/details/110280455
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        //1.获取本次请求的url
        String requestURI = request.getRequestURI();
        log.info("本次请求拦截的url{}",requestURI);
        //定义不需要处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**",
                //用户在登录前需要执行的操作
                "/user/sendMsg", //移动端发送短信
                "/user/login" //移动端登录
        };

        //2.判断本次请求是否需要处理
        boolean check = check(urls, requestURI);

        //3.如果不需要处理,则直接放行
        if (check) {
            log.info("本次请求{}不需要处理",requestURI);
            filterChain.doFilter(request, response);
            return;
        }

        //4-1.判断登录状态,如果已登录,则直接放行
        if (request.getSession().getAttribute("employee") != null) {
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));

            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setThreadLocal(empId);

           /* long id2 = Thread.currentThread().getId();
            log.info("doFilter线程id为:{}",id2);*/
            filterChain.doFilter(request, response);
            return;
        }


        //4-2 判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("user") != null){
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("user"));

            Long userId = (Long)request.getSession().getAttribute("user");
            BaseContext.setCurrentId(userId);

            filterChain.doFilter(request,response);
            return;
        }

        log.info("用户未登录");
        //5.如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;


        //1.在doFilter()方法中,在chain.doFilter()之前的代码,一般是对request执行的过滤操作
        //2.在chain.doFilter()后面的代码,一般是对response执行的操作;
        //3.chain.doFiter()执行下一个过滤器或者业务处理器。
        //4.如果在doFilter()方法中,不写chain.doFilter(),业务无法继续往下处理;
    }

    /**
     * 路径匹配 检查本次请求是否需要放行
     *
     * @param urls
     * @param requestURI
     * @return
     */
    public boolean check(String[] urls, String requestURI) {
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) {
                return true;
            }
        }
        return false;
    }
}

1.4 对代码的细解

1.41  implements Filter,  Spring Boot 自定义过滤器(Filter)

Filter 过滤器主要是用来过滤用户请求的,它允许我们对用户请求进行前置处理和后置处理,比如实现 URL 级别的权限控制、过滤非法请求等等。

javax.servlet.Filter

public interface Filter {
  
   //初始化过滤器后执行的操作
    default void init(FilterConfig filterConfig) throws ServletException {
    }
   // 对请求进行过滤
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
   // 销毁过滤器后执行的操作,主要用户对某些资源的回收
    default void destroy() {
    }
}

其中 LoginCheckFilter 类就实现了doFilter方法

1.42 路径匹配器  AntPathMatcher

AntPathMatcher 为spring 工具类

//路径匹配器 支持通配符
private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

创建一个urls的字符串型数组
requestURI为request.getRequestURI();
check判断方法

    /**
     * 路径匹配 检查本次请求是否需要放行
     *
     * @param urls:不需要处理的请求路径
     * @param requestURI 本次请求的url
     * @return
     */
    public boolean check(String[] urls, String requestURI) {
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) {
                return true;
            }
        }
        return false;
    }

1.43 doFilter()方法

1. 容器中的每一次请求都会调用该方法,从而实现过滤功能。在doFilter()方法中,chain.doFilter()前的一般是对request执行的过滤操作,chain.doFilter后面的代码一般是对response执行的操作

2. 如果你在一个Filter方法后不加chain.doFilter(request, response) 则后续的Filter和Servlet都不会执行

/**
     * doFilter方法被容器调用,同时传入分别指向这个请求/响应链中的
     * Servlet Request、Servlet Response 和 Filter Chain 对象的引用。
     * 然后过滤器就有机会处理请求,将处理任务传递给链中的下一个资源
     * (通过调用 Filter Chain 对象引用上的 doFilter方法),之后在处理控制权返回该过滤器时处理响应。
     *
     * @param servletRequest  上一个 Filter传递过来的请求
     * @param servletResponse 上一个 Filter响应对象
     * @param filterChain     当前 Filter 链的对象,
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //为什么要强转:https://blog.csdn.net/weixin_41066584/article/details/110280455
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

}

注:为什么要强转?
doFilter的参数request对象的生成方式不是ServletRequest request = new ServletRequest();这种形式,而是ServletRequest request = new HttpServletRequest();这种形式,参数里的request不是父类ServletRequest的对象,而是HttpServletRequest的上转型对象.

HttpServletRequest和ServletRequest都是接口

request对象的确是HttpServletRequest的一个实例。

public interface HttpServletRequest extends ServletRequest {
}

ServletRequest request;这个是将子类对象赋给父类引用,他运行时的类型是子类,编译时的类型是父类,但是在运行时,父类类型对象调用的方法如果子类里面有,那就执行子类里面的方法,如果编译时的类型也就是父类没有调用的那个方法,则报错。所以在那里要做一个强制类型转换,否则就会报错

 参考博客:(92条消息) 关于Filter中ServletRequest强转HttpServletRequest问题_HD243608836的博客-CSDN博客

(92条消息) JAVA servlet doFilter()用法-CSDN博客

二、新增员工_编写全局异常处理器

2.1 前提需求

就是当我们在新增员工时输入的账号已经存在,由于employee表中对该字段加入了唯一约束,此时程序会抛出异常:

此时需要我们的程序进行异常捕获,

2.2 代码实现

package com.itheima.reggie.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;

/**
 * 全局异常处理器
 */
//annotations:指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理。
//@controllerAdvice最为实用的一个场景就是将所有@ExceptionHandler方法收集到一个类中,这样所有的异常都能在一个地方进行一致处理。
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 异常处理方法
     *
     * @return
     */
    //@ControllerAdvice默认所有控制的抛出的异常都会在这个类进行处理
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex) {
        log.error(ex.getMessage());

        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());
    }



}
annotations:指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理。
@controllerAdvice最为实用的一个场景就是将所有@ExceptionHandler方法收集到一个类中,这样所有的异常都能在一个地方进行一致处理。

ControllerAdvice本质上是一个Component因此也会被当成组建扫描,

这个类是为那些声明了(@ExceptionHandler-处理全局异常@InitBinder —对请求参数的预处理或 @ModelAttribute—预设全局数据注解修饰的)方法的类而提供的专业化的@Component , 以供多个 Controller类所共享。

说白了,就是aop思想的一种实现,你告诉我需要拦截规则,我帮你把他们拦下来,具体你想做更细致的拦截筛选和拦截之后的处理,你自己通过@ExceptionHandler@InitBinder 或 @ModelAttribute这三个注解以及被其注解的方法来自定义。

参考博客:(92条消息) @ControllerAdvice 的介绍及三种用法_Ethan.Han的博客-CSDN博客

 三、新增员工

3.1 代码实现

   /**
     * 新增员工
     *
     * @param employee
     * @return
     */
    @PostMapping()
    public R<String> save(HttpServletRequest request, @RequestBody Employee employee) {
        log.info("新增员工,员工信息:{}", employee.toString());

        //设置初始密码 ,需要进行md5加密处理
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));

        /*employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());*/

        //获取当前用户的id
        Long empId = (Long) request.getSession().getAttribute("employee");

        /*employee.setCreateUser(empId);
        employee.setUpdateUser(empId);*/

        employeeService.save(employee);

        return R.success("新增员工成功");
    }

 四、员工信息分页查询

1.配置MP分页插件

/**
 * 配置MP的分页插件
 */
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();

        //MybatisPlusInterceptor#addInnerInterceptor() 方法用于添加拦截器插件;
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());//自动分页: PaginationInnerInterceptor
        return mybatisPlusInterceptor;
    }
}

 2.代码实现

 /**
     * 员工信息分页查询
     *
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name) {
        log.info("page = {}, pageSize = {}, name = {}", page, pageSize, name);

        //构造分页构造器
        Page pageInfo = new Page(page, pageSize);

        //构造条件构造器
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();

        //添加过滤条件
        queryWrapper.like(StringUtils.isNotEmpty(name), Employee::getName, name);

        //添加排序条件
        queryWrapper.orderByDesc(Employee::getUpdateTime);

        //执行查询      `1
        employeeService.page(pageInfo, queryWrapper);
        return R.success(pageInfo);
    }

 五、启用、禁用员工账号

5.1 需求分析

启用、禁用员工账号,本质上就是一个更新操作,也就是对status状态字段进行操作在Controller中创建update方法,此方法是一个通用的修改员工信息的方法

5.2 代码实现

    /**
     * 根据id修改员工信息
     *
     * @param employee
     * @return
     */
    @PutMapping
    public R<String> update(HttpServletRequest request, @RequestBody Employee employee) {
        log.info(employee.toString());

        /*Long empId = (Long) request.getSession().getAttribute("employee");*/

        long id = Thread.currentThread().getId();
        log.info("update线程id为:{}", id);
        /*employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(empId);*/
        employeeService.updateById(employee);

        return R.success("员工信息修改成功");
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值