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>
<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";
- }
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";
- }
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));
- }
- }
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);
- }
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 了。注意,这样做有隐患!当重定向的资源是其它应用程序时,除非你了解机制,否则请不要这么做!
相关推荐
35. **Spring MVC中Forward和Redirect的区别** - `forward`是服务器内部转发,不改变浏览器地址栏;`redirect`则是服务器重新定向,浏览器地址栏会发生变化。 36. **在Spring MVC中如何实现数据绑定** - Spring ...
模型用来封装业务数据和逻辑,Spring MVC 中可以通过将对象添加到 `Model` 或 `ModelAndView` 中来传递数据到视图层。 #### 控制器(Controller) 控制器负责接收用户请求,并决定调用哪个模型来处理请求,最后选择...
在传统的 Servlet 和 JSP 结合的开发模式中,请求首先到达 Servlet,Servlet 处理业务逻辑,然后设置 Model,接着通过 Forward 或 Redirect 将控制权交给 JSP,JSP 负责渲染视图并返回给客户端。Spring MVC 在此基础...
3. **Redirect 和 Forward**:可以使用 `ModelAndView` 中的 `setViewName()` 分别实现重定向和转发。 **六、异常处理** 1. **@ExceptionHandler**:控制器方法可以处理特定的异常。 2. **Global Exception ...
Spring MVC提供了丰富的控制器类和自定义标签,使得Web开发更加高效和方便。 ##### 4.1 SimpleFormController SimpleFormController是Spring MVC早期版本中用于处理表单的控制器。尽管现在已经被废弃,但了解其...
在Spring MVC 3的基础实践中,配置是至关重要的第一步。要启用Spring MVC,首先需要在`web.xml`中进行相应的配置。以下是一些关键步骤: 1. **Servlet Dispatcher配置**: - 首先,定义一个`DispatcherServlet`,...
`UrlRewriteFilter`是Tuckey组织提供的一款强大的URL重写工具,它可以方便地集成到Spring MVC中,实现URL的美化和管理。下面我们将详细介绍如何配置和使用`UrlRewriteFilter`。 1. **引入依赖** 首先,我们需要在...
在Spring MVC中,可以通过注解`@RequestMapping`结合`return "forward:/path"`或`return "redirect:/path"`来实现转发或重定向。如示例代码所示: ```java @RequestMapping(value="/users") @Controller public ...
redirect和forward是Spring MVC中两种常用的请求转发机制。redirect可以将请求重定向到另一个URL,而forward可以将请求转发到另一个控制器方法。这种机制可以帮助控制器类处理复杂的业务逻辑。 7. 使用Session和 ...
Spring MVC 结果跳转方法是指在 Spring MVC 框架中,如何将请求结果跳转到指定的页面或执行特定的操作。本文将详细介绍 Spring MVC 结果跳转方法的实现方式。 一、使用 ModelAndView 对象 在 Spring MVC 中,可以...
在实际开发中,Spring MVC的这些组件和特性使得开发者能够构建高效、可扩展的Web应用。通过合理配置和定制,可以满足各种复杂的业务场景需求。同时,Spring MVC与其他Spring模块的集成,如Spring Security、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通过forward()或redirect()方法,决定跳转到哪个JSP页面。 - JSP页面通过EL(Expression Language)和JSTL(JavaServer Pages Standard Tag Library)从request或session中获取数据,并显示在页面上。 6...
在Spring MVC和Spring Boot框架中,处理HTTP请求是核心功能之一。当涉及到“Spring MVCboot在POST上转发到静态资源”这个主题时,我们主要关注的是如何在处理POST请求后,将用户重定向或者转发到静态资源页面,比如...
Struts2 和 Spring 是两个非常重要的 Java Web 开发框架,它们在企业级应用程序中广泛应用。Struts2 主要是用于处理 MVC(Model-View-Controller)架构中的控制器部分,而 Spring 则是一个全面的后端解决方案,包括...
Spring MVC的主要作用是解耦应用程序的业务逻辑和用户界面,使得开发和维护更加高效。以下将详细解释Spring MVC的工作流程、重定向与转发、与AJAX的交互以及常用的注解。 一、Spring MVC工作流程 1. 用户发起请求...
在Java框架面试中,Spring MVC是一个经常被讨论的话题。...理解并掌握这些核心概念和实践,对于在面试中展现出对Spring MVC的深入理解至关重要。在实际开发中,灵活运用这些知识可以提高代码的可维护性和效率。
Spring MVC 是一个强大的Java ...以上是对Spring MVC面试中常见的一些知识点的详细解答,这些内容涵盖了Spring MVC的基本概念、工作流程、核心组件、与其他框架的区别以及与AJAX的交互等方面,有助于理解和准备面试。
- **Spring MVC支持**:为了支持Struts1与Spring的整合,还需要引入`spring-webmvc`和`spring-webmvc-struts`库。 2. **配置Struts1中的Spring插件**: - 在`struts-config.xml`文件中加入Spring的插件配置,以便...
Spring MVC支持多种视图跳转的方式,可以通过返回不同的字符串来控制跳转逻辑,如“redirect:/somePage”表示重定向到某个页面,“forward:/somePage”表示转发到某个页面。此外,还可以通过在Controller中返回特定...