建网站好,国外最开放的浏览器,网站价格评估 优帮云,郑州好的企业网站建设目录
一、用户登录权限验证
1.1 SpringAOP可以进行处理吗#xff1f;
1.2 创建自定义拦截器 1.3 将自定义拦截器配置到系统配置项中
1.4 拦截器的实现原理
1.4.1 实现原理源码分析
1.5 统一访问前缀添加
二、统一异常处理
2.1 为什么需要使用统一异常处理#xff1f;…目录
一、用户登录权限验证
1.1 SpringAOP可以进行处理吗
1.2 创建自定义拦截器 1.3 将自定义拦截器配置到系统配置项中
1.4 拦截器的实现原理
1.4.1 实现原理源码分析
1.5 统一访问前缀添加
二、统一异常处理
2.1 为什么需要使用统一异常处理
2.2 统一异常处理的实现
三、统一数据返回格式
3.1 为什么需要统一数据返回格式
3.2 统一数据返回格式的实现 3.3 返回值为String类型时应该如何处理
3.3.1 将 StringHttpMessageConverter 去掉。
3.3.2 在统一数据返回的时候单独处理String类型让其返回一个String字符串而非 HashMap 总结 前言
一般Spring Boot统一功能处理模块也是AOP的实战环节要实现课程目标有以下3个
统一用户登录权限验证统一数据格式统一异常处理
一、用户登录权限验证
1.1 SpringAOP可以进行处理吗
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
Aspect
Component
public class UserAspect {// 定义切点⽅法 controller 包下、⼦孙包下所有类的所有⽅法Pointcut(execution(* com.example.demo.controller..*.*(..)))public void pointcut(){ }// 前置⽅法Before(pointcut())public void doBefore(){}// 环绕⽅法Around(pointcut())public Object doAround(ProceedingJoinPoint joinPoint){Object obj null;System.out.println(Around ⽅法开始执⾏);try {// 执⾏拦截⽅法obj joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println(Around ⽅法结束执⾏);return obj;}
}我们知道SpringAOP虽然就提供了对用户登录的处理逻辑但是存在一些问题
没有办法获取HttpSession对象如果要对一部分方法拦截一部分方法不拦截这种情况很难处理。比如注册和登录方法在用户登录权限验证中是不能进行拦截的 拦截器和SpringAOP虽然都是AOP的实现方式但是这两个其实是完全不同的技术体系。 Spring提供了具体的实现拦截器HandlerInterceptor该SpringBoot 拦截器实现分为以下两个步骤
自定义拦截器将自定义拦截器配置到系统配置项并且设置合理的拦截规则
1.2 创建自定义拦截器
自定义拦截器继承HandlerInterceptor后需要重写相对应的方法这里我们重写 preHandle方法 代码如下
package com.example.demo.common;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标识验证成功,程序会继续执行后续流程* 返回false, 表示拦截器拦截失败, 验证未通过, 后续的流程和目标方法不再执行。* 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(session_userinfo) ! null) {// 用户已经登录return true;}return false;}
}1.3 将自定义拦截器配置到系统配置项中 重写addInterceptors方法 设置拦截规则代码如下
package com.example.demo.config;import com.example.demo.common.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;public class MyConfig implements WebMvcConfigurer {AutowiredLoginInterceptor loginInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns(/**) //拦截所有的url.excludePathPatterns(user/login) // url为user/login 不进行拦截 以下同理.excludePathPatterns(user/reg).excludePathPatterns(image/**); // image夹目录下的所有url都不进行拦截}
}或者使用Spring方法通过DI注入的方式这样可以实现不用new一个实例
首先需要将拦截器添加到spring中也就是给其添加一个五大类注解这里我们就使用Component。接着就可以使用Autowired来得到实例。 其中
addPathPatterns: 表示需要拦截的URL“**”表示拦截任意方法也就是所有方法。excludePathPatterns: 表示需要排除的URL。 说明以上拦截规则可以拦截此项目中的URL包括静态文件图片文件JS和CSS等文件。
1.4 拦截器的实现原理
下面我们先来看一组正常情况下的调用顺序 然而有了拦截器之后会在调用Controller之前进行相应的业务处理执行的流程如下图所示 1.4.1 实现原理源码分析
所有的Controller执行都会通过一个调度器DispatcherServlet来实现这一点可以从Spring Boot控制台的打印信息看出如下图所示 在IDEA中通过全局搜索doDispatch方法代码如下 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest request;HandlerExecutionChain mappedHandler null;boolean multipartRequestParsed false;WebAsyncManager asyncManager WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv null;Object dispatchException null;try {processedRequest this.checkMultipart(request);multipartRequestParsed processedRequest ! request;mappedHandler this.getHandler(processedRequest);if (mappedHandler null) {this.noHandlerFound(processedRequest, response);return;}HandlerAdapter ha this.getHandlerAdapter(mappedHandler.getHandler());String method request.getMethod();boolean isGet HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified ha.getLastModified(request, mappedHandler.getHandler());if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 实现Controller的业务逻辑mv ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception var20) {dispatchException var20;} catch (Throwable var21) {dispatchException new NestedServletException(Handler dispatch failed, var21);}this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);} catch (Exception var22) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException(Handler processing failed, var23));}} finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler ! null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else if (multipartRequestParsed) {this.cleanupMultipart(processedRequest);}}}观察DispatcherServlet中的某段代码 我们发现在执行后续的Controller代码之前都会先执行这个applyPreHandle方法于是鼠标双击 applyPreHandle得到代码如下
从上述源码可以看出在applyPreHandle中会获取所有的拦截器HandlerInterceptor并执行拦截器中的preHandle方法这样就和之前定义的拦截器对应上了如下图所示
package com.example.demo.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;/*** 自定义拦截器*/
Component
public class LoginInterceptor implements HandlerInterceptor {/*** 以下方法是调用目标方法之前执行的方法。此方法返回boolean类型的值* 返回true标识验证成功,程序会继续执行后续流程* 返回false, 表示拦截器拦截失败, 验证未通过, 后续的流程和目标方法不再执行。* 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(session_userinfo) ! null) {// 用户已经登录return true;}response.setContentType(application/json);response.setCharacterEncoding(utf8);response.getWriter().println(asdasd);return false;}
}1.5 统一访问前缀添加
所有请求地址添加api前缀 代码如下
package com.example.demo.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;Configuration
public class AppConfig implements WebMvcConfigurer {Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.addPathPrefix(fox, c - true);}
}二、统一异常处理
2.1 为什么需要使用统一异常处理
通俗来讲统一异常处理的主要目的是为了方便前端让其更好的处理后端的信息尽量将逻辑处理这块放置于后端前端的目的其实主要是为用户服务。
比如可以跟前端约定出现异常报错时候的状态码是多少这样方便前端的处理也方便后续后端在日志文件中将其找到并修改异常。
2.2 统一异常处理的实现
统一异常处理使用的是ControllerAdvice ExceptionHandler 来实现的ControllerAdvice表示控制器通知类, ExceptionHandler是异常处理器两个结合表示当出现异常的时候执行某个通知也就是执行某个方法事件具体实现代码如下 以上方法表示如果出现了异常就返回给前端一个HashMap对象 其中包含的字段如代码定义那样。 注意 方法名和返回值都是可以自定义的另外 ExceptionHandler中的参数是可以选择的这里是Exception.class表示的是可以在程序抛出异常的时候执行这里的代码让其返回数据给前端如果填入的参数是NullPointerException那么表示的是当程序出现空指针异常的时候会执行这里的代码。
这里的实现逻辑和Java中的异常处理是相似的如果开发者有对Exception和NullPointerException分别进行了处理那么当程序出现NullPointerException异常的时候还是会根据我们写的NullPointerException执行逻辑进行处理并不会直接走Exception的逻辑。 示例如下 访问页面后效果如下 总结:当有多个异常通知时匹配顺序为当前类及其子类向上依次匹配。
三、统一数据返回格式
3.1 为什么需要统一数据返回格式
统一数据返回格式的优点如下比如以下几个
方便前端程序员更好的接受和解析后端数据接口的数据降低前端程序员和后端程序员的沟通成本有利于项目统一数据的维护和修改有利于后端技术部门的统一规范的标准制定不会出现稀奇古怪的返回内容
3.2 统一数据返回格式的实现
统一的数据返回格式可以使用ControllerAdvice ResponseBodyAdvice 的方式实现具体实现代码如下
package com.example.demo.common;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;/*** 统一数据格式处理*/
ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {/*** 是否执行 beforeBodyWrite 方法, 返回 true 就执行, 返回 false 就不执行* 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*/Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 这里我们规定统一的数据返回为HashMapif (body instanceof HashMap) {return body;}// 重写返回结果让其返回一个统一的数据格式HashMapString, Object result new HashMap();result.put(code,200);result.put(data,body);result.put(msg,);return result;}
}访问user/login1
经过统一功能处理后代码展现如下 3.3 返回值为String类型时应该如何处理 但是如果将返回值改为String类型按照以上的执行逻辑那么就无法走上述的正常数据统一处理 我们发现当返回类型为String的时候程序会抛出异常从而被我们的 统一异常处理模块拦截。 观察异常信息发现 抛出异常java.lang.ClassCastException: java.util.HashMap cannot be cast to java.lang.String 可能会感到奇怪为什么会抛出这个异常呢 下面我们来看看后端返回前端时候的执行流程
1. 一开始前端访问该网址时方法返回的是 String 2. 统一数据返回之前会进行处理将 String 转换为 HashMap 3. 将HaspMap转换成 application/json 字符串给前端接口
这个步骤有两种情况先判断原Body的类型
是 String 类型那么就会使用 StringHttpMessageConverter 进行类型转换如果不是 String 类型那么使用 HttpMessageConverter 进行类型转换
以上报错就是因为原始Body是String类型所以在类型转换时候报错了
解决方案有如下两种
将 StringHttpMessageConverter 去掉。在统一数据返回的时候单独处理String类型让其返回一个String字符串而非HashMap 3.3.1 将 StringHttpMessageConverter 去掉。 在配置文件中使用以下代码即可
package com.example.demo.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*/Overridepublic void configureMessageConverters(ListHttpMessageConverter? converters) {converters.removeIf(converter - converter instanceof StringHttpMessageConverter);}
}访问地址后显示如下 3.3.2 在统一数据返回的时候单独处理String类型让其返回一个String字符串而非 HashMap 引入ObjectMapper(ObjectMapper 是Jackson库中的一个类用于在Java对象POJOPlain Old Java Objects和JSON数据之间进行相互转换 对Body为String进行单独处理 访问页面如下所示 总结
本文主要介绍了统一用户登录权限的效验使用WebMvcConfigurer HandlerInterceptor 来实现。统一异常处理使用 ControllerAdvice ExceptionHandler 来实现统一返回值处理使用ControllerAdvice ResponseBodyAdvice来处理。