Java Web 请求一个Controller 转发到另一个Controller 的四种解决方式总结(HandlerInterceptorAdapter, Filter, ModelAndView)

总思路

项目在一些特殊的情况下可能会将一个请求转发给另一个请求去处理,有可能是业务系统直接互相调用。也有可能第一个请求只做了转发不做实际的处理,第二个请求才去做实际的处理。也有可能两个i请求的处理方式都是由第二种给出的等等。

这里我的场景是做请求转发,实际处理依旧是第二个请求去处理。
这里通过资料查询以及实践给出三种请求转发的方式和案例:

一、HandlerInterceptorAdapter 方式(参考资料)

为了准确定位一些Controller的方法,我自定义了注解去判断是否需要执行转发逻辑,或者你也可以直接通过请求路径判断似乎做转发逻辑,示例如下:
我的注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NeedHandle {
}

我的 HandlerInterceptorAdapter 这里只用到了 preHandle 的处理 还有其他方法可以重写

preHandle 为进入实际请求controller 之前的自定义动作

import com.dtdream.dthink.dtalent.dmall.annotation.NeedHandle;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class OpenCheckInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        HandlerMethod handlerMethod = (HandlerMethod)handler;
        // 是否含有我指定的注解
        if (handlerMethod.hasMethodAnnotation(NeedHandle.class)){
            // 获取请求路径
            String requestURI = request.getRequestURI();
            // 含有需要更改的请求路径则需要更正路径  这里我将路径中的 /open  去除了
            String replacePath = requestURI.replace("/open", "");
            // 转发
            request.getRequestDispatcher(replacePath).forward(request, response);
        }
        // 最终放行请求
        return true;
    }

}

当然 如果为一般的 springMvc 你的这个类需要加入到对应xml 中或者直接使用 @Component 注解 将拦截器注入完成
使用注解时 如下:

@Component
public class OpenCheckInterceptor extends HandlerInterceptorAdapter {
   ..........
}

xml 方式如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

....................
.................... 其他
....................


    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.aurora.interceptor.HttpInterceptor">bean>
        mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.aurora.interceptor.OpenCheckInterceptor">bean>
        mvc:interceptor>
    mvc:interceptors>

该方法实际在我的项目中前半段是生效的,但是转发时出现了问题,可能是由于项目框架较老而且内部对请求做了很多其他的特殊处理,实际我修改了部分代码如下 如果你也出现了问题可以参考我的其他思路

这里我替换了
String requestURI = request.getRequestURI(); 为 String requestURI = request.getRequestURL().toString();

实际测验后才进入了我想要进入的controller 中

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        HandlerMethod handlerMethod = (HandlerMethod)handler;
        // 是否含有我指定的注解
        if (handlerMethod.hasMethodAnnotation(NeedHandle.class)){
            // 获取请求路径
            String requestURI = request.getRequestURL().toString();  // 替换部分
            // 含有需要更改的请求路径则需要更正路径 这里我将路径中的 /open  去除了
            String replacePath = requestURI.replace("/open", "");
            // 转发
            request.getRequestDispatcher(replacePath).forward(request, response);
        }
        // 最终放行请求
        return true;
    }

二、Filter

Filte 过滤器的修改方式与上方第一个方法差不多 这里还有其他可重写方法,这里只用到了 doFilter
示例如下:
使用注解的方式:

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(filterName = "myFilter", urlPatterns = {"/*"})
@Component
public class OpenCheckFilter implements Filter {


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 不需要httpServletResponse 时可以不判断 HttpServletResponse 仅仅是为了保护
        if ((servletRequest instanceof  HttpServletRequest) && (servletResponse instanceof HttpServletResponse)){
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            // 不需要httpServletResponse 时可以不加
            HttpServletResponse response = (HttpServletResponse) servletResponse;             String requestURI = request.getRequestURI();
            if (requestURI.contains("/open/test")){
                // 含有需要更改的请求路径则需要更正路径
                String replacePath = requestURI.replace("/open", "");
                // 转发
                request.getRequestDispatcher(replacePath).forward(request, response);
            }
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }
}

如果不使用注解的方式 则需要找 web.xml 中添加对应的 filter 类似如下:
web.xml


<web-app version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
.............
............. 其他
  <filter>
    <filter-name>openFilterfilter-name>
    <filter-class>com.dtdream.dthink.dtalent.dmall.filter.ReceiveManagerFilterfilter-class>
  filter>
  
  <filter-mapping>
    <filter-name>openFilterfilter-name>
    <url-pattern>/*url-pattern>
  filter-mapping>
  
  
  <filter-mapping>
    <filter-name>openFilterfilter-name>
    <url-pattern>/api/v1/test/*url-pattern>
  filter-mapping>

web-app>

但是在这里我遇到了和上面一样的问题,依旧失败没能进入目标 controller,所以采用了同样的修改方式

String requestURI = request.getRequestURI();
替换为
String requestURI = request.getRequestURL().toString();

如果遇到可以参考这样的方式;

三、ModelAndView

这个需要是 springframework(spring 框架) 才能使用
方式类似,但ModelAndView 中 View 指定了访问的地址或者资源路径,跳转路径,Model 即携带的参数
使用案例:
这里我重新定义了一个 controller

@RequestMapping("/open")
@RestController
public class OpenApiPathController {

    // 重定向
    private static final String REDIRECT_ ="redirect:";

    @GetMapping("/test/count")
    public ModelAndView messageCount(HttpServletRequest request, HttpServletResponse response){
        String replace = request.getRequestURL().toString().replace("/open/test/count", "/test/count");
        // 获取请求参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        
        ModelAndView modelAndView = new ModelAndView();
        // 指定重定向的地址
        modelAndView.setViewName(REDIRECT_ + replace);
        // 同时携带请求的所有参数
        modelAndView.addAllObjects(parameterMap);
        return modelAndView;
    }

这里我使用 request.getRequestURL() 才最终成功,但也可以试试 request.getRequestURI() 搜寻的资料中大部分用的都是URI 可以成功,但我只有 URL时才成功。

redirect: + 请求 为固定写法 冒号( : )不可以省略
一般来说 redirect: 后默认携带/ 如果你的有问题可以携带 / 再次尝试一次
转发时可以使用 forward:

四、Controller 注入 Controller

其实就是字面意思,在Controller 层通过 @AutoWired 注解注入其他Controller 使得可以直接调用其他controller 的方法,实现间接使用原有controller 的其他方法,实际操作后可行,但是值得一提的是 springBoot 框架下可能出问题, 需要更换 @service 才能正常引用。所以一般来说建议对 业务的处理都封入 service 使得调用Service和controller 几乎没有任何差别。

总结

拦截请求并转发请求可以使用 HandlerInterceptorAdapter,Filter,ModelAndView 三种方式或者其他
如果 request 获取的 requestURI 不能成功则尝试使用 requestURL 。转发请求 不是 forword(转发)就是redirect(重定向)

你可能感兴趣的:(JavaWeb开发,java,spring)