本文介绍: 本篇博客主要介绍Spring AOP在实际开发中的应用,也就是统一功能处理这里主要介绍统一登录校验统一前缀添加统一异常处理以及统一的数据格式返回。这些统一的功能处理我们项目中还是非常有用的。

目录

前言

一、统一登录校验

二、统一的异常处理

三、统一数据格式返回

为什么要对String类型进行特殊的处理呢?

前言

        本篇博客主要介绍Spring AOP在实际开发中的应用,也就是统一功能的处理这里主要介绍统一的登录校验,统一前缀添加,统一的异常处理以及统一的数据格式返回。这些统一的功能处理我们项目中还是非常有用的。

一、统一登录校验

        所谓的统一登录校验其实就是拦截器,主要的功能就是判断用户是否登录了,对于未登录用户进行一些页面的拦截。对于拦截器实现,我们可以采用Spring AOP中的环绕通知或者前置通知来实现,但是那样的实现还是比较麻烦的。要克服的问题主要是获取HttpSession对象定义拦截规则表达式是比较难写的,因为我们只对一个类中的部分方法拦截,不是全部拦截。所以我们这里就采用Spring中提供的拦截器(HandlerInterceptor)来实现。

使用HandlerInterceptor实现拦截器步骤

1.自定义拦截器:实现HandlerInterceptor接口,并实现里面preHandle(执⾏具体⽅法之前的预处理)方法;

2.将自定义拦截器配置系统配置中,并设置拦截规则:实现WebMvcConfigurer接口,实现里面addInterceptors(增加拦截规则方法)方法

自定义拦截器实现代码

package com.example.blog_interceptor.common;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 在preHandle方法中,我们返回true,就代表可以继续往后执行后续方法
 * 返回false就代表拦截器拦截成功,不再执行后续方法
 */
@Component//加此注解主要是为了该类能在spring启动加载容器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是否为空为空就是未登录
            return true;
        }
        response.sendRedirect("/user/login");//访问失败跳转到登录页
        return false;
    }
}

 将自定义拦截器加入到系统配置中,实现代码

package com.example.blog_interceptor.config;
import com.example.blog_interceptor.common.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 首先传入我们自定义的拦截器,接着拦截所有接口,再根据需求进行放行部分接口
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")//先拦截所有的接口和方法,再根据需求放行部分接口
                .excludePathPatterns("/user/login")//放行登录接口
                .excludePathPatterns("/user/reg");//放行注册接口
    }
}

        接下来我们就在UserController类中验证一下我们的拦截器是否生效了。UserCOntroller类代码如下:

访问login方法:

 访问personalCenter方法:

        我们在代码中写到了,当访问除了login和reg方法之外,都会被拦截,然后跳转至Login方法,所以当我们访问/user/center时依旧看到的是login方法的内容。 

二、统一的异常处理

        在我们一个正常的程序中,总会有运行出现异常的情况,如果我们忘记使用trycatch去处理或者没有声明异常,那么程序级很有可能会崩溃,对于这种情况,我们就需要对我们无法处理到的异常做一手准备,也就是这里异常的统一处理。

统一异常处理的实现方法:

        创建一个统一异常处理类,添加@ControllerAdvice + @ExceptionHandler注解,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器两个结合表示当出现异常的时候执⾏某个通知, 也就是执⾏某个⽅法事件,具体实现代码如下:

package com.example.blog_interceptor.common;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice//不加这个注解的话该类就不会随着项目启动启动public class ExceptionAdvice {
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public String nullPointException(){
        return "出现了空指针异常";
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public String allException(){
        return "操作出现异常了";
    }
}

        对于以上代码我们需要注意的是: 我们需要@ExceptionHandle()中指定对应异常的类对象,这个方法才会根据相应的类对象来捕获相应的异常,但是我们的异常种类非常多,如果让我们一一列举出来是不现实的。所以我们就可以列出一些常见的异常,然后最后再加上所有异常的父类Exception,这样就可以保证所有异常都能被捕获了。

没有加统一异常处理功能,出现异常时出现的情况:

加了统一异常处理功能后,出现空指针异常时:

加了统一异常处理功能后,出现其他异常时:

        通过运行结果我们就可以看出我们已经可以成功的捕获异常了,这里我们只是简单的将结果显示页面上而已,在项目中我们是需要返回状态码、错误信息、等等给前端的。这里只是简单的演示效果而已。

三、统一数据格式返回

统一数据格式返回的好处:

1. ⽅便前端程序员更好接收解析后端数据接⼝返回数据

2. 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就⾏了,因为所有接⼝都是这样返回的。

3. 有利于项⽬统⼀数据的维护和修改

4. 有利于后端技术部⻔的统⼀规范的标准制定,不会出现稀奇古怪的返回内容

统一数据格式返回实现步骤

        在实现统一的数据格式返回之前我们一般是先规定数据返回的格式(其实就是一个类),然后每次都返回相同的对象给前端;定义的类如下所示

package com.example.blog_interceptor.common;
import lombok.Data;

/**
 * 定义返回的数据格式的类
 * 包括状态码,以及状态码描述信息,返回的数据对象
 * 类中还有success和fail方法,当我们能正确处理前端发来的请求时,就调用success方法,
 * 当无法正确处理前端发来的请求时,就调用
 */
@Data
public class ResultAjax {
    private int code;//状态码
    private String msg;//描述信息
    private Object data;//返回数据
    public static ResultAjax success(Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setData(data);
        return resultAjax;
    }

    public static ResultAjax fail(int code){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        return resultAjax;
    }

    public static ResultAjax fail(int code,String msg){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        return resultAjax;
    }

    public static ResultAjax fail(int code,String msg,Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(data);
        return resultAjax;
    }
}

        统⼀的数据返回格式可以使⽤ @ControllerAdvice 注解的⽅式实现,再实现ResponseBodyAdvice接口,重写的其中的supports()和beforeBodyWrite()方法,具体实现代码如下: 

package com.example.blog_interceptor.common;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * supports方法中只有返回true,才会走这里的统一格式返回
 */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    ObjectMapper objectMapper;
    @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 ResultAjax){//如果是ResultAjax格式就直接返回
            return body;
        }
        if(body instanceof String){
            try {
                ResultAjax.success(objectMapper.writeValueAsString(body));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
       return ResultAjax.success(body);
    }
}

对于以上代码的一些说明统一的数据格式返回可以说是一个保底策略,当我们不小心返回了非ResultAjax类型数据时,我们就对数据包装成ResultAjax进行返回,这也就是统一数据格式返回的意义所在。对于统一的数据格式返回,我们的实现思路就是:先判断要返回的对象是不是ResulAjax类型的,如果是,就直接返回就可以,如果不是就将要返回的数据设置为ResultAjax中的data,接着再返回就可以。

测试结果

为什么要对String类型进行特殊的处理呢?

当我们不对String类型进行特殊处理时,也不进行统一异常处理时,返回String类型出现的结果

        从上面的结果中我们可以看出,服务器报的异常就是类型转换异常,说ResultAjax转换成String类型时出错了。但是这里我们“好像没有做”把ResultAjax转换成String类型这件事,那为什么会报这个错误呢?我们可以继续往下看:

出现上面情况的具体原因如下:

首先,我们需要了解的是:我们这个统一返回的执行流程

1.首先是方法是需要返回一个String类型的数据;

2.统一数据返回之前:我们将String类型转成了ResultAjax中的Object类型;

3.将ResultAjax转换成application/json字符串返回给前端,在这一步就涉及到了将ResultAjax转换成String这一过程了。

        到这里我们就知道了问题是出在了将ResultAjax转换成application/json字符串返回给前端这一步了。那么在这里为什么会转换失败呢?其实是Spring的原因导致的,原本String类型在Java中就是比较特殊的存在,所以Spring在处理HTTP请求响应时,就有两种消息转换器分别是StringHttpMessageConverter和HttpMessageConverter,前一种是针对String类型的。在这里Spring是会根据原本body的类型进行转换,这里body原本类型是String,所以就会调用StringHttpMessageConverter进行转换,但是由于我们现在的类型是ResultAjax,所以就不能调用StringHttpMessageConverter的方法进行转换了,一旦调用了,程序就会报错。所以这里报错的原因也正是这个。

问题出处如下图所示

所以解决方案就有两个

1.一是我们手动的将ResultAjax数据转成json格式数组进行返回;

2.二是我们在项目指定不使用StringHttpMessageConverter进行转换

我们上面的代码中是使用了第一种方式,使用了Spring Boot中内置的jackson,将ResultAjax转换成了json格式数组进行返回。

第二种方式代码如下:

package com.example.blog_interceptor.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class MyConfig implements WebMvcConfigurer {
    /**
     * 移除 StringHttpMessageConverter
     * @param converters
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);
    }
}

原文地址:https://blog.csdn.net/m0_64986499/article/details/133745242

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_10231.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注