【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;
        }
    }
    
  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");
    }
}

拦截器的工作原理:
为了理解拦截器源码实现,首先要知道一件事,一个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;
          }
      }
      
      

      上面的代码指定的异常类的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;
          }
      }
      

注意:如果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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值