Spring MVC 中,我们在返回逻辑视图时,框架会通过 viewResolver 来解析得到具体的 View,然后向浏览器渲染。假设逻辑视图名为 hello,通过配置,我们配置某个 ViewResolver 如下:

Xml代码:

<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 前缀来达到转发到其它资源的目的:

Java代码:

public String handle() {
// return "forward:/hello" => 转发到能够匹配 /hello 的 controller 上
// return "hello" => 实际上还是转发,只不过是框架会找到该逻辑视图名对应的 View 并渲染
// return "/hello" => 同 return "hello"
return "forward:/hello";
}

  

同理,如果我们想重定向到某个资源,我们可以通过 redirect 前缀来达到重定向到其它资源的目的:

java代码:

public String handle() {
// 重定向到 /hello 资源
return "redirect:/hello";
}

  

如果想做转发操作,不需要写 contextPath;如果想做重定向操作,推荐写包括 contextPath 在内的 url。因此,在使用 Spring MVC 的 redirect 前缀时,里面是有坑的!

仍然假设应用程序的 contextPath 为 /ctx。我们来看看 RedirectView.renderMergedOutputModel 的片段:

java代码:

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:

java代码:

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 了。注意,这样做有隐患!当重定向的资源是其它应用程序时,除非你了解机制,否则请不要这么做!

Spring MVC 中的 forward 和 redirect的更多相关文章

  1. SpringMvc(4-1)Spring MVC 中的 forward 和 redirect

    Spring MVC 中,我们在返回逻辑视图时,框架会通过 viewResolver 来解析得到具体的 View,然后向浏览器渲染.通过配置,我们配置某个 ViewResolver 如下: <b ...

  2. SpringMvc(4-1)Spring MVC 中的 forward 和 redirect(转)

    Spring MVC 中,我们在返回逻辑视图时,框架会通过 viewResolver 来解析得到具体的 View,然后向浏览器渲染.通过配置,我们配置某个 ViewResolver 如下: <b ...

  3. Spring MVC 中的 forward redirect Flash属性

    forward:转发 redirect:重定向 -- 转发比重定向快,因为重定向经过客户端,而转发并没有. -- 重定向能够重定向到一个外部网站,但转发不行. -- 重定向能够避免在用户重新加载页面时 ...

  4. Spring MVC中forward请求转发2种方式(带参数)

    Spring MVC中forward请求转发2种方式(带参数) http://www.51gjie.com/javaweb/956.html  

  5. Spring MVC 中的基于注解的 Controller【转】

    原文地址:http://my.oschina.net/abian/blog/128028 终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 H ...

  6. Spring MVC中基于注解的 Controller

         终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法以响 ...

  7. Spring MVC中数据绑定(转)

    Spring MVC中数据绑定 比如Product有一个createTime属性,是java.util.Date类型.那么最简单的转型处理是,在SimpleFormController中覆盖initB ...

  8. Spring MVC 中采用注解方式 Action中跳转到另一个Action的写法

    Spring MVC 中采用注解方式 Action中跳转到另一个Action的写法 在Action中方法的返回值都是字符串行,一般情况是返回某个JSP,如: return "xx" ...

  9. Spring MVC 中的基于注解的 Controller(转载)

           终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法 ...

随机推荐

  1. Android MediaPlayer播放一般音频与SoundPool播放短促的音效

    [1]使用MediaPlayer实现一般的音频播放 MediaPlayer播放通常的音频文件 MediaPlayer mediaPlayer = new MediaPlayer(); if (medi ...

  2. YIi 使用 beginContent() 和 endContent() 设定 Yii 的 layouts

    Yii 的 views/layouts 是用来放置 layouts 的目录,在默认的情况下会有 main.php 和 column1.php 和 column2.php. main.php 内容定义了 ...

  3. 关于MIUI6下使用Widget调用Toast的一个问题

    编写了一个Widget程序,在继承AppWidgetProvider类中调用Toast,发现如下问题: 在小米2,MIUI Version:MIUI5.6.4|Beta, Android Versio ...

  4. BZOJ1831: [AHOI2008]逆序对

    1831: [AHOI2008]逆序对 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 341  Solved: 226[Submit][Status] ...

  5. Maven学习一:用Maven创建Java Project

    转自:http://blog.csdn.net/lfsfxy9/article/details/9399093  Maven环境配置只是入门的基础,现在要通过Maven基本命令生成一个Java Pro ...

  6. 约瑟夫环的java实现

    转自:http://www.cnblogs.com/timeng/p/3335162.html 约瑟夫环:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到 ...

  7. HDU 2546 饭卡

    http://acm.hdu.edu.cn/showproblem.php?pid=2546 呆呆. 饭卡 Time Limit: 5000/1000 MS (Java/Others)    Memo ...

  8. [原]RobotFrameWork(四)变量运算与Evaluate

    一.特殊变量运算: 执行结果: 二.Evaluate使用 函数释义:Evaluate是执行python表达式,并返回执行结果 示例1: 执行结果: 示例2: 执行结果: 作者:liuheng12345 ...

  9. Interleaving String——Leetcode

    Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example,Given:s1 = ...

  10. 18. 4Sum

    #include <string>#include <stack>#include <vector>#include <map>#include < ...