SpringMVC拦截器HandlerInterceptor拦截后返回数据或视图View

11 篇文章 0 订阅
7 篇文章 0 订阅

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可以从容器中获取到。这里我们主要用到的只有两个RequestResponseBodyMethodProcessorViewNameMethodReturnValueHandler

返回纯数据

返回纯数据,适用于返回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,因此可以用下面的方法实现。

String viewName = "yourViewName";
UrlBasedViewResolver viewResolver = (UrlBasedViewResolver) SpringContextHolder.getBean("velocityViewResolver");
View view = viewResolver.resolveViewName(viewName, Locale.CHINA);
if (view == null) {
    throw new RuntimeException("cannot find " + viewName + " view.");
}
view.render(null, request, response);

完整代码实现

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下注册拦截器:org.springframework.web.servlet.config.annotation.WebMvcConfigurer#addInterceptors

结尾

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

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
感谢您的提问。我会依次回答您的每一个问题: 1. Spring和SpringMVC的区别: Spring是一个框架,它提供了依赖注入和面向切面编程等功能,可以用来简化Java应用程序的开发。 SpringMVC是Spring框架的一个模块,它提供了一个基于MVC的Web应用程序开发框架,使开发人员可以更容易地开发Web应用程序。 2. 我使用过的一些MVC提供的拦截器包括: - HandlerInterceptor:在控制器方法执行前后拦截请求。 - LocaleChangeInterceptor:用于拦截请求中的区域设置参数,并将其应用于当前线程的Locale。 - ThemeChangeInterceptor:用于拦截请求中的主题参数,并将其应用于当前线程的主题。 3. SpringMVC的调用过程: - 浏览器发送一个请求到DispatcherServlet。 - DispatcherServlet根据请求的URL找到对应的HandlerMapping,得到对应的Controller。 - HandlerAdapter调用Controller处理请求,并将处理结果返回给DispatcherServlet。 - DispatcherServlet选择一个ViewResolver来解析Controller返回的逻辑视图名。 - ViewResolver解析逻辑视图名,得到View对象。 - DispatcherServlet将模型数据传递给ViewView使用模型数据渲染视图。 - 返回响应给浏览器。 4. IoC是Java基础的哪个点: IoC(Inversion of Control,控制反转)是一种设计模式,它是Java基础中面向对象编程的一部分。 5. 我对SpringMVC源码进行过一些研究,但并不是专业的开发者,所以我的理解可能不够深入。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值