本章内容:html
稍等还没结束前端
若是你有幸能看到。后面的章节暂时不更新了,改变学习方式了。重要理解思想,这本书写的太好了。记得要看做者的代码,书上只是阐述了知识点。还有之后会把重点放在GitHub上,阅读别人的代码,本身理解的同时在模仿出来,分享给你们。大家的点赞就是对个人支持,谢谢你们了。java
谈一些我的感觉git
在不少方面,Spirng MVC(整个Spirng也是如此),也有还没结束这样的感受。github
在第五章,咱们学习了Sprng MVC的基础知识,以及如何编写控制器来处理各类请求,基于这些知识。咱们在第六章学习了如何建立JSP和Thymeleaf视图,这些视图会将模型数据展现给用户。你可能认为咱们已经掌握了Spring MVC的所有知识,可是,稍等!还没结束。web
在本章中,咱们将会看到如何编写控制器处理文件上传,如何处理控制器所抛出的异常,以及如何在模型中传递数据,使其可以在重定向(redirect)以后依然存活。spring
但,首先我要兑现一个承诺。在第5章中,咱们快速展示了如何经过AbstractAnnotationConfigDispatcherServletInitializer
搭建Spring MVC,当时,咱们承诺会为读者展示其余的配置方案。因此,在介绍文件上传和异常处理以前,咱们花时间探讨一下如何使用其余方式来搭建DispatcherServlet
和ContextLoaderListener
chrome
尽管对不少Spring应用来讲,这是一种安全的假设,可是并不必定能知足咱们的要求。除了DispatcherServlet
之外,咱们还可能须要额外的DispatcherServlet
和Filter
,咱们可能还须要对DispatcherServlet
自己作一些额外的配置:或者,若是咱们须要将应用部署到Servlet3.0以前的容器中,那么还须要将DispatcherServlet
配置到传统的web.xml中。数组
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
AbstractAnnotationConfigDispatcherServletInitializer
所完成的事其实比看上去要多,在SpitterWebInitializer
中咱们所编写的三个方法仅仅是必需要重载的三个抽象方法,但实际上还有更多的方法能够进行重载,从而实现额外的配置。浏览器
此类的方法之一就是customizeRegistration()
.在AbstractAnnotationConfigDispatcherServletInitializer
将DispatcherServlet
注册到Servlet容器中就会调用customizeRegistration()
,并将Servlet注册后获得的Registration.Dynamic
传递进来,经过重载customizeRegistration()
方法,咱们就能够对DispatcherServlet
进行额外的配置。
在本章稍后,咱们将会看到如何在Spirng MVC中处理multiparty请求和文件上传。若是计划使用Servlet3.0对multiparty配置的支持,那么咱们须要使用DispatcherServlet
的registration来启用multilpart请求。咱们能够重载customizeRegistration()
方法来设置MultipartConfigElement,
@Override protected void customizeRegistration(Dynamic registration) { registration.setMultipartConfig( new MultipartConfigElement("C:\\Temp")); //设置上传文件目录 }
借助customizeRegistration()
方法中的ServletRegistration.Dynamic
咱们可以完成更多的任务,
setLoadOnstartup()
设置load-on-startup 优先级,setInitParameter()
设置初始化参数,setMultipartConfig()
配置Servlet3.0对multipart的支持,按照AbstractAnnotationConfigDispatcherServletInitializer
的定义,它会建立DispatcherServlet
和ContextLoaderListener
.可是若是你想要注册其余的Servlet、Filter、Listener的话,那该怎么办?
基于Java的初始化器(initializer)的一个好处在于咱们能够定义任意数量的初始化类。若是咱们想要往Web容器中注册其余组件的话,只须要建立一个 新的初始化类就能够了,最简单的方式就是实现Spring的WebApplicationInitializer
并注册一个Servlet。
public class MyServletInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { Dynamic myServlet = servletContext.addServlet("myServlet",myServlet.class); myServlet.addMapping("/custom/**"); } }
以上程序至关基础的Servlet注册初始化器类,它注册了一个Servlet并将其映射到了一个路径上,咱们也能够经过这种方式来手动注册DispatcherServlet
.(可是不必,由于AbstractAnnotationConfigDispatcherServletInitializer
没用太多代码就将这项任务完成得很漂亮)
相似的,咱们还能够建立新的WebApplicationInitializer
来实现注册Listener和 Filter,
@Override public void onStartup(ServletContext servletContext) throws ServletException { javax.servlet.FilterRegistration.Dynamic filter = servletContext.addFilter("myFilter",myFilter.class); filter.addMappingForUrlPatterns(null,false,"/custom/*"); }
若是你将应用部署到Servlet3.0的容器中,那么WebApplicationInitializer
提供了一种通用的方法,实如今Java中注册Servlet和Filter、Listener,若是你只是注册Filter,而且该Filter只会映射到DispatcherServlet
上的话,那么AbstractAnnotationConfigDispatcherServletInitializer
还有一种快捷的方式。
为了注册Filter并将其映射到DispatcherServlet
,所须要作的仅仅是重载AbstractAnnotationConfigDispatcherServletInitializer
的getServletFilter()方法。
@Override protected Filter[] getServletFilters() { return new Filter[] {new Myfilter()}; }
这个方法返回一个javax.servlet.filter数组。在这里没有必要声明它的映射路径,getServletFilter()方法返回全部Filter都会被映射到DispatcherServlet
上。
若是要将应用部署到Servlet3.0上,那么Spring容器提供了多种注册方式,而没必要建立web.xml文件,可是,若是你不想采起上述方案的话,也是能够的,假设你将应用部署到不支持Servlet3.0的容器中(或者你只但愿使用web.xml),那么咱们彻底能够按照传统的方式,经过web.xml配置Spirng MVC,
在典型的Spirng MVC应用中,咱们会须要DispatcherServlet
和ContextLoaderListener
.AbstractAnnotationConfigDispatcherServletInitializer
会自动注册它们,但若是须要在web.xml中注册的话,那就须要咱们本身动手来完成了。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="taotao" version="2.5"> <display-name>appServlet</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <!-- 加载spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext-*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 解决post乱码 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- springmvc的前端控制器 --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- contextConfigLocation不是必须的, 若是不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
ContextLoaderListener
和DispatcherServlet
各自都会加载一个Spirng应用上下文。上下文ContextLoaderLocation
指定了一个XMl文件的地址,这个文件定义了根据应用上下文,它会被ContextLoaderListener
加载。根上下文会从"/WEB-INF/spring/applicationContext-*.xml"中加载bean的定义
DispatcherServlet
会根据Servlet的名字找到一个文件,并基于该文件加载应用上下文。
若是你但愿指定DispatcherServlet
配置文件的话,那么能够在Servlet指定一个ContextLoaderLocation
初始化参数。
<!-- springmvc的前端控制器 --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- contextConfigLocation不是必须的, 若是不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
如今咱们已经看到了如何以多种不一样的方式来搭建Spring MVC,那么接下来咱们看一下如何使用Spring MVC来处理文件上传。
在Web应用中,容许用户上传内容是很常见的需求,在Facebook和Flickr这样的网站中,容许用户会上传图片和视频,并与家人朋友分享。还有一些服务器中容许用户上传照片,而后按照传统的方式将其打印在纸上,或者咖啡杯上。
Spittr应用有两个地方须要文件上传。当新用户注册的时候,咱们但愿可以上传一张照片,从而与他的我的信息相关联。当用户提交新的Spittle时,除了文本信息外,他们可能还会上传一张照片。
通常表单提交所造成的请求结果是很是简单的,就是以"&"符号分割的多个name-value对,尽管这种方式简单,而且对于典型的基于文本的表单提交也足够知足需求,可是对于二进制数据,就显得力不从心了。与之不一样的是multipart格式的数据会将一个表单拆分为多个部分(part),每一个部分对应一个输入域。在通常的表单输入域中,它所对应的部分中会放置文本数据,可是若是是上传文件的话,它所对应的部分能够是二进制。
Content-Type 他表它的类型。尽管multipart请求看起来很复杂,可是在SpringMVC中处理它却很容易,在编写控制器方法处理文件上传以前,咱们必须配置一个multipart解析器,经过它来告诉DispatcherServlet
该如何读取multipart请求。
DispatcherServlet
并无实现爱你任何解析multipart请求数据的功能。它将该任务委托给了Spring中的MultipartResolver策略接口的实现,经过这个实现类来解析multipart请求中的内容。从Spirng 3.1开始,Spirng内置了两个MultipartResolver的实现供咱们选择。
通常来说StandardServletMultipartResolver
可能会是优选方案,他使用Servlet所提供的功能支持。并不须要依赖任何其余的项目。若是,咱们须要将项目部署到Sevrvlet3.0以前的容器中,或者尚未使用Spring 3.1 或者更高的版本,那么可能就须要 CommonsMultipartResolver
了。
使用Servlet3.0解析multipart
兼容Servlet3.0的StandardServletMultipartResolver
没有构造器参数,也没有要设置的参数,这样,在Spring应用上下文中,将其声明为bean就会很是简单。
@Bean public MultipartResolver multipartResolver() throws IOException { return new StandardServletMultipartResolver(); }
若是咱们采用Servlet初始化类的方式来配置DispatcherServlet
的话,这个初始化类应该已经实现了WebApplicationInitializer
,那么咱们能够在ServletRegistration
上调用setMultipartConfig()
方法,传入一个MultipartConfigElement
实例,具体的配置以下:
@Override protected void customizeRegistration(Dynamic registration) { registration.setMultipartConfig( new MultipartConfigElement("C:\\Temp")); } }
经过重载customizeRegistration()
方法(它会获得一个Dynamic做为参数)类配置multipart的具体细节。
到目前为止,咱们所使用的是只有一个参数的MultipartConfigElemenet构造器,这个参数指定的是文件系统中一个绝对目录,上传文件将会临时写入该目录,可是,咱们还能够经过其余的构造器来限制上传文件的大小,除了临时路径的位置,其余的构造器能够接受的参数以下:
例如,假设咱们想要限制文件的大小不超过2MB,整个请求不超过4MB,并且全部的文件都要写入磁盘,
@Override protected void customizeRegistration(Dynamic registration) { registration.setMultipartConfig( new MultipartConfigElement("C:\\Temp\\uploads",2097152, 4194304, 0)); }
若是使用更为传统的web.xml来配置MultipartConfigResolver的话,那么可使用<servlet>中的<multipart-config>元素
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1<load-on-startup> <multipart-config> <location>C:\\Temp\\uploads<location> <max-file-size>2097152</max-file-size> <max-request-size>4194304 </max-request-size> </multipart-config> </servlet>
配置Jakarta Commons FileUpload Multipart解析器
Spring内置了CommonsMultipartResolver,能够做为StandardServletMultipartResolver
的替代方案。
已经配置好multipart请求的处理器,那么接下来咱们就编写控制器方法来接收上传的文件。要实现这一点,最多见的方法就是在某个控制器方法参数上添加@RequestPart注解。
<form method="POST" th:object="${spitter}" enctype="multipart/form-data"> <label>Profile Picture</label>: <input type="file" name="profilePicture" accept="image/jpeg,image/png,image/gif" /><br/> <input type="submit" value="Register" /> </form>
<form>标签如今将enctype属性设置为multipart/form-data
,这会告诉浏览器以multipart数据的形式提交表单,而不是以表单数据的形式进行提交。还添加了一个新的< input>域其type为file。 accept属性用来将文件类型限制为jpeg,png,gif格式的。根据name属性,图片数据将会发送到multipart请求中的profilePicture之中。
如今咱们须要修改processRegistration()
方法,使其可以接受上传的图片。其中一种方式就是添加btye数组,并为其添加@RequestPart注解。
@RequestMapping(value="/register", method=POST) public String processRegistration( @RequestPart(value = "profilePictures") byte[] profilePicture, @Valid Spitter spitter, Errors errors) {
当表单提交的时候,profilePicture属性将会给定一个byte数组,这个数组中包含了请求中对应的part的数据(经过@RequestPart指定的)。若是没有选择文件,那么这个数据为空(而不是null),获取到图片数据后,processRegistration()
方法剩下的任务就是将文件保存到某个地方。
接受MultipartFile
使用上传文件的原始byte比较简单,可是功能有限。所以,Spring提供了MultipartFile接口,它为处理multipart数据提供了内容更为丰富的对象。
package org.springframework.web.multipart; /** * A representation of an uploaded file received in a multipart request. * @author Juergen Hoeller * @author Trevor D. Cook */ public interface MultipartFile { /** * Return the name of the parameter in the multipart form. * @return the name of the parameter (never {@code null} or empty) */ String getName(); /** * Return the original filename in the client's filesystem. */ String getOriginalFilename(); /** * Return the content type of the file. */ String getContentType(); /** * Return whether the uploaded file is empty, that is, either no file has * been chosen in the multipart form or the chosen file has no content. */ boolean isEmpty(); /** * Return the size of the file in bytes. */ long getSize(); /** * Return the contents of the file as an array of bytes. */ byte[] getBytes() throws IOException; /** * Return an InputStream to read the contents of the file from. */ InputStream getInputStream() throws IOException; /** * Transfer the received file to the given destination file. */ void transferTo(File dest) throws IOException, IllegalStateException; }
能够看到,MultipartFile提供了获取文件上传文件byte的方式,还能获取原始的文件名,大小以及内容类型、还提供了一个InputStream用来将文件数据以流的方式进行读取。
除此以外,还提供了一个transferTo()方法,它可以帮助咱们将上传文件写入到文件系统中。做为样例,咱们在能够在processRegistration()
方法中添加以下的几行代码,从而将上传的图片文件写入到文件系统中
profilePicture.transferTo( new File("date/spittr" + profilePicture.getOriginalFilename()));
将文件保存到本地文件系统中是很是简单的,可是这须要咱们对这些文件进行管理。咱们须要确保有足够的空间,确保当出现硬件故障时,文件进行了备份,还须要在集群的多个服务器之间处理这些图片文件的同步。
以Part的形式接受上传的文件
Spring MVC接受javax.servlet.http.Part做为控制器方法的参数,若是使用part来替换MultiFile的话,那么processRegistration()方法签名会变成以下的形式。
@RequestMapping(value="/register", method=POST) public String processRegistration( @RequestPart(value="profilePictures", required=false) Part fileBytes, RedirectAttributes redirectAttributes, @Valid Spitter spitter, Errors errors) throws IOException { if (errors.hasErrors()) { return "registerForm"; }
Part接口
package javax.servlet.http; public interface Part { public InputStream getInputStream() throws IOException; public String getContentType(); public String getName(); public String getSubmittedFileName(); public long getSize(); public void write(String fileName) throws IOException; public void delete() throws IOException; public String getHeader(String name); public Collection<String> getHeaders(String name); public Collection<String> getHeaderNames(); }
不少状况下,Part方法的名称与MultiPartFile方法的名称是彻底相同的。有一些比较相似,可是稍有差异。
好比getSubmittedFileName()
方法对应getOriginalFilename()
.相似的,write()方法对应于transfer()方法,借助该方法咱们可以将上传的文件写入文件系统中。
值得一提的是,若是没有在编写控制器方法的时候,经过Part参数的形式接受文件上传,那就不必配置MultipartResolver
了。只有使用MultipartFile
的时候 ,咱们才须要MultipartResolver
.
Spring提供了多种方式将异常转换为响应
处理异常最简单的方式就是将其映射到HTTP状态码上。
异常通常由Spring自身抛出,做为DispatcherServlet处理过程当中或执行校验时出现问题的结果。
Spring提供了一种机制,可以经过使用@RequestStatus注解将其映射为HTTP状态码
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET) public String spittle( @PathVariable("spittleId") long spittleId, Model model) { Spittle spittle = spittleRepository.findOne(spittleId); if (spittle == null) { throw new SpittleNotFoundException(); //这里会抛出异常 } model.addAttribute(spittle); return "spittle"; }
若是资源没有找到的话,HTTP状态码404是最为精确的响应状态码
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Spittle Not Found") public class SpittleNotFoundException extends RuntimeException { }
若是响应中不只包含状态码,还要包含所产生的错误信息,须要按照请求的方式来处理异常。
@RequestMapping(method=RequestMethod.POST) public String saveSpittle(SpittleForm form, Model model) { try { spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude())); return "redirect:/spittles"; } catch (DuplicateSpittleException e) { //捕获异常 return "error/duplicate"; } }
@RequestMapping(method=RequestMethod.POST) public String saveSpittle(SpittleForm form, Model model) { spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude())); return "redirect:/spittles"; return "error/duplicate"; }
它只关注成功保存Spittle的状况,因此只须要一个执行路径,很容易理解和测试。
@ExceptionHandler(DuplicateSpittleException.class) public String handleNotFound() { return "error/duplicate"; }
方法上加上@ExceptionHandler注解后,当方法抛出异常的时候,将委托该方法来处理,它可以处理同一个控制器中全部的方法抛出的异常。
若是控制器类的特定切面可以 运用到整个应用程序的全部控制器中,那么这将会便利不少。,为了 避免重复,咱们会建立一个基础的控制器,全部的控制器类要扩展这个类,从而继承通用的@ExceptionHandler方法。
Spring3.2 引入了一个新的解决方法:控制器通知。控制器通知(controllerAdvice)是任意带有@ControllerAdvice注解的类,这个类会包含一个或多个 以下类型的方法:
@ControllerAdvice public class AppWideExceptionHandler { @ExceptionHandler(DuplicateSpittleException.class) public String handleNotFound() { return "error/duplicate"; } }
“redirect:”前缀能让重定向功能变得更简单,可是,请稍等,Spirng为重定向功能还提供了一些其余的辅助功能。
当一个处理器方法完成以后,该方法所指定的模型数据会将复制到请求中,并做为请求中的属性,请求会转发(forward)到视图上进行渲染。
对于 重定向来讲,模型并不能完全数据,有一些其余方法,可以从发起重定向的方法传递数据给处理重定向的方法
@RequestMapping(value="/register", method=POST) public String processRegistration( @Valid SpitterForm spitterForm, Errors errors) throws IllegalStateException, IOException { if (errors.hasErrors()) { return "registerForm"; } Spitter spitter = spitterForm.toSpitter(); spitterRepository.save(spitter); MultipartFile profilePicture = spitterForm.getProfilePicture(); profilePicture.transferTo(new File("/tmp/spittr/" + spitter.getUsername() + ".jpg")); return "redirect:/spitter/" + spitter.getUsername(); //根据名字重定向 }
经过路径变量和查询参数的形式跨重定向传递数据是很简单直接的方式,可是也有限制它只能发送简单的值,如String和数字的值。在URl中,并无 办法发送更为复杂的值,但这正是flash属性可以提供帮助。
### 7.5.2 使用flash属性。
有个方案是将Spittr放到会话中,会话能长期存在,而且会话可以跨多个请求,因此咱们能够在重定向以前将Spittr放到会话中,并在重定向后,从会话中取出 ,固然,咱们须要负责在重定向以后在会话中将其清理掉。
Spring提供了将数据发送为flash属性的功能,按照定义,flash属性会一直携带这些数据,直到下一次请求,而后才消失
Spring提供了经过RedirectAttributes设置flash属性的方式,这是Spring3.1引入的Modwl的一个子接口 。 RedirectAttributes提供了Model的全部功能。除此以外,还有几个方法用来设置flash属性。
public String processRegistration( @RequestPart(value="profilePictures", required=false) Part fileBytes, RedirectAttributes redirectAttributes, @Valid Spitter spitter, Errors errors) throws IOException { if (errors.hasErrors()) { return "registerForm"; } spitterRepository.save(spitter); redirectAttributes.addAttribute("username", spitter.getUsername()); //调用方法,将spitter做为ket,Spitter做为值。也能够不设置值,根据值得类型自行判断。 redirectAttributes.addFlashAttribute(spitter); return "redirect:/spitter/" + spitter.getUsername();
在重定向以前,全部的flash属性都会复制到会话中,在重定向结束后,存在会话中的 flash属性会被取出,并从会话中转移到模型之中。
@RequestMapping(value="/{username}", method=GET) public String showSpitterProfile( @PathVariable String username, Model model) { if (!model.containsAttribute("spitter")) { model.addAttribute( spitterRepository.findByUsername(username)); } return "profile"; }
showSpitterProfile()方法首先检查是否存在key为sptter的modle属性。若是模型中包含的话,那就什么都不用作了。包含的Spitter对象将会传递到视图中进行渲染。若是不包含则从spitterRepository中查找Spitter,并将其放到模型中。
在Spirng中,老是会有“尚未结束”的感受更多的特性,更多的选择,以及实现开发目标的更多方式。Spring MVC有不少功能和技巧。
固然,Spirng MVC的环境搭建是由多种可选方案的一个领域。在本章中,咱们首先看来一下搭建Spring MVC中DispatcherServlet
和ContextLoaderListener
的多种方式。还看到了如何调整DispatcherServlet
的注册功能以及如何注册自定义的Servlet和FIlterr。若是你须要将应用部署到更老的服务器上,咱们还快速了解了如何使用web.xml声明DispatcherServlet
和ContextLoaderListener
.
而后咱们了解 如何处理Spirng MVC控制器所抛出的异常,尽管带有@Requestmapping注解的方法能够在自身的代码中处理异常,可是若是将异常处理的代码抽取到单独的方法中,那么控制器的代码会整洁不少。
为了采用一致的方式处理通用的任务,包括在应用中的全部控制器 中处理异常,Spirng 3.2 引入了@ControllerAdvice,他所建立的类可以将控制器的通用行为抽取到同一个方法。
最后,咱们看了下如何跨重定向传递数据,包括Spring对flash属性的支持:相似于模板,可是能在重定向后存活下来。这样的话,就能采用很是恰当的方式为POST请求执行一个重定向回应。并且可以将处理POST请求时的模型数据传递过来,而后再重定向后使用或展示这些模型数据。
若是你有疑惑的话,那么能够告诉你,这就是我所说的“更多的功能”,其实,咱们并无讨论到Spirng MVC 的每一个方面。咱们将会在16章中从新讨论 Spirng MVC,到时你会看到如何使用它来建立REST API。
但如今,咱们将会暂时放下Spring MVC,看一下Spirng web Flow,这是一个构建在Spirng MVC 之上的流程框架,它可以引导用户执行一系列向导步骤。
纳闷,你忘记总结文件上传了。期待下一章。