• 【Spring Boot】Spring Boot 统一功能处理


    Spring拦截器

    为何需要Spring拦截器?

    在之前Servlet开发中,对登陆校验有两种方法:

    1. 在每个方法中都获取到session中的用户信息,然后判断用户是否存在,如果用户存在则登陆,如果用户不存在则未登陆
    2. 提供统一的方法验证用户是否登陆,在需要验证用户登陆的地方调用该方法进行判断

    从上述可以看出这样做比较复杂,因为如果有许多地方需要进行登陆验证的话,就需要写重复的校验代码或者重复调用判断登陆的方法

    对此我们提供Spring拦截器来实现用户的统一登陆验证,拦截器的实现分为以下两个步骤:

    1. 创建自定义拦截器类,实现HandlerInterceptor接口并重写preHandle方法
    2. 创建配置类,将自定义的拦截器添加到该配置类中,并添加拦截规则
      • 给配置类添加@Configuration注解
      • 实现WebMvcConfigurer接口
      • 重写addInterceptors方法

    注意:一个项目中可以配置多个拦截器

    自定义拦截器

    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HttpSession session = request.getSession();
            if(session != null && session.getAttribute("user") != null){
                return true;
            }
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    preHandle方法的返回值为boolean类型

    • 如果返回值为true,表示通过拦截,可以访问后续接口
    • 如果返回值为false,表示拦截未通过,直接返回结果给前端

    如果返回为false,前端展示的为空白,对于登陆校验,当拦截未通过时,我们希望返回到登陆界面,所以可以在返回false之前重定向到登陆页面

    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HttpSession session = request.getSession();
            if(session != null && session.getAttribute("user") != null){
                return true;
            }
            //当代码进行到这一步时,说明拦截未通过,防止前端显示空白,重定向到登陆页面
            response.sendRedirect("/login.html");
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    加入系统配置并配置拦截规则

    @Configuration
    public class AppConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterceptor()).
                    addPathPatterns("/**"). //拦截所有url
                    excludePathPatterns("/user/login"). //排除登陆接口
                    excludePathPatterns("/user/register"). //排除注册接口
                    //排除所有的静态页面
                    excludePathPatterns("/login.html").
                    excludePathPatterns("/register.html").
                    excludePathPatterns("/**/*.js").
                    excludePathPatterns("/**/*.css").
                    excludePathPatterns("/**/*.jpg");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    说明:addPathPatterns表示需要拦截的URL,excludePathPatterns表示需要排除的URL

    验证登陆拦截器

    1. 先创建login.html和content.html页面,然后创建后端接口
        @RequestMapping("/login")
        public String login(String username, String password, HttpServletRequest req){
            if(username==null || password==null){
                return "请输入用户名和密码";
            }
            if(username.equals("abc") && password.equals("123")){
                HttpSession session = req.getSession(true);
                session.setAttribute("user","user");
                return "登陆成功";
            }
            //登录失败也要返回前端失败的数据,否则前端会展示空白
            response.setContentType("application/json; charset=utf8");
            Map<String,Object> result = new HashMap<>();
            result.put("code",-1);
            result.put("msg","未登录不允许访问");
            result.put("data",null);
            response.getWriter().write(objectMapper.writeValueAsString(result));
            return "登陆失败";
        }
    
        @RequestMapping("/content")
        public String content(){
            return "已经登陆成功";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. 创建拦截器(与上述相同,去掉重定向的那行代码),配置拦截规则:我们排除对登陆页面和登陆接口的拦截
    @Configuration
    public class AppConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterceptor()).
                    addPathPatterns("/**"). //拦截所有url
                    excludePathPatterns("/user/login"). //排除登陆接口
                    excludePathPatterns("/login.html");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 启动程序访问登陆页面,登陆页面正常访问
      在这里插入图片描述
    2. 访问登陆接口,正常访问,此时未登陆
      在这里插入图片描述
    3. 在未登录的情况下访问content.html,可以看到显示空白说明拦截器拦截成功
      在这里插入图片描述
    4. 在未登录的情况下访问content接口,可以看到显示空白,说明拦截成功
      在这里插入图片描述
    5. 我们进行登陆后再访问content页面和content接口
      在这里插入图片描述
    6. 登陆成功后,访问content页面成功
      在这里插入图片描述
    7. 登陆成功后,访问content接口成功
      在这里插入图片描述

    经过上述步骤,我们的登陆拦截器验证成功,满足我们需求

    添加统一访问前缀

    给所有的请求url添加前缀

    1. 添加方式一:在配置拦截规则的配置类中重写configurePathMatch方法
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            //c -> true意思是给所有的controller都添加api前缀
            configurer.addPathPrefix("api",c -> true);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 添加方式二:在application配置文件中添加,该方式是给全局添加访问前缀,包括访问html静态文件
    server:
      servlet:
        context-path: /api
    
    • 1
    • 2
    • 3

    验证统一前缀是否添加成功

    启动程序,用之前的url访问登陆接口,发现找不到资源

    在这里插入图片描述

    添加api前缀访问,访问成功

    在这里插入图片描述

    注意:如果添加了拦截规则,也必须给拦截规则添加前缀,否则会被拦截器拦截到,前端将显示空白

     excludePathPatterns("/api/user/login")
    
    • 1

    统一异常处理

    统一异常的步骤:

    1. 创建一个类,添加@ControllerAdvice注解
    2. 创建一个方法,方法上添加@ExceptionHandler注解

    @ControllerAdvice表示当前类是针对controller的通知类(增强类),@ExceptionHandler是异常处理器,两个结合表示当出现异常的时候执行某个通知

    //返回json格式数据,将@Controller与@ResponseBody注解合成一个注解@RestController
    @RestControllerAdvice
    public class MyException {
    
        @ExceptionHandler(Exception.class)
        public Object handler(Exception e){
            Map<String,Object> map = new HashMap<>();
            map.put("state",-1);
            map.put("data",null);
            map.put("msg",e.getMessage());
            return map;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    对异常进行统一处理后,当服务器出现500错误时,会给前端返回自定义的错误信息,不会再直接返回给前端500状态码了

    在这里插入图片描述

    统一数据返回格式

    统一数据返回格式步骤:

    1. 创建一个类,添加@ControllerAdvice注解,实现ResponseBodyAdvice接口
    2. 重写supports和beforeBodyWrite方法
    @RestControllerAdvice
    public class MyResponse implements ResponseBodyAdvice {
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            //返回true表示返回数据之前对数据进行重写,也就是会进入beforeBodyWrite方法再返回
            //返回false表示对返回数据不进行任何处理,不会进入beforeBodyWrite方法,直接返回
            return true;
        }
    
        @Override //controller返回结果之前,进行格式重写
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        	//假设标准返回格式为hashmap
        	if(body instanceof HashMap){
        		return body; //如果格式正确,直接返回
        	}
        	//如果格式不正确,进行格式重写
            Map<String,Object> result = new HashMap<>();
            result.put("state",1);
            result.put("data",body);
            result.put("message","");
            return request;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    上述是保底策略,也就是当程序返回的格式不正确时,才会进行数据格式重写,我们在写代码的时候,往往都是返回自定义数据格式

    如果程序在controller中返回的类型为String,此时,程序会出现类型转换异常

    在这里插入图片描述

    所以我们可以在重写格式里,对String类单独判断一下

    if(body instanceof String){
        return objectMapper.writeValueAsString(body);
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    Java泛型
    设计模式学习笔记 - 设计原则 - 1.单一职责原则
    一张图搞懂微服务架构设计
    LeetCode:200.岛屿数量
    CH340系列Linux驱动安装
    1.2 信息系统开发方法
    docker搭建ELK
    springboot二手书籍线上回收网站java ssm-0401u
    css返回顶部快速回到页面顶部
    Linux下的Framebuffer编程
  • 原文地址:https://blog.csdn.net/qq_58710208/article/details/128088386