`

Spring MVC 中的 forward 和 redirect

 
阅读更多

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

Xml代码 复制代码 收藏代码
  1. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  2.     <description>  
  3.         假如逻辑试图名为 "hello",因此 viewResolver 将解析成 /WEB-INF/jsp/hello.jsp   
  4.     </description>  
  5.     <property name="order" value="10" />  
  6.     <property name="prefix" value="/WEB-INF/jsp/" />  
  7.     <property name="suffix" value=".jsp" />  
  8. </bean>  
<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代码 复制代码 收藏代码
  1. public String handle() {   
  2.     // return "forward:/hello" => 转发到能够匹配 /hello 的 controller 上   
  3.     // return "hello" => 实际上还是转发,只不过是框架会找到该逻辑视图名对应的 View 并渲染   
  4.     // return "/hello" => 同 return "hello"   
  5.     return "forward:/hello";   
  6. }  
public String handle() {
    // return "forward:/hello" => 转发到能够匹配 /hello 的 controller 上
    // return "hello" => 实际上还是转发,只不过是框架会找到该逻辑视图名对应的 View 并渲染
    // return "/hello" => 同 return "hello"
    return "forward:/hello";
}



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

Java代码 复制代码 收藏代码
  1. public String handle() {   
  2.     // 重定向到 /hello 资源   
  3.     return "redirect:/hello";   
  4. }  
public String handle() {
    // 重定向到 /hello 资源
    return "redirect:/hello";
}


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

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

Java代码 复制代码 收藏代码
  1. protected void renderMergedOutputModel(   
  2.     Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)   
  3.     throws IOException {   
  4.   
  5.   // Prepare target URL.   
  6.   StringBuilder targetUrl = new StringBuilder();   
  7.   if (this.contextRelative && getUrl().startsWith("/")) {   
  8.     // Do not apply context path to relative URLs.   
  9.     targetUrl.append(request.getContextPath());   
  10.   }   
  11.   targetUrl.append(getUrl());   
  12.   
  13.   // ...   
  14.   
  15.   sendRedirect(request, response, targetUrl.toString(), this.http10Compatible);   
  16. }   
  17.   
  18. protected void sendRedirect(   
  19.     HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible)   
  20.     throws IOException {   
  21.   
  22.   if (http10Compatible) {   
  23.     // Always send status code 302.   
  24.     response.sendRedirect(response.encodeRedirectURL(targetUrl));   
  25.   }   
  26.   else {   
  27.     HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);   
  28.     response.setStatus(statusCode.value());   
  29.     response.setHeader("Location", response.encodeRedirectURL(targetUrl));   
  30.   }   
  31. }  
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代码 复制代码 收藏代码
  1. protected View createView(String viewName, Locale locale) throws Exception {   
  2.   // If this resolver is not supposed to handle the given view,   
  3.   // return null to pass on to the next resolver in the chain.   
  4.   if (!canHandle(viewName, locale)) {   
  5.     return null;   
  6.   }   
  7.   // Check for special "redirect:" prefix.   
  8.   if (viewName.startsWith(REDIRECT_URL_PREFIX)) {   
  9.     String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());   
  10.     return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());   
  11.   }   
  12.   // Check for special "forward:" prefix.   
  13.   if (viewName.startsWith(FORWARD_URL_PREFIX)) {   
  14.     String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());   
  15.     return new InternalResourceView(forwardUrl);   
  16.   }   
  17.   // Else fall back to superclass implementation: calling loadView.   
  18.   return super.createView(viewName, locale);   
  19. }  
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 了。注意,这样做有隐患!当重定向的资源是其它应用程序时,除非你了解机制,否则请不要这么做!

分享到:
评论

相关推荐

    SSM框架面试题.pdf

    35. **Spring MVC中Forward和Redirect的区别** - `forward`是服务器内部转发,不改变浏览器地址栏;`redirect`则是服务器重新定向,浏览器地址栏会发生变化。 36. **在Spring MVC中如何实现数据绑定** - Spring ...

    spring mvc 自學筆記

    模型用来封装业务数据和逻辑,Spring MVC 中可以通过将对象添加到 `Model` 或 `ModelAndView` 中来传递数据到视图层。 #### 控制器(Controller) 控制器负责接收用户请求,并决定调用哪个模型来处理请求,最后选择...

    第三课:spring mvc 原理深度解析1

    在传统的 Servlet 和 JSP 结合的开发模式中,请求首先到达 Servlet,Servlet 处理业务逻辑,然后设置 Model,接着通过 Forward 或 Redirect 将控制权交给 JSP,JSP 负责渲染视图并返回给客户端。Spring MVC 在此基础...

    spring MVC学习笔记

    3. **Redirect 和 Forward**:可以使用 `ModelAndView` 中的 `setViewName()` 分别实现重定向和转发。 **六、异常处理** 1. **@ExceptionHandler**:控制器方法可以处理特定的异常。 2. **Global Exception ...

    spring mvc 文档

    Spring MVC提供了丰富的控制器类和自定义标签,使得Web开发更加高效和方便。 ##### 4.1 SimpleFormController SimpleFormController是Spring MVC早期版本中用于处理表单的控制器。尽管现在已经被废弃,但了解其...

    Spring3_MVC_基础实践之路

    在Spring MVC 3的基础实践中,配置是至关重要的第一步。要启用Spring MVC,首先需要在`web.xml`中进行相应的配置。以下是一些关键步骤: 1. **Servlet Dispatcher配置**: - 首先,定义一个`DispatcherServlet`,...

    Spring MVC 配置 urlrewrite 实例

    `UrlRewriteFilter`是Tuckey组织提供的一款强大的URL重写工具,它可以方便地集成到Spring MVC中,实现URL的美化和管理。下面我们将详细介绍如何配置和使用`UrlRewriteFilter`。 1. **引入依赖** 首先,我们需要在...

    详解spring mvc 请求转发和重定向

    在Spring MVC中,可以通过注解`@RequestMapping`结合`return "forward:/path"`或`return "redirect:/path"`来实现转发或重定向。如示例代码所示: ```java @RequestMapping(value="/users") @Controller public ...

    编写Spring MVC控制器的14个技巧(小结)

    redirect和forward是Spring MVC中两种常用的请求转发机制。redirect可以将请求重定向到另一个URL,而forward可以将请求转发到另一个控制器方法。这种机制可以帮助控制器类处理复杂的业务逻辑。 7. 使用Session和 ...

    Spring mvc结果跳转方法详解

    Spring MVC 结果跳转方法是指在 Spring MVC 框架中,如何将请求结果跳转到指定的页面或执行特定的操作。本文将详细介绍 Spring MVC 结果跳转方法的实现方式。 一、使用 ModelAndView 对象 在 Spring MVC 中,可以...

    SpringMVC面试专题及答案整理1

    在实际开发中,Spring MVC的这些组件和特性使得开发者能够构建高效、可扩展的Web应用。通过合理配置和定制,可以满足各种复杂的业务场景需求。同时,Spring MVC与其他Spring模块的集成,如Spring Security、Spring ...

    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 ...

    基于Servlet实现的MVC项目demo

    - Servlet通过forward()或redirect()方法,决定跳转到哪个JSP页面。 - JSP页面通过EL(Expression Language)和JSTL(JavaServer Pages Standard Tag Library)从request或session中获取数据,并显示在页面上。 6...

    spring-post-forward-static:Spring MVCboot 在 POST 上转发到静态资源

    在Spring MVC和Spring Boot框架中,处理HTTP请求是核心功能之一。当涉及到“Spring MVCboot在POST上转发到静态资源”这个主题时,我们主要关注的是如何在处理POST请求后,将用户重定向或者转发到静态资源页面,比如...

    data(struts2,spring)

    Struts2 和 Spring 是两个非常重要的 Java Web 开发框架,它们在企业级应用程序中广泛应用。Struts2 主要是用于处理 MVC(Model-View-Controller)架构中的控制器部分,而 Spring 则是一个全面的后端解决方案,包括...

    springmvc相关面试问题.docx

    Spring MVC的主要作用是解耦应用程序的业务逻辑和用户界面,使得开发和维护更加高效。以下将详细解释Spring MVC的工作流程、重定向与转发、与AJAX的交互以及常用的注解。 一、Spring MVC工作流程 1. 用户发起请求...

    JAVA框架面试题汇总.docx

    在Java框架面试中,Spring MVC是一个经常被讨论的话题。...理解并掌握这些核心概念和实践,对于在面试中展现出对Spring MVC的深入理解至关重要。在实际开发中,灵活运用这些知识可以提高代码的可维护性和效率。

    SpringMVC面试题.pdf

    Spring MVC 是一个强大的Java ...以上是对Spring MVC面试中常见的一些知识点的详细解答,这些内容涵盖了Spring MVC的基本概念、工作流程、核心组件、与其他框架的区别以及与AJAX的交互等方面,有助于理解和准备面试。

    Struts1说明以及整合Spring

    - **Spring MVC支持**:为了支持Struts1与Spring的整合,还需要引入`spring-webmvc`和`spring-webmvc-struts`库。 2. **配置Struts1中的Spring插件**: - 在`struts-config.xml`文件中加入Spring的插件配置,以便...

    JAVA框架面试题汇总编程资料

    Spring MVC支持多种视图跳转的方式,可以通过返回不同的字符串来控制跳转逻辑,如“redirect:/somePage”表示重定向到某个页面,“forward:/somePage”表示转发到某个页面。此外,还可以通过在Controller中返回特定...

Global site tag (gtag.js) - Google Analytics