100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【Spring AOP】如何统一“拦截器校验 数据格式返回 异常返回”处理?

【Spring AOP】如何统一“拦截器校验 数据格式返回 异常返回”处理?

时间:2019-04-06 12:17:45

相关推荐

【Spring AOP】如何统一“拦截器校验 数据格式返回 异常返回”处理?

目录

一、Spring 拦截器

1.1、背景

1.2、实现步骤

1.3、拦截原理

二、 统一url前缀路径

2.1、方法一:在系统的配置文件中设置

2.2、方法二:在 application.properies 中配置

三、统一异常处理

四、统一返回数据返回格式处理

4.1、背景

4.2、具体实现

4.3、实际项目中的写法

一、Spring 拦截器

1.1、背景

在原生的 Spring AOP 中实现统一的拦截的难点在于:1.定义拦截规则(表达式)很难,2.在切面类中拿到 HttpSession 比较难;如何解决这两个难点呢?使用拦截器!

1.2、实现步骤

实现一个普通的拦截器关键在于以下两步:

实现 HandlerInterceptor 接口,重写 preHandler 方法,在方法中编写自己的业务代码。将拦截器添加到配置文件中,设置拦截规则。

具体的,首先步骤一,例如要实现一个用户登录判断,就需要创建一个类,这里起名叫LoginInterceptor 类,实现HandlerInterceptor 接口,重写preHeadler 方法(此方法返回的是以个 boolean 类型,如果为 true 表示验证成功,可以继续执行后面的流程,若是 false 表示验证失败,后面的流程就不执行了),通过是否可以获取到 Session 信息判断用户是否已经登陆,来返回 true 或 false。

a)实现 HandlerInterceptor 接口,重写preHandler方法,如下代码:

import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;//登录拦截器public class LoginInterceptor implements HandlerInterceptor {/*** 此方法返回一个 boolean,若为 true 表示验证成功,否则验证失败,后面的流程不能执行了* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 用户登录业务判断HttpSession session = request.getSession(false);if(session != null && session.getAttribute("userinfo") != null) {//说明用户已经登陆return true;}//可以调整登录页面,或者 返回一个 401/403 没有权限response.sendRedirect("/login.html");return false;}}

b)将拦截器添加到配置文件中,设置拦截规则

import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class AppConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //连接所有请求,值得注意的是,这里不能只写一个 *,一个 * 表示一级路径.excludePathPatterns("/user/login") //不拦截的 url.excludePathPatterns("/user/reg").excludePathPatterns("/**/*.html"); //不拦截所有的页面}}

注意:

addPathPatterns:表示需要拦截的 URL,“**”表示拦截任意⽅法(也就是所有⽅法)。 excludePathPatterns:表示需要排除的 URL。

说明:以上拦截规则可以拦截此项⽬中的使⽤ URL,包括静态⽂件(图⽚⽂件、JS 和 CSS 等⽂件)

1.3、拦截原理

二、 统一url前缀路径

2.1、方法一:在系统的配置文件中设置

具体的,重写WebMvcConfigurer 接口下的configurePathMatch 方法,例如修改所有请求url添加前缀 /zhangsan ,如下代码:

import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class AppConfig implements WebMvcConfigurer {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.addPathPrefix("/zhangsan", c -> true);}}

2.2、方法二:在 application.properies 中配置

例如修改所有请求url添加前缀 /zhangsan,如下代码:

server.servlet.context-path=/zhangsan

三、统一异常处理

统一异常处理是通过如下两个注解结合实现的:

@ControllerAdvice:表示控制器通知类。@ExceptionHandler:表示异常处理器。

两个结合表示出现异常的时候执行某个通知方法,具体的步骤如下:

创建一个类,标识上 @ControllerAdvice;在方法上添加 @ExceptionHandler;

如下代码:

import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;@ControllerAdvice@ResponseBodypublic class MyExHandler {/*** 拦截所有的空指针异常,进行统一的数据返回* @param e* @return*/@ExceptionHandler(Exception.class) //这里也可以根据实际情况,填写不同的异常public HashMap<String, Object> nullException(NullPointerException e) {HashMap<String, Object> reslut = new HashMap<>();reslut.put("code", "-1");reslut.put("msg", "空指针异常" + e.getMessage());reslut.put("data", null);//这里返回 HashMap ,就相当于项前端返回了一个 JSON 格式的数据return reslut;}}

四、统一返回数据返回格式处理

4.1、背景

为什么要统一数据返回格式处理?例如以下几个原因:

方便前端程序员更好的接收和解析后端返回的数据;降低约定前后端交互接口的成本,按照某种格式实现即可,因为所有的接口都是这样返回的。有利于项目的统一数据的维护和修改。

4.2、具体实现

统一数据格式返回的实现需要以下两个步骤:

创建一个类,并添加 @ControllerAdvice。实现 ResponseBodyAdvice 接口,重写 supports 和 beforeBodyWrite。

Ps:

1、supports 方法不用编写业务逻辑,而是像一个控制器一样,返回 true 则执行beforeBodyWrite 方法,反之则不执行。

2、beforeBodyWrite 方法就是用来实现统一对象的。

具体的如下:

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;import java.util.HashMap;@ControllerAdvicepublic class ResponseAdvice implements ResponseBodyAdvice {//将 java 对象转化成 JSON 格式@Autowiredprivate ObjectMapper objectMapper;/*** 此方法返回 true 则执行下面的 beforeBodyWrite 方法,反之则不执行* @param returnType* @param converterType* @return*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {HashMap<String, Object> result = new HashMap<>();result.put("code", 200);result.put("msg", "");result.put("data",body);// 这里需要进行特殊处理,因为 String 在转换的时候报错if(body instanceof String) {try {return objectMapper.writeValueAsString(result);} catch (JsonProcessingException e) {e.printStackTrace();}}return result;}}

代码中为什么要进行特殊处理?(最易出错!)

在 java 程序中, String 是一个最特殊的类型(既不是基础类型也不是对象),并且在重写方法时也很特殊,除了 String 其他的都是用一个格式化工具,而 String 用的是自己的一套格式化工具,因此在转换成 HashMap 的时候还没有被加载好,而其他的转换器已经加载好了,最后就会引发如下异常:

因此就要判断 body 是否为 String ,若为 String 类型,就要进行特殊处理,使用 JSON 的writeValueAsString 方法将 Java 对象转换成 JSON 格式再返回。

4.3、实际项目中的写法

实际的项目中,统一数据格式的返回我们因该这样写:

创建一个类(自定义类名为:AjaxResult)约束为 JSON 返回格式。创建一个类(自定义类名为:ResponseAdvice)实现 ResponseBodyAdvice 接口,重写其中的两个方法,一个是“开关”,另一个是用来对数据的格式进行校验和封装(注意:方法中的参数body 若为 Spring 类型,比较特殊,需要使用 ObjectMapper 单独进行JSON 格式处理)。

具体的如下代码:

注意:@Data 注解一定不能忘!!!

a)AjaxResult 类

import lombok.Data;import java.io.Serializable;/*** JSON统一数据格式返回*/@Datapublic class AjaxResult implements Serializable {//状态码private Integer code;//状态码描述信息private String msg;//返回的数据private Object data;/*** 操作成功返回的结果*/public static AjaxResult success(Object data) {AjaxResult result = new AjaxResult();result.setCode(200);result.setMsg("");result.setData(data);return result;}public static AjaxResult success(int code, Object data) {AjaxResult result = new AjaxResult();result.setCode(code);result.setMsg("");result.setData(data);return result;}public static AjaxResult success(int code, String msg, Object data) {AjaxResult result = new AjaxResult();result.setCode(200);result.setMsg(msg);result.setData(data);return result;}/*** 返回失败结果*/public static AjaxResult fail(int code, String msg) {AjaxResult result = new AjaxResult();result.setCode(code);result.setMsg(msg);result.setData(null);return result;}public static AjaxResult fail(int code, String msg, Object data) {AjaxResult result = new AjaxResult();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}}

b)ResponseAdvice 类

import com.mon.AjaxResult;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.SneakyThrows;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;/*** 实现统一数据返回的保底类* 说明:在返回数据之前,检测数据类型是否为统一对象,如果不是就封装*/@ControllerAdvicepublic class ResponseAdvice implements ResponseBodyAdvice {@Autowiredprivate ObjectMapper objectMapper;/*** 开关* @param returnType* @param converterType* @return*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}/*** 对数据格式进行校验和封装* @param body* @param returnType* @param selectedContentType* @param selectedConverterType* @param request* @param response* @return*/@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if(body instanceof AjaxResult) {return body;}//String比较特殊,要单独处理if(body instanceof String) {return objectMapper.writeValueAsString(AjaxResult.success(body));}return AjaxResult.success(body);}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。