SpringBoot 统一功能处理

目录

一. 统一用户登录权限验证

Spring 拦截器 

统一访问前缀添加

二. 统一异常处理

三. 统一数据格式返回

String 格式的特殊处理 


一. 统一用户登录权限验证

在没有统一功能处理之前,对于用户登录权限验证,每个方法都需要单独写用户登录验证的方法,就导致后期的修改和维护的成本大大提高 ,而这个验证方法与具体要实现的业务也一般没有关联,所以就需要提供一个公共的 AOP 方法来进行统一的用户登录权限验证。

Spring 拦截器 

此处就可以采用 Spring 拦截器 来处理问题,相比于 Spring AOP,它可以根据 HttpServletRequest 对象来获取到 HttpSession,而且对于一部分方法拦截,一部分方法不拦截的定义也更加方便。 

 1. 创建自定义拦截器,实现 HandleInterceptor 接口,实现 preHandle 方法,preHandle 方法表示执行具体方法之前的预处理,返回值为  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("session_userinfo")!=null){
//          用户已经登录,继续执行目标方法
            return true;
        }
//          验证失败,后续不再执行
        return false;
    }
}

2. 将自定义拦截器配置到系统配置项,并且设置合理的拦截规则。实现  WebMvcConfigurer 接口,实现方法 addInterceptors(InterceptorRegistry registry); 通过 registry.addInterceptor(对应的拦截器) 来将拦截器添加到系统配置项里,通过 registry.addInterceptor.addPathPatterns(拦截路径) 和 registry.addInterceptor.excludePathPatterns(放行路径) 来添加拦截规则


@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Autowired
    public LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        拦截器添加到系统配置项里面了    registry.addInterceptor(loginInterceptor)
        registry.addInterceptor(loginInterceptor)       // 设置拦截规则
                .addPathPatterns("/**")                 // /** 表示拦截所有路径(url)
                .excludePathPatterns("/**/login")        // 还要放行一些路径,比如登录和注册
                .excludePathPatterns("/user/reg");
    }
}

也可以拦截图片文件,JS文件等;

 "/**/*.jpg" "/**/*.js" "/**/*.css"

有了拦截器后的,执行流程就是:当用户调用的时候,就先进入拦截器进行预处理,判断是否满足业务条件,如果满足条件了,再进入控制器层进行数据交互和参数校验,然后进入服务层,再根据业务逻辑来决定要调用 哪个Mapper,从而访问数据库,再依次返回,而如果拦截器预处理不满足业务条件,那么后面也就不再需要执行了。 

 统一访问前缀添加

可以在访问的地址上添加前缀;

//    所有请求地址添加 add 前缀
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
//        其中第⼆个参数是⼀个表达式,设置为 true 表示启动前缀。
        configurer.addPathPrefix("add",c -> true);
    }
//    http://localhost:8080/add/user/login 加完前缀后要访问的 url 地址
//    excludePathPatterns() 中参数地址也要改为 add/user/reg

二. 统一异常处理

 统一异常处理使用:@ControllerAdvice + @ExceptionHandle 注解来实现。

1. @ControllerAdvice 表示控制器通知类,加这个注解之后,会去检测控制器的异常,如果控制器发生异常了,底下的方法就可以感知得到,感知到后就可以根据写的业务代码来将结果返回给前端;

2. @ExceptionHandle 是异常处理器,可以针对某个类型的异常进行监测和处理;

@ResponseBody + @ControllerAdvice 这两个注解可以用 @RestControllerAdvice 表示

@ControllerAdvice
@ResponseBody
public class MyExceptionAdvice {
//  检测异常类型
    @ExceptionHandler(NullPointerException.class)
    public HashMap<String,Object> doNullPointerException(NullPointerException e){
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",-1);
        result.put("msg","空指针:"+e.getMessage());
        result.put("data",null);
        return result;
    }
}

对于上述的代码就可以处理空指针异常,而异常的类型是有很多的,所以一般也就用父类 Exception 来作为处理。 当子类同时存在的时候,以子类的处理方法为主;

@ExceptionHandler(Exception.class)
    public HashMap<String,Object> doException(Exception e){
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",-1);
        result.put("msg","Exception: "+e.getMessage());
        result.put("data",null);
        return result;
}

三. 统一数据格式返回

 统一数据格式返回可以带来很多的方便:

1. 方便前端更好的接收和解析后端接口返回的数据;

2. 降低前后端之间的沟通成本;

3. 有利于项目后期的维护和修改;

主要使用注解:@ControllerAdvice + ResponseBodyAdvice 接口实现 ;

实现 ResponseBodyAdvice 接口的时候,需要重写 supports 方法 和 beforBodyWrite 方法,support 方法返回值 (布尔类型) 就决定于数据格式是否重写,数据重写是发生在给用户返回数据之前。beforeBodyWrite 方法就是重写数据格式,此方法中的参数 Object body 指的就是原始返回值;

对于统一数据格式返回,不是实现数据返回,可以理解为是为数据加工处理的一步,所以一般不加 @ResponseBody ;

在 beforeBodyWrite 方法中,主要关注参数 Object body, 指的是方法体的返回值,方法实现的时候就是针对这个值进行数据加工,让这个值以统一数据格式返回,

@ControllerAdvice
//@ResponseBody           // 他不是实现数据返回的,他可以理解为数据加工处理的一步,所以可以不加
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
//    数据的重写,执不执行取决于 supports 方法,true的时候才执行
//    给用户返回数据之前,进行数据重写
//    body 指的是原始返回值
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//        主要关注 Object body,指的是方法体的返回值,对这个值进行数据加工
//        让这个数据以标准格式进行返回,此处假设标准格式为 hashMap<String,Object> -> code,msg,data
//        instanceof 判断当前类型
        if (body instanceof HashMap){
            return body;        // 满足 hashMap 的格式,直接返回
        }
//        不满足,重写返回结果,让其返回一个统一的数据格式      user/login 返回一个 int
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("data",body);    // 参数 body
        result.put("msg","");
        return result;
    }
}

String 格式的特殊处理 

但在统一数据格式返回的时候,如果遇到 String 类型的时候,会返回报错:大概意思是 HashMap 无法转换为 String 类型

此处可以对异常进行返回,也是因为前面的代码中做了统一异常处理;

对此我们作出解析,在返回的执行流程中,执行的顺序是:

1. 首先方法返回 String 类型:

 2. 统一数据返回之前处理:将 String 转为 HashMap 类型,也就是执行方法内容;

3. 最后将 HashMap 转换成 application / json 字符串给前端;

而这一步也正是报错对应的那一步:HashMap 无法转换为 String 类型

从原码的角度分析:

1. 当 body 的类型是 String 的时候,会使用 StringHttpMessageConverter 进行类型转换;而使用这个方法将 HashMap(对象) 转换为 String 是没办法的。

2. 当 body 的类型非 String 的时候, 会使用 HttpMessageConverter 进行类型转换; 

因此就有了两种解决方案:

1. 在配置中,将 StringHttpMessageConverter 去掉,那默认就会执行 HttpMessageConverter 方法,就不会出错:

// 当前的类作为系统配置项
// 解决方案一,去掉 StringHttpMessageConverter
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);
    }
}

2. 在统一数据重写的时候,单独处理 String 类型,让对象转换成 String,让其返回一个 String 字符串,而非HashMap; (采用 Jackson 的方法)

@ControllerAdvice
//@ResponseBody           // 他不是实现数据返回的,他可以理解为数据加工处理的一步,所以可以不加
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
//    数据的重写,执不执行取决于 supports 方法,true的时候才执行
//    给用户返回数据之前,进行数据重写
//    body 指的是原始返回值
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//        主要关注 Object body,指的是方法体的返回值,对这个值进行数据加工
//        让这个数据以标准格式进行返回,此处假设标准格式为 hashMap<String,Object> -> code,msg,data
//        instanceof 判断当前类型
        if (body instanceof HashMap){
            return body;        // 满足 hashMap 的格式,直接返回
        }

//        不满足,重写返回结果,让其返回一个统一的数据格式      user/login 返回一个 int
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("data",body);    // 参数 body
        result.put("msg","");

        //        解决特殊问题 String 类型,方案2
        if ( body instanceof String){
            try {
                return objectMapper.writeValueAsString(result);      // 将对象转换成字符串
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

 这样就可以解决问题了:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PlLI-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值