• 【Spring MVC】统一功能处理


    一、登录验证

    登录验证通过拦截器实现,拦截器就是在用户访问服务器时,预先拦截检查一下用户的访问请求。

    • 没有拦截器时,用户访问服务器的流程是:用户–>controller–>service–>Mapper。
    • 有拦截器时,用户访问服务器流程是:用户–>拦截器–>controller–>service–>Mapper。

    拦截器起到了预先帮助Controller层检查请求是否合规的作用,让Controller层的代码更加专注于核心的业务逻辑。
    登录验证就是拦截器的一种应用。在请求没Controller层处理前,先判断发出请求的用户是否是登录状态。

    1. 创建拦截器类

      • 拦截器类要实现拦截器接口HanderInterCeptor
      • 重写里面的preHandle方法,这个preHandle方法就是拦截器在拦截到http请求后做的事情。
        • 返回值是Boolean类型,返回true代表通过拦截,继续向深层代码执行
        • 返回false代表请求不合规,被拦截了,直接返回响应。
      @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("userinfo")!=null){
                  return true;
              }
              return false;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    2. 配置拦截器拦截的url

      • 重新创建一个配置类,并且这个类要加上@Configuration注释交给Spring容器托管。
      • 继承WebMvcConfigurer接口,并且重写addInterceptor方法
        • 参数InterceptorRegistry就是拦截器的注册列表,要将拦截器添加到列表中拦截器才能在url访问时候调用。
        • registry的addInterceptor方法就是将传入的拦截器对象注册加入拦截器列
        • addPathPatterns方法就是添加拦截器的拦截路径,/※代表这一级的所有路径,/** 代表所有的多级路径。
        • excludePathPatterns方法就是在已添加的路径中剔除路径
    @Configuration
    public class LoginConfiguration implements WebMvcConfigurer {
        @Autowired
        private LoginInterceptor interceptor;
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(interceptor).
                    addPathPatterns("/**").
                    excludePathPatterns("/image/**").excludePathPatterns("/sayhi");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    拦截器的工作原理:
    为了理解拦截器源码实现,首先要知道一件事,一个http请求访问到项目,项目进行处理的时候,是有一个调度器(DispatcherServlet)来调度程序执行的先后顺序的.这个调度器是SpringMVC框架实现的。

    当spring boot项目启动后,控制台没有DispatcherServlet启动的日志
    在这里插入图片描述

    但是当首个http请求发出后,DispatcherServlet就会被初始化
    在这里插入图片描述

    调度器在收到http请求后的首先就会执行拦截器列表中的拦截器对象的方法,只有列表中的所有方法都返回true后,拦截器才会调度深层代码继续执行。有一个返回false都会直接返回响应。

    二、统一异常处理

    当前端传入的某些特殊数据时,可能会导致后端程序在执行时抛出异常,如果不对这个异常做出处理,后端会直接返回500的错误页面,破坏前后端交互的正常进行,异常处理的作用就是就算后端抛出了异常,响应也要按照正确格式返回,只是在返回的时候说明后端发生了错误。

    1. 自定义异常处理类
      • 这个类要用@ControllerAdvice和@ResponseBody修饰(可用@RestControllerAdvice代替这两个)

      • 类内的方法要指定是针对哪个异常进行处理,使用@ExceptionHandeler注解指定异常,注解参数是异常类的class对象

      • 处理方法的返回值就是响应的body返回值(要将按正常格式返回)

        @ControllerAdvice
        @ResponseBody
        public class MyExceptionController {
            @ExceptionHandler(Exception.class)
            public HashMap<String,Object> handException(Exception e){
                HashMap<String,Object> map=new HashMap<>();
                map.put("data",e.getMessage());
                return map;
            }
        }
        
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11

        上面的代码指定的异常类的class是所有异常的父类Exception,这样指定是起到一个保底的作用,因为异常是不可预知的,你在写代码的时候,并不知道代码会抛出什么异常,针对特定异常的处理是极少的,直接使用父类异常接受可以兜底保证异常抛出时,会被正确的处理。
        抛出异常时,处理方法的匹配原则是先匹配对应异常的处理方法,如果没有才会匹配父类异常的处理方法。

    三、统一数据格式

    1. 要定义一个格式处理类,使用@ControllerAdvice类修饰
    2. 然后这个类要继承ResponseBodyAdvice接口,重写supports方法和beforeBodyWrite方法
      • Support方法的返回值就是设置是否要调用beforeBodyWrite方法的,true表示要调用,false表示不调用

      • beforeBodyWrite就是修改body的数据格式的方法,可以根据自带的object参数body获取判断参数格式是否符合要求。(用instanceof方法判断Object是否是约定要返回的对象类型)

        @ControllerAdvice
        public class ResponseAdvice implements ResponseBodyAdvice {
            @Override
            public boolean supports(MethodParameter returnType, Class converterType) {
                return true;
            }
        
            @Override
            public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
               if(body instanceof HashMap){
                   //格式正确
                   return body;
               }
               HashMap<String,Object> map=new HashMap();
               map.put("code",1);
               map.put("data",body);
               return map;
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19

    注意:如果body是String类型的数据,将这个hashMap最终转换成json格式的时候,会报错。
    原因就是在hashmap转换成json字符串的时候会判断参数Object data的源类型,如果原类型是String就会使用的StringHttpMessageConverter进行转换,这个转换只能进行单纯的字符串转换,最终生成的响应的body就是字符串,bodyType就是Text/html,但是hashMap无法被转换成字符串,正常hashMap也应该被被转换成json格式,所以会报错。
    最根本的原因是,统一返回数据格式使用的是AOP思想,在所有方法返回之后拦截了响应方法的返回值,但是响应方法的返回值的类型就决定了返回值转换成响应中body的转换器以及响应中的bodyType。
      如果是String类型的返回值,默认的转换器就是StringHttpMessageConvert,由此bodyType为Text/html
      如果是其他类型,则会是json格式
    解决办法:
    1.在统一格式转换方法里,判断当前的Object body的源类型是不是String,如果是就直接在方法中将最终统一返回的类型对象使用ObjectMapper转换成json格式的字符串返回,ObjectMapper对象是Spring框架也内置了,可以直接注入使用。
    2.直接禁用StringHttpMessageConvert(查查chatgpt咋禁用)。

    @ControllerAdvice
    public class ResponseAdvice implements ResponseBodyAdvice {
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            return true;
        }
    
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
           if(body instanceof HashMap){
               //格式正确
               return body;
           }
           HashMap<String,Object> map=new HashMap();
           map.put("code",1);
           map.put("data",body);
           //判断是否是String
           if(body instanceof String){
               ObjectMapper objectMapper=new ObjectMapper();
               String result= null;
               try {
                   result = objectMapper.writeValueAsString(map);
               } catch (JsonProcessingException e) {
                   e.printStackTrace();
               }
               return result;
           }
           return map;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • 相关阅读:
    谷歌AI搜索功能“翻车”,用户体验引担忧
    物体6D位姿估计方法总结
    HTTP协议和Tomcat服务器
    JVM垃圾回收系列之垃圾收集算法
    第03章 卷积神经网络
    目标检测YOLO实战应用案例100讲-面向恶劣环境下的多模态 行人识别(续)
    《智能风控实践指南》笔记(二)
    ARM32开发——GPIO输入
    [附源码]java毕业设计学校缴费系统
    【SpringMVC】自定义注解与AOP结合使用
  • 原文地址:https://blog.csdn.net/qq_61925446/article/details/132701397