当前位置: 首页 > news >正文

嘉兴网站建设方案咨询平台app制作哪家好

嘉兴网站建设方案咨询,平台app制作哪家好,石家庄网络公司查封,WordPress分享到笔记目录 一、用户登录权限验证 1.1 SpringAOP可以进行处理吗? 1.2 创建自定义拦截器 1.3 将自定义拦截器配置到系统配置项中 1.4 拦截器的实现原理 1.4.1 实现原理源码分析 1.5 统一访问前缀添加 二、统一异常处理 2.1 为什么需要使用统一异常处理?…

目录

一、用户登录权限验证

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. 将自定义拦截器配置到系统配置项,并且设置合理的拦截规则

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;}// 重写返回结果,让其返回一个统一的数据格式HashMap<String, 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(List<HttpMessageConverter<?>> converters) {converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);}
}

 访问地址后显示如下:

3.3.2 在统一数据返回的时候,单独处理String类型,让其返回一个String字符串,而非 HashMap

引入ObjectMapper(ObjectMapper 是Jackson库中的一个类,用于在Java对象(POJO,Plain Old Java Objects)和JSON数据之间进行相互转换):

对Body为String进行单独处理: 

访问页面如下所示:

 总结:

本文主要介绍了统一用户登录权限的效验,使用WebMvcConfigurer + HandlerInterceptor 来实现。统一异常处理使用 @ControllerAdvice + @ExceptionHandler 来实现,统一返回值处理使用@ControllerAdvice + ResponseBodyAdvice来处理。

http://www.15wanjia.com/news/155100.html

相关文章:

  • uniapp怎么做淘客网站网站建设口号
  • 方太网站谁做的搜索引擎作弊网站有哪些
  • dedecms学校网站模板免费下载明朝传奇网页游戏
  • 网站和域名都注册怎么连接成网址87网站一起做
  • 做网站用什么cms 知乎wordpress怎么设置网站描述
  • flash网站教程大凤号 网站建设
  • 百度站长怎么做网站维护欧美购物网站排名
  • 网站查询访问域名设计发明的网站
  • 为企业开发网站网站弹窗无法显示
  • 长沙网站推广seo安卓商城
  • 怎么做app网站建筑图片大全高清
  • 武安网站建设中国建设报官网
  • 用discuz做网站知名建站企业哪家好
  • php素材网站源码免费下载大中型网站开发价格
  • 做图有什么网站传奇游戏电脑版
  • 51单片机可以做网站内蒙古城乡建设部网站首页
  • 南京网站设南京网站设计计气球网站建设
  • 青岛学校论坛网站建设施工企业降本增效的方法和措施
  • 长沙seo网站排名优化公司建设部网站退休注册人员
  • 怎么找网站是由什么建的海东网站建设
  • wap网站 全屏html好看的颜色代码
  • 包头seo优化seo案例网站
  • 郑州专业网站设计wordpress框架文件上传
  • 网站优化及推广公司北京网站优化推广方案
  • easyui 网站设计广东seo网站推广
  • 网站主题下载哪个网站专做二手相机
  • 人才网站建站建设校园网站的好处
  • 电子商务网站建设讨论专业的购物网站定制
  • 网站公众号建设工具跨境电商网站建设主管岗位职责
  • 教你如何建网站微信商城分销系统