`
zachary.guo
  • 浏览: 482551 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Spring MVC 中的 forward 和 redirect

阅读更多
        Spring MVC 中,我们在返回逻辑视图时,框架会通过 viewResolver 来解析得到具体的 View,然后向浏览器渲染。假设逻辑视图名为 hello,通过配置,我们配置某个 ViewResolver 如下:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<description>
		假如逻辑试图名为 "hello",因此 viewResolver 将解析成 /WEB-INF/jsp/hello.jsp
	</description>
	<property name="order" value="10" />
	<property name="prefix" value="/WEB-INF/jsp/" />
	<property name="suffix" value=".jsp" />
</bean>

        实际上,框架还是通过 forward 的方式转发到了 /WEB-INF/jsp/hello.jsp。如果逻辑视图名是 /hello,实际还是转发到了 /WEB-INF/jsp/hello.jsp,即 /WEB-INF/jsp//hello.jsp 等同于 /WEB-INF/jsp/hello.jsp。

        现在有个问题,如果 /hello 就是某个 controller 的映射,我想转发到这个 controller,怎么办?我们可以通过 forward 前缀来达到转发到其它资源的目的:
public String handle() {
    // return "forward:/hello" => 转发到能够匹配 /hello 的 controller 上
    // return "hello" => 实际上还是转发,只不过是框架会找到该逻辑视图名对应的 View 并渲染
    // return "/hello" => 同 return "hello"
    return "forward:/hello";
}


        同理,如果我们想重定向到某个资源,我们可以通过 redirect 前缀来达到重定向到其它资源的目的:
public String handle() {
    // 重定向到 /hello 资源
    return "redirect:/hello";
}

        还记得 java web 中的转发和重定向 这篇文章吗?我强调过,如果想做转发操作,不需要写 contextPath;如果想做重定向操作,推荐写包括 contextPath 在内的 url。因此,在使用 Spring MVC 的 redirect 前缀时,里面是有坑的!

        仍然假设应用程序的 contextPath 为 /ctx。我们来看看 RedirectView.renderMergedOutputModel 的片段:
protected void renderMergedOutputModel(
    Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
    throws IOException {

  // Prepare target URL.
  StringBuilder targetUrl = new StringBuilder();
  if (this.contextRelative && getUrl().startsWith("/")) {
    // Do not apply context path to relative URLs.
    targetUrl.append(request.getContextPath());
  }
  targetUrl.append(getUrl());

  // ...

  sendRedirect(request, response, targetUrl.toString(), this.http10Compatible);
}

protected void sendRedirect(
    HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible)
    throws IOException {

  if (http10Compatible) {
    // Always send status code 302.
    response.sendRedirect(response.encodeRedirectURL(targetUrl));
  }
  else {
    HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);
    response.setStatus(statusCode.value());
    response.setHeader("Location", response.encodeRedirectURL(targetUrl));
  }
}

        sendRedirect 方法没什么特别的,它就是调用 HttpServletResponse 的 sendRedirect 方法而已。因此,关键点就是 renderMergedOutputModel 方法对转发的资源的 url 进行处理了。最终的 url 与 contextRelative 和你要重定向的资源是否以 / 开头有关!当且仅当 renderMergedOutputModel 为 true,并且你要重定向的资源是以 / 开头,spring 会在该资源前添加 contextPath。

        response.sendRedirect() 的参数,如果不以 / 开头,那么容器最终计算出来的资源是相对于做重定向操作的资源的 url;如果以 / 开头,容器将它视为相对于主机的 url。如此说来,spring 的 RedirectView 怎么着都只能将资源重定向到当前应用程序上。将 url 开头的 / 去掉不是解决之道,因此本机的其它应用程序的 contextPath 必定是以 / 开头,因此我们要想办法设置 contextRelative 了。

        RedirectView 自身持有 contextRelative 属性,用于在程序中通过 new 操作符来构造一个 RedirectView 并可以设置 contextRelative。当处理请求的方法返回类型为 String 时,是通过 viewResolver 来解析得到 View 的。UrlBasedViewResolver 就是能够解析出 RedirectView 的 viewResolver。该 viewResolver 持有 redirectContextRelative 属性,当它发现逻辑视图名以 "redirect:" 开头时,会将自身持有的 redirectContextRelative 传入 RedirectView 的构造函数以创建 RedirectView。因此我们通过注册 UrlBasedViewResolver 时设置 redirectContextRelative 以达到控制 RedirectView 修改 url 的行为。UrlBasedViewResolver 解析出 View:
protected View createView(String viewName, Locale locale) throws Exception {
  // If this resolver is not supposed to handle the given view,
  // return null to pass on to the next resolver in the chain.
  if (!canHandle(viewName, locale)) {
    return null;
  }
  // Check for special "redirect:" prefix.
  if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
    String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
    return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
  }
  // Check for special "forward:" prefix.
  if (viewName.startsWith(FORWARD_URL_PREFIX)) {
    String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
    return new InternalResourceView(forwardUrl);
  }
  // Else fall back to superclass implementation: calling loadView.
  return super.createView(viewName, locale);
}

        UrlBasedViewResolver 的 redirectContextRelative 的默认值为 true,这意味着,只要重定向的资源以 / 开头,那么 spring 会帮你添加 contextPath。站在 Spring MVC 的角度上来说,/ 开头的资源就是相对于当前应用程序,这和 forward 一样了。因此,如果你确定重定向操作是在同一应用程序中操作,那就使用 Spring MVC 的默认值吧,这样就不需要你写 contextPath 了。注意,这样做有隐患!当重定向的资源是其它应用程序时,除非你了解机制,否则请不要这么做!
分享到:
评论
1 楼 xuxiaoyinliu 2015-11-30  
     

相关推荐

    Spring-Reference_zh_CN(Spring中文参考手册)

    2.5.1. Spring MVC的表单标签库 2.5.2. Spring MVC合理的默认值 2.5.3. Portlet 框架 2.6. 其他特性 2.6.1. 动态语言支持 2.6.2. JMX 2.6 .3. 任务规划 2.6.4. 对Java 5(Tiger)的支持 2.7. 移植到Spring 2.0 ...

    史上最全java面试,103项重点知识,带目录

    80. forward 和 redirect 的区别? 39 81. 简述 tcp 和 udp的区别? 40 82. tcp 为什么要三次握手,两次不行吗?为什么? 40 84. OSI 的七层模型都有哪些? 42 85. get 和 post 请求有哪些区别? 42 86. 如何实现...

    Java常见面试题208道.docx

    80.forward 和 redirect 的区别? 81.简述 tcp 和 udp的区别? 82.tcp 为什么要三次握手,两次不行吗?为什么? 83.说一下 tcp 粘包是怎么产生的? 84.OSI 的七层模型都有哪些? 85.get 和 post 请求有哪些区别? 86...

    spring-framework-reference-4.1.2

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    2021年最新java面试题--视频讲解(内部培训84个知识点超详细).rar

    Java面试题19.Servlet中forward和redirect的区别 Java面试题20.jsp和Servlet的相同点和不同点 Java面试题21.内置对象和四大作用域和页面传值 Java面试题22.Session和Cookie的区别和使用场景 Java面试题23.mvc模式和...

    涵盖了90%以上的面试题

    forward与redirect区别 页面请求的工作流程 HTTP中的GET和POST方法有什么区别 什么是Servlet servlet是线程安全的吗 Servlet的生命周期 JSP和Servlet的区别和联系 什么是MVC模型 TCP的连接和释放过程 什么是长连接...

    java面试题

    forward和redirect的区别? 答:forward是转发,浏览器跳转后不显示新的地址。 redirect是重定向,浏览器跳转后显示新的地址。 对比之下forward更加高效,并且它有助于隐藏实际地址,但是有些情况则必须使用...

    spring-framework-reference4.1.4

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    java面试宝典

    187、JAVA SERVLET API中forward() 与redirect()的区别? 44 189、Can a Java Thread be started from Servlet class, and what will be the implications? 45 190、What is ...

    千方百计笔试题大全

    187、JAVA SERVLET API中forward() 与redirect()的区别? 44 189、Can a Java Thread be started from Servlet class, and what will be the implications? 45 190、What is ...

    最新Java面试题视频网盘,Java面试题84集、java面试专属及面试必问课程

    │ Java面试题19.forward和redirect的区别.mp4 │ Java面试题20.jsp和Servlet的相同点和不同点?.mp4 │ Java面试题21.内置对象和四大作用域和页面传值.mp4 │ Java面试题22.Session和Cookie的区别.mp4 │ Java面试...

    Java面试宝典2010版

    6、SERVLET API中forward() 与redirect()的区别? 86 7、什么情况下调用doGet()和doPost()? 8、Request对象的主要方法: 87 9、forward 和redirect的区别 10、request.getAttribute() 和 request.getParameter() ...

    最新Java面试宝典pdf版

    6、SERVLET API中forward() 与redirect()的区别? 86 7、什么情况下调用doGet()和doPost()? 86 8、Request对象的主要方法: 87 9、forward 和redirect的区别 87 10、request.getAttribute() 和 request....

    Java面试笔试资料大全

    6、SERVLET API中forward() 与redirect()的区别? 86 7、什么情况下调用doGet()和doPost()? 86 8、Request对象的主要方法: 87 9、forward 和redirect的区别 87 10、request.getAttribute() 和 request....

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    【WEB】转发(forward)和重定向(redirect)的区别 38 forward(转发): 38 redirect(重定向): 39 区别: 39 【WEB】实现会话跟踪的技术有哪些? 40 【WEB】什么是ORM 42 【反射】反射中,Class.forName和...

    JAVA面试宝典2010

    6、SERVLET API中forward() 与redirect()的区别? 86 7、什么情况下调用doGet()和doPost()? 86 8、Request对象的主要方法: 87 9、forward 和redirect的区别 87 10、request.getAttribute() 和 request....

    Java面试宝典-经典

    6、SERVLET API中forward() 与redirect()的区别? 86 7、什么情况下调用doGet()和doPost()? 86 8、Request对象的主要方法: 87 9、forward 和redirect的区别 87 10、request.getAttribute() 和 request....

    java面试题大全(2012版)

    6、SERVLET API中forward() 与redirect()的区别? 86 7、什么情况下调用doGet()和doPost()? 86 8、Request对象的主要方法: 87 9、forward 和redirect的区别 87 10、request.getAttribute() 和 request....

Global site tag (gtag.js) - Google Analytics