package com.xxxx.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ModelAndViewDefiningException;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects; /**
* 基础拦截器,通过@Configuration自行配置为Bean,可以配置成多个拦截器。
*
* @author obiteaaron
* @since 2019/12/26
*/
public class BaseInterceptor implements AsyncHandlerInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseInterceptor.class); private ApplicationContext applicationContext; protected InterceptorPreHandler interceptorPreHandler; @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean checkResult = interceptorPreHandler.check(request, response, handler);
if (!checkResult) {
postInterceptor(request, response, handler);
return false;
} else {
return true;
}
} /**
* 拦截后处理
*/
protected void postInterceptor(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果被拦截,返回信息
if (((HandlerMethod) handler).getMethodAnnotation(ResponseBody.class) != null) {
// 返回json
HandlerMethod handlerMethod = new HandlerMethod(((HandlerMethod) handler).getBean(), ((HandlerMethod) handler).getMethod());
Object returnValue = interceptorPreHandler.getResponseBody();
MethodParameter returnValueType = handlerMethod.getReturnValueType(returnValue);
applicationContext.getBean(RequestMappingHandlerAdapter.class).getReturnValueHandlers();
RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = findRequestResponseBodyMethodProcessor();
requestResponseBodyMethodProcessor.handleReturnValue(returnValue, returnValueType, new ModelAndViewContainer(), new ServletWebRequest(request, response));
// end
} else {
// 返回页面
HandlerMethod handlerMethod = new HandlerMethod(((HandlerMethod) handler).getBean(), ((HandlerMethod) handler).getMethod());
String viewName = interceptorPreHandler.getViewName();
MethodParameter returnValueType = handlerMethod.getReturnValueType(viewName);
ViewNameMethodReturnValueHandler viewNameMethodReturnValueHandler = findViewNameMethodReturnValueHandler();
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();
// viewNameMethodReturnValueHandler 内的实现非常简单,其实可以不用这个的,直接new ModelAndViewContainer()就好了。
viewNameMethodReturnValueHandler.handleReturnValue(viewName, returnValueType, modelAndViewContainer, new ServletWebRequest(request, response)); // 抛出异常由Spring处理
ModelMap model = modelAndViewContainer.getModel();
ModelAndView modelAndView = new ModelAndView(modelAndViewContainer.getViewName(), model, modelAndViewContainer.getStatus());
throw new ModelAndViewDefiningException(modelAndView);
// end
}
} private RequestResponseBodyMethodProcessor findRequestResponseBodyMethodProcessor() {
RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
for (HandlerMethodReturnValueHandler value : Objects.requireNonNull(requestMappingHandlerAdapter.getReturnValueHandlers())) {
if (value instanceof RequestResponseBodyMethodProcessor) {
return (RequestResponseBodyMethodProcessor) value;
}
}
// SpringMVC的环境下一定不会走到这里
throw new UnsupportedOperationException("cannot find RequestResponseBodyMethodProcessor from RequestMappingHandlerAdapter by Spring Context.");
} private ViewNameMethodReturnValueHandler findViewNameMethodReturnValueHandler() {
RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
for (HandlerMethodReturnValueHandler value : Objects.requireNonNull(requestMappingHandlerAdapter.getReturnValueHandlers())) {
if (value instanceof ViewNameMethodReturnValueHandler) {
return (ViewNameMethodReturnValueHandler) value;
}
}
// SpringMVC的环境下一定不会走到这里
throw new UnsupportedOperationException("cannot find ViewNameMethodReturnValueHandler from RequestMappingHandlerAdapter by Spring Context.");
} public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
} public void setInterceptorPreHandler(InterceptorPreHandler interceptorPreHandler) {
this.interceptorPreHandler = interceptorPreHandler;
} public interface InterceptorPreHandler {
/**
* @see HandlerInterceptor#preHandle(HttpServletRequest, HttpServletResponse, Object)
*/
boolean check(HttpServletRequest request, HttpServletResponse response, Object handler); /**
* 拦截后返回的视图名称
*
* @see ModelAndView
* @see ViewNameMethodReturnValueHandler
*/
String getViewName(); /**
* 拦截后返回的对象
*
* @see ResponseBody
* @see RequestResponseBodyMethodProcessor
*/
Object getResponseBody();
}
}

SpringBoot版本:2.1.6.RELEASE
SpringMVC版本:5.1.8.RELEASE

SpringMVC拦截器
比如说在SpringMVC Web环境下,需要实现一个权限拦截的功能,一般情况下,大家都是实现了org.springframework.web.servlet.AsyncHandlerInterceptor或者org.springframework.web.servlet.HandlerInterceptor接口,从而实现的SpringMVC拦截。而要实现拦截功能,通常都是通过preHandle方法返回false拦截。

拦截器的preHandle
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}

那么拦截后,如果你什么都不做,会直接返回空页面,页面上什么也没有。如果要返回结果,需要自己给response写数据。简单的写法网上很多,这里不赘述,这里将会讲解如何通过SpringMVC本身的处理机制在拦截后返回结果。

拦截后返回结果
拦截后返回数据通常是两种,第一种如果是返回的Restful接口,那么返回一个json数据即可,第二种如果返回的是一个页面,那么需要返回错误页面(比如无权限页面)。

SpringMVC的所有结果都是通过HandlerMethodReturnValueHandler接口的实现类返回的,无论是Json,还是View。因此可以通过具体的实现类返回我们想要的数据。与之对应的还有``,这些所有的参数和返回结果的处理器,都定义在RequestMappingHandlerAdapter中,这个Adapter可以从容器中获取到。这里我们主要用到的只有两个RequestResponseBodyMethodProcessor和ViewNameMethodReturnValueHandler。

返回纯数据
返回纯数据,适用于返回Controller的方法通过@ResponseBody标注了。因此需要用到RequestResponseBodyMethodProcessor。
RequestResponseBodyMethodProcessor里面对不同的数据会有不同的处理方式,一般都是处理为json,具体实现可以看HttpMessageConverter的实现类。这里是直接将结果写到了response中。实现代码在文末。

返回视图
返回视图,适用于返回Controller的方法通过是个String,其实是ViewName。因此需要用到ViewNameMethodReturnValueHandler。

通过查看DispatcherServlet代码会发现,其实preHandle方法执行在RequestMappingHandlerAdapter执行前,所以没有ModelAndView生成,因此需要自己向Response里面写数据。这里只是借助了RequestMappingHandlerAdapter生产需要写入的数据。然后通过抛出异常ModelAndViewDefiningException,从而将我们的生产的ModeAndView透出给Spring进行渲染DispatcherServlet#processDispatchResult。

实现代码在文末。

直接使用视图解析器方法
如果你知道自己的视图解析器是谁,那么还有一个方法,比如,我用的是Velocity的视图解析器,Velocity的视图解析器配置的beanName是velocityViewResolver,因此可以用下面的方法实现。

SpringBoot下注册拦截器:org.springframework.web.servlet.config.annotation.WebMvcConfigurer#addInterceptors

结尾
其他类型的实现,可以自行实现。

 

springboot拦截器过滤token,并返回结果及异常处理的更多相关文章

  1. SpringBoot拦截器中Bean无法注入(转)

    问题 这两天遇到SpringBoot拦截器中Bean无法注入问题.下面介绍我的思考过程和解决过程: 1.由于其他bean在service,controller层注入一点问题也没有,开始根本没意识到Be ...

  2. 【SpringBoot】SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener

    =================6.SpringBoot拦截器实战和 Servlet3.0自定义Filter.Listener ============ 1.深入SpringBoot2.x过滤器Fi ...

  3. springboot拦截器总结

    Springboot 拦截器总结 拦截器大体分为两类 : handlerInterceptor 和 methodInterceptor 而methodInterceptor 又有XML 配置方法 和A ...

  4. Java结合SpringBoot拦截器实现简单的登录认证模块

    Java结合SpringBoot拦截器实现简单的登录认证模块 之前在做项目时需要实现一个简单的登录认证的功能,就寻思着使用Spring Boot的拦截器来实现,在此记录一下我的整个实现过程,源码见文章 ...

  5. Springboot拦截器实现IP黑名单

    Springboot拦截器实现IP黑名单 一·业务场景和需要实现的功能 以redis作为IP存储地址实现. 业务场景:针对秒杀活动或者常规电商业务场景等,防止恶意脚本不停的刷接口. 实现功能:写一个拦 ...

  6. SpringBoot拦截器及源码分析

    1.拦截器是什么 java里的拦截器(Interceptor)是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止 ...

  7. SpringBoot拦截器中无法注入bean的解决方法

    SpringBoot拦截器中无法注入bean的解决方法 在使用springboot的拦截器时,有时候希望在拦截器中注入bean方便使用 但是如果直接注入会发现无法注入而报空指针异常 解决方法: 在注册 ...

  8. Springboot拦截器未起作用

    之前遇到要使用springboot拦截器却始终未生效的状况,查了网上的博客,大抵都是@Component,@Configuration注解未加,或是使用@ComponentScan增加包扫描,但是尝试 ...

  9. SpringMvc Controller请求链接忽略大小写(包含拦截器)及@ResponseBody返回String中文乱码处理

    SpringMvc Controller请求链接忽略大小写(包含拦截器)及@ResponseBody返回String中文乱码处理... @RequestMapping(value = "/t ...

  10. SpringBoot拦截器中service或者redis注入为空的问题

    原文:https://my.oschina.net/u/1790105/blog/1490098 这两天遇到SpringBoot拦截器中Bean无法注入问题.下面介绍我的思考过程和解决过程: 1.由于 ...

随机推荐

  1. 2024年9月中国数据库排行榜:openGauss系多点开花,根社区优势明显

    在墨天轮发布的9月中国数据库流行度排行榜中,中国数据库产业格局进一步聚集刷新,呈现出3大显著特征: 开源势力力争上游显优势领先潮流: openGauss 开源根社区优势明显: 阿里华为两极鼎立云上云下 ...

  2. 墨天轮访谈 | OceanBase 白超:海量数据管理,为什么选择OceanBase?

    分享嘉宾:白超(大窑) OceanBase解决方案架构师.前蚂蚁集团数据库团队DBA专家 整理:墨天轮社区 导 读 大家好,我是白超(花名:大窑),在过去的几年中,作为蚂蚁集团数据库SRE团队成员,经 ...

  3. 开源的口袋妖怪自走棋「GitHub 热点速览」

    作为一名 90 后,我对口袋妖怪(宝可梦)游戏有着特殊的感情,满满的都是回忆.如果你也喜欢宝可梦主题的游戏,这款开源的宝可梦自走棋游戏 pokemonAutoChess 一定要试试,它采用战棋(自走棋 ...

  4. ToDesk云电脑堪比万元PC?黑悟空、老头环及战锤40K实测体验!

    2009年,OnLive首次在旧金山游戏开发者大会推出"云游戏"的概念,但受限于当时的网络宽带技术,云游戏并不被十分看好.现如今,5G时代已然到来,数据通量和画质传输给予云游戏崛起 ...

  5. 工作中的技术总结_JQuery_20210825

    工作中的技术总结_JQuery_20210825 JQuery此前接触不多,所以先把此次接触的一些基本操作 1.DOM节点的取值或者赋值: 语法: $(selector).val(value) 参数 ...

  6. mysql重置id排列重新排序

    1.删除表中的原有的主键字段 ALTER TABLE table2 DROP id 2.表中重新创建一个字段 ALTER TABLE table2 ADD id int NOT NULL FIRST; ...

  7. Nuxt.js 应用中的 imports:extend 事件钩子详解

    title: Nuxt.js 应用中的 imports:extend 事件钩子详解 date: 2024/10/28 updated: 2024/10/28 author: cmdragon exce ...

  8. UniswapV2Pair细节

    UniswapV2Pair合约是Uniswap V2协议中的核心部分,用于管理流动性池.代币交换.流动性代币的铸造和销毁等操作.以下是对UniswapV2Pair合约中所有主要方法及其参数的详细讲解. ...

  9. ABC240Ex Sequence of Substrings

    ABC240Ex Sequence of Substrings LIS 的好题改编. 约定 \(S(l,r)\) 为字符串 \(s\) 中第 \(l\) 位到底 \(r\)​ 位. \(S(l,r)& ...

  10. Solon(Spring 的替代方案)最近半年下载量突破 1200万!

    不断突破 2023年04月,单月破100万(Maven 中央仓库单月下载量) 2023年06月,单月破200万 2023年11月,单月破250万 2024年11月,最近半年下载量突破 1200万 So ...