【SpringBoot】统一功能处理

目录

1 拦截器

1.1 拦截器的代码实现

1.2 拦截器的实现原理

2 拦截器应用——登录验证

3 异常统一处理

4 统一数据返回格式

4.1 为什么需要统一数据返回格式

4.2 统一返回对象

4.3 统一数据处理(强制执行)


本篇文章介绍 Spring Boot 的统一功能处理模块,也就是 AOP 的实战环节。

1 拦截器

没有登录的情况下,会跳转到登录页面。

1.1 拦截器的代码实现

Spring 提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤:

1. 创建自定义拦截器,实现 HandlerInterceptor 接口,重写 preHandle 方法。(执行具体方法之前的预处理)。

2. 将自定义拦截器加入 WebMvcConfigurer 的 addInterceptors 方法中。

package com.example.demo.configuration;

import com.example.demo.common.AppVar;
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;

/**
 * 自定义拦截器
 */
@Component
public class UserInterceptor implements HandlerInterceptor {

    /**
     * 返回 true -> 拦截器验证成功,继续执行后续的方法
     *     false -> 拦截器验证失败,不会执行后续的目标方法
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("do UserInterceptor"); // 拦截时候会打印

        // 业务方法
        HttpSession session = request.getSession(false);
        if(session != null &&
        session.getAttribute(AppVar.SESSION_KEY) != null){
            // 用户已经登录了
            return true; // 继续执行后续流程
        }
        // 未登录的情况,跳转到 百度
        response.sendRedirect("https://www.baidu.com");
        return false;
    }
}
package com.example.demo.common;

/**
 * 全局变量
 */
public class AppVar {
    // Session Key
    public static final String SESSION_KEY = "SESSION_KEY";
}

【SpringBoot】统一功能处理_第1张图片

package com.example.demo.configuration;

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;

/**
 * 将自定义拦截器加入到系统配置
 * 有两种写法
 * 一是 new 一个 UserInterceptor 对象
 * 二是 注入的方式
 */
@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Autowired
    private UserInterceptor userInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // registry.addInterceptor(new UserInterceptor());

        registry.addInterceptor(userInterceptor)
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/user/reg") // 登录页面不拦截
                .excludePathPatterns("/user/login") // 注册页面不拦截
        ;
    }
}

 * 一级路由,** 所有路由。

/user 就是一级路由,/user/reg 是二级路由。

addPathPatterns 表示需要拦截的 URL,** 表示拦截所有方法

excludePathPattern 表示需要排除的 URL 

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getuser")
    public String getUser(){
        System.out.println("do getUser()");
        return "getuser";
    }

    @RequestMapping("/reg")
    public String reg(){
        System.out.println("do reg()");
        return "reg";
    }

    @RequestMapping("/login")
    public String getlogin(){
        System.out.println("do login()");
        return "login";
    }
}

输出:

do UserInterceptor
do reg()
do login()

1.2 拦截器的实现原理

正常情况下的调用顺序:

【SpringBoot】统一功能处理_第2张图片

有了拦截器之后,在调用 controller 之前,会执行拦截器,如果为 true,则继续执行后续程序,如果为 false,则跳转相关页面。

【SpringBoot】统一功能处理_第3张图片

2 拦截器应用——登录验证

package com.example.demo.configuration;

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;

/**
 * 将自定义拦截器加入到系统配置
 * 有两种写法
 * 一是 new 一个 UserInterceptor 对象
 * 二是 注入的方式
 */
@Configuration

public class AppConfig implements WebMvcConfigurer {

    @Autowired
    private UserInterceptor userInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // registry.addInterceptor(new UserInterceptor());

        registry.addInterceptor(userInterceptor)
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/user/reg") // 登录页面不拦截
                .excludePathPatterns("/reg.html")
                .excludePathPatterns("/login.html")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/editor.md/**")
                .excludePathPatterns("/img/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/user/login") // 注册页面不拦截
                .excludePathPatterns("/image/**") // image 文件下所有的图片格式拦截
        ;
    }
}

除了注册页面、登录页面之外,其余页面都会跳转到百度。

3 异常统一处理

    @RequestMapping("/reg")
    public String reg(){
        System.out.println("do reg()");
        Object obj = null;
        System.out.println(obj.hashCode());
        return "reg";
    }

【SpringBoot】统一功能处理_第4张图片

【SpringBoot】统一功能处理_第5张图片

如果每个页面都出现异常,可不可以统一处理呢?

出现的所有异常按照统一的格式返回: 

package com.example.demo.common;

import lombok.Data;

/**
 * 统一返回对象
 */
@Data
public class ResultAjax {
    private int code;  // 状态码
    private String msg; // 状态码的描述信息
    private Object data; // 返回数据
}

异常统一处理时,需要两个注解。一个是 @ControllerAdvice / @RestControllerAdvice ,另一个是 @ExceptionHandler(Exception.class) 统一返回对象。

package com.example.demo.configuration;

import com.example.demo.common.ResultAjax;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(NullPointerException.class)
    public ResultAjax doNullPointerException(NullPointerException e){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(-1);
        resultAjax.setMsg("空指针异常:"+e.getMessage());
        resultAjax.setData(null);
        return resultAjax;
    }
}

【SpringBoot】统一功能处理_第6张图片

再举一个算术异常的例子:

由于所有的异常都继承自 Exception,所以  @ExceptionHandler(Exception.class) 里面的异常类不用写那么详细也可以:

    @RequestMapping("/login")
    public String login(){
        System.out.println("do login()");
        int num = 10 / 0;
        return "login";
    }
    @ExceptionHandler(Exception.class)
    public ResultAjax doException(Exception e){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(-1);
        resultAjax.setMsg("异常:"+e.getMessage());
        resultAjax.setData(null);
        return resultAjax;
    }

【SpringBoot】统一功能处理_第7张图片

 

4 统一数据返回格式

4.1 为什么需要统一数据返回格式

统⼀数据返回格式的优点有很多,⽐如以下⼏个:

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

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

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

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

4.2 统一返回对象

对 ResultAjax 这个类进行改造,添加两种方法,一是成功之后返回的数据,二是失败之后返回的数据。

package com.example.demo.common;

import lombok.Data;

/**
 * 统一返回对象
 */
@Data
public class ResultAjax {
    private int code;  // 状态码
    private String msg; // 状态码的描述信息
    private Object data; // 返回数据

    /**
     * 成功时返回
     * @param data
     * @return
     */
    public static ResultAjax success(Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setMsg("");
        resultAjax.setData(data);
        return resultAjax;
    }

    public static ResultAjax success(String msg, Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setMsg(msg);
        resultAjax.setData(data);
        return resultAjax;
    }
    
    public static ResultAjax fail(int code, String msg){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(null);
        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;
    }
    
}
package com.example.demo.controller;

import com.example.demo.common.ResultAjax;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getuser")
    public ResultAjax getUser(){
        System.out.println("do getUser()");
        return ResultAjax.success("getuser");
    }

    @RequestMapping("/reg")
    public ResultAjax reg(){
        System.out.println("do reg()");
        return ResultAjax.success("reg");
    }

    @RequestMapping("/login")
    public ResultAjax login(){
        System.out.println("do login()");
        return ResultAjax.success("login");
    }
}

 

【SpringBoot】统一功能处理_第8张图片

4.3 统一数据处理(强制执行)

如果就是有人不按要求返回 ResultAjax 这一格式呢?比如下面这样:

    @RequestMapping("getnum")
    public int getNum(){
        System.out.println("getNum()");
        return 1;
    }

【SpringBoot】统一功能处理_第9张图片

这时就可以对返回的数据进行统一处理,这是强制执行的。

使用:

1. @ControllerAdvice 

2. 实现 ResponseBodyAdvice 接口,并重写它的两个方法,supports 必须返回 true,beforeBodyWrite 方法中进行重新判断和重写操作。

package com.example.demo.configuration;

import com.example.demo.common.ResultAjax;
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;

@ControllerAdvice
public class ResponAdvice 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) {
        // 已经包装好的对象
        // body 是 ResultAjax 的格式
        if(body instanceof ResultAjax){
            return body;
        }

        // 对字符串进行判断和处理
        // 重新封装成 ResultAjax 的格式
        return ResultAjax.success(body);

    }
}

【SpringBoot】统一功能处理_第10张图片

    @RequestMapping("/getstr")
    public String getStr(){
        System.out.println("getStr()");
        return "whoooooo~~";
    }

 如果返回的是 String,而不是 int 类型,会报错。在把 String 转化成 json格式的时候报错了。所以对于返回类型是 String 的话,需要单独处理。不使用 String 解析引擎,而是手动转成 json。

【SpringBoot】统一功能处理_第11张图片

package com.example.demo.configuration;

import com.example.demo.common.ResultAjax;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
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;

@ControllerAdvice
public class ResponAdvice implements ResponseBodyAdvice {
//    springboot 框架自动注入
    @Autowired
    private 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) {
        // 已经包装好的对象
        // body 是 ResultAjax 的格式
        if(body instanceof ResultAjax){
            return body;
        }

        // 对字符串进行判断和处理
        // 手动转换成 json 格式
        if(body instanceof String){
            ResultAjax resultAjax = ResultAjax.success(body);
            try {
                return objectMapper.writeValueAsString(resultAjax);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }

        }
        
        return ResultAjax.success(body);

    }
}

 

【SpringBoot】统一功能处理_第12张图片

 

如果返回的是对象呢? 

    @RequestMapping("/usermsg")
    public User usermsg(){
        User user = new User();
        user.setId(263);
        user.setName("柳飘飘");
        user.setPassword("96134");
        return user;
    }

【SpringBoot】统一功能处理_第13张图片


【SpringBoot】统一功能处理_第14张图片 

你可能感兴趣的:(spring,boot,后端,java,java-ee)