SpringMVC 与表单提交(post/put/delete的用法)
为了迎合Restful风格,提供的接口可能会包含:put、delete提交方式。在springmvc中实现表单以put、delete方式提交时,需要使用HiddenHttpMethodFilter过滤器。该过滤器的实现原理,默认在form表单内部定义一个hidden隐藏的标签,默认需要标签名为:_method,hidden标签的value为put或delete;过滤器会接收_method的hidden标签的value,如果发现存在_method的标签,并且value为put或delete时,会重新包装一个请求,请求类型会设置为_method标签的value值。进而实现将post方式提交表单转化为Delete或Put请求。
实现form post表当以put或delete方式提交的步骤包含以下:
idea下创建一个pom.xml工程:
创建完后,工程结构如下:
设置web.xml配置如下:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app>
<display-name>springmvcdemo</display-name>
<welcome-file-list>
<welcome-file>/index</welcome-file>
</welcome-file-list> <!--结束后端数据输出到前端乱码问题-->
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--可以通过配置覆盖默认'_method'值 -->
<init-param>
<param-name>methodParam</param-name>
<param-value>_method</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <servlet>
<servlet-name>myAppServletName</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myAppServletName</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> </web-app>
此时在WEB-INF下还需要新建一个applicationContext.xml配置文件,配置文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描所有的 spring包下的文件;-->
<!--当然需要在spring配置文件里面配置一下自动扫描范围
<context:component-scan base-package="*"/>
*代表你想要扫描的那些包的目录所在位置。Spring 在容器初始化时将自动扫描 base-package 指定的包及其子包下的所有的.class文件,
所有标注了 @Repository 的类都将被注册为 Spring Bean。
-->
<context:component-scan base-package="com.dx.test"/>
<!--新增加的两个配置,这个是解决406问题的关键-->
<!--mvc注解驱动(可代替注解适配器与注解映射器的配置),默认加载很多参数绑定方法(实际开发时使用)-->
<context:annotation-config/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
</bean> <!--自己后加的,该BeanPostProcessor将自动对标注@Autowired的bean进行注入-->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"></bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<!--<ref bean="stringHttpMessageConverter"/>-->
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter"/>
<!--
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
-->
</list>
</property>
</bean> </beans>
配置pom.xml引入依赖如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.dx.test</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging> <name>demo Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!--Spring版本号-->
<spring.version>5.2.0.RELEASE</spring.version>
<!--jstl标签库-->
<jstl.version>1.2</jstl.version>
<standard.version>1.1.2</standard.version>
</properties> <dependencies>
<!-- servlet相关
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency> <!--form 设置为enctype="multipart/form-data",多文件上传,在applicationContext.xml中配置了bean multipartResolver时,需要依赖该包。-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency> <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency> <!--spring单元测试依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency> <!--springMVC核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency> <!--spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency> <!--AOP begin-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency> <dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency> <dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
<!--AOP end--> <!--json依赖-->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.5.2</version>
</dependency> <!--jstl库-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>${standard.version}</version>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies> <build>
<finalName>demo</finalName>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>9090</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
/WEB-INF/views/index.jsp内容如下:
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
/WEB-INF/views/article/list.jsp内容如下:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!-- 屏蔽tomcat 自带的 EL表达式 -->
<%@ page isELIgnored="false" %>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC Demo</title>
</head>
<body>
<h2>All Articles</h2>
<table border="1">
<tr>
<th>Article Id</th>
<th>Article Name</th>
</tr> <c:forEach items="${articles}" var="article">
<tr>
<td>${article.id}</td>
<td>${article.title}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
/WEB-INF/views/article/show.jsp内容如下:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!-- 屏蔽tomcat 自带的 EL表达式 -->
<%@ page isELIgnored="false" %>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC Demo</title>
</head>
<body>
<h2>Post方式提交:</h2>
<form:form name="article" method="POST" action="update_with_post" modelAttribute="article">
Id:<form:hidden path="id"/>
Title: <form:input path="title" style="width:200px;"/><br/>
Content: <form:input path="content" style="width:200px;"/><br/>
<input type="submit" value="Submit" />
</form:form>
<form name="article" method="POST" action="update_with_post">
Id:<input name="id" id="id" value="${article.id}"/><br/>
Title:<input name="title" id="title" value="${article.title}"/><br/>
Content:<input name="content" id="content" value="${article.content}"/><br/>
<input type="submit" value="Submit" />
</form>
<h2>Post包含上传文件提交:</h2>
<form:form name="article" method="POST" action="update_with_post_file" modelAttribute="article" enctype="multipart/form-data">
Id:<form:hidden path="id"/>
Title: <form:input path="title" style="width:200px;"/><br/>
Content: <form:input path="content" style="width:200px;"/><br/>
yourfile: <input type="file" name="file"/><br/>
<input type="submit" value="Submit" />
</form:form>
<form name="article" method="POST" action="update_with_post_file" enctype="multipart/form-data">
Id:<input name="id" id="id" value="${article.id}"/><br/>
Title:<input name="title" id="title" value="${article.title}"/><br/>
Content:<input name="content" id="content" value="${article.content}"/><br/>
yourfile: <input type="file" name="file"/><br/>
<input type="submit" value="Submit" />
</form> <h2>Put方式提交:</h2>
<form:form name="article" method="POST" action="update_with_put" modelAttribute="article">
<input type="hidden" name="_method" value="PUT"/>
Id:<form:hidden path="id"/>
Title: <form:input path="title" style="width:200px;"/><br/>
Content: <form:input path="content" style="width:200px;"/><br/>
<input type="submit" value="Submit" />
</form:form>
<form name="article" method="POST" action="update_with_put">
<input type="hidden" name="_method" value="PUT"/>
Id:<input name="id" id="id" value="${article.id}"/><br/>
Title:<input name="title" id="title" value="${article.title}"/><br/>
Content:<input name="content" id="content" value="${article.content}"/><br/>
<input type="submit" value="Submit" />
</form> <h2>Put包含上传文件提交:</h2>
<form:form name="article" method="POST" action="update_with_put_file" modelAttribute="article" enctype="multipart/form-data">
<input type="hidden" name="_method" value="PUT"/>
Id:<form:hidden path="id"/>
Title: <form:input path="title" style="width:200px;"/><br/>
Content: <form:input path="content" style="width:200px;"/><br/>
yourfile: <input type="file" name="file"/><br/>
<input type="submit" value="Submit" />
</form:form>
<form method="POST" name="article" action="update_with_put_file" enctype="multipart/form-data">
<input type="hidden" name="_method" value="PUT"/>
Id:<input name="id" id="id" value="${article.id}"/><br/>
Title:<input name="title" id="title" value="${article.title}"/><br/>
Content:<input name="content" id="content" value="${article.content}"/><br/>
yourfile: <input type="file" name="file"/><br/>
<input type="submit" value="Submit" />
</form> </body>
</html>
com.dx.test.model.ArticleModel.java
package com.dx.test.model; import java.io.Serializable;
import java.util.Date; /**
* 文章内容
*/
public class ArticleModel implements Serializable {
private Long id;
private Long categoryId;
private String title;
private String content; private Date createTime;
private String createUser;
private String createUserId;
private Date modifyTime;
private String modifyUser;
private String modifyUserId; public ArticleModel() {
} public ArticleModel(Long id, Long categoryId, String title, String content) {
this.id = id;
this.categoryId = categoryId;
this.title = title;
this.content = content;
} /**
* @return the id
*/
public Long getId() {
return id;
} /**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
} /**
* @return the categoryId
*/
public Long getCategoryId() {
return categoryId;
} /**
* @param categoryId the categoryId to set
*/
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
} /**
* @return the title
*/
public String getTitle() {
return title;
} /**
* @param title the title to set
*/
public void setTitle(String title) {
this.title = title;
} /**
* @return the content
*/
public String getContent() {
return content;
} /**
* @param content the content to set
*/
public void setContent(String content) {
this.content = content;
} /**
* @return the createTime
*/
public Date getCreateTime() {
return createTime;
} /**
* @param createTime the createTime to set
*/
public void setCreateTime(Date createTime) {
this.createTime = createTime;
} /**
* @return the createUser
*/
public String getCreateUser() {
return createUser;
} /**
* @param createUser the createUser to set
*/
public void setCreateUser(String createUser) {
this.createUser = createUser;
} /**
* @return the createUserId
*/
public String getCreateUserId() {
return createUserId;
} /**
* @param createUserId the createUserId to set
*/
public void setCreateUserId(String createUserId) {
this.createUserId = createUserId;
} /**
* @return the modifyTime
*/
public Date getModifyTime() {
return modifyTime;
} /**
* @param modifyTime the modifyTime to set
*/
public void setModifyTime(Date modifyTime) {
this.modifyTime = modifyTime;
} /**
* @return the modifyUser
*/
public String getModifyUser() {
return modifyUser;
} /**
* @param modifyUser the modifyUser to set
*/
public void setModifyUser(String modifyUser) {
this.modifyUser = modifyUser;
} /**
* @return the modifyUserId
*/
public String getModifyUserId() {
return modifyUserId;
} /**
* @param modifyUserId the modifyUserId to set
*/
public void setModifyUserId(String modifyUserId) {
this.modifyUserId = modifyUserId;
} @Override
public String toString() {
return "ArticleModel{" +
"id=" + id +
", categoryId=" + categoryId +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
com.dx.test.controller.HomeController.java
package com.dx.test.controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; @Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
return "index";
}
}
com.dx.test.controller.ArticleController.java
package com.dx.test.controller; import com.dx.test.model.ArticleModel;
import com.dx.test.service.IArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest;
import javax.xml.ws.http.HTTPBinding;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional; @Controller
@RequestMapping("/article")
public class ArticleController {
@Autowired
private IArticleService articleService; @GetMapping("/list")
public String queryList(Model model, @RequestHeader(value = "userId", required = false) String userId) {
List<ArticleModel> articleModelList = articleService.queryList();
model.addAttribute("articles", articleModelList);
return "article/list";
} @RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView queryById(@PathVariable(value = "id", required = true) Long id) {
ArticleModel articleModel = articleService.getById(id);
ModelAndView mv = new ModelAndView();
mv.setViewName("article/show");
mv.addObject("article", articleModel);
return mv;
} @RequestMapping(value = "/update_with_post", method = RequestMethod.POST)
public String update_with_post(@ModelAttribute(value = "article") ArticleModel article) {
System.out.println(article);
return "index";
} @RequestMapping(value = "/update_with_post_file", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public String update_with_post_file(@ModelAttribute(value = "article") ArticleModel article, @RequestBody MultipartFile file, HttpServletRequest request) {
System.out.println(article);
System.out.println(file);
String id = request.getParameter("id");
String title = request.getParameter("title");
String content = request.getParameter("content");
System.out.println(String.format("%s,%s,%s", id, title, content)); return "index";
} @RequestMapping(value = "/update_with_put", method = RequestMethod.PUT)
public String update_with_put(@ModelAttribute(value = "article") ArticleModel article) {
System.out.println(article);
return "index";
} @RequestMapping(value = "/update_with_put_file", method = RequestMethod.PUT, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public String update_with_put_file(@ModelAttribute(value = "article") ArticleModel article, @RequestBody MultipartFile file, HttpServletRequest request) {
System.out.println(article);
System.out.println(file);
String id = request.getParameter("id");
String title = request.getParameter("title");
String content = request.getParameter("content");
System.out.println(String.format("%s,%s,%s", id, title, content)); return "index";
}
}
给demo工程配置tomcat:
之后启动后可以从Console上打印信息,查看到端口,我这里端口是9090
1)Post(不含上传文件)方式提交
在http://localhost:9090/article/1页面中:
对应show.jsp上的内容:
按照上边新建项目的web.xml、applicationContext.xml、ArticleController.java,提交'Post方式提交:,后台就能正常接收到参数,此时后台打印信息如下:
[DEBUG] POST "/article/update_with_post", parameters={masked}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_post(ArticleModel)
ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容', createTime=null, createUser='null', createUserId='null', modifyTime=null, modifyUser='null', modifyUserId='null'}
[DEBUG] View name 'index', model {article=ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容', createTime=null, createUser='null', createUserId='null', modifyTime=null, modifyUser='null', modifyUserId='null'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
[DEBUG] POST "/article/update_with_post", parameters={masked}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_post(ArticleModel)
ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容', createTime=null, createUser='null', createUserId='null', modifyTime=null, modifyUser='null', modifyUserId='null'}
[DEBUG] View name 'index', model {article=ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容', createTime=null, createUser='null', createUserId='null', modifyTime=null, modifyUser='null', modifyUserId='null'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
备注:
1)post不含上传文件的意思是:form表单中不包含属性enctype="multipart/form-data"设置。
2)上边日志包含两次提交分别代表/WEB-INF/views/article/show.jsp页面中“Post方式提交:”下边的两个表单提交:第一个表单是jst方式的表单,第二个表单是纯html的表单。
2)Post(包含上传文件)方式提交
在http://localhost:9090/article/1页面中:
对应show.jsp中内容:
因为是上传文件,而且handler方法使用HttpServletRequest参数,因此需要在pom.xml中引入上传文件依赖包,和servlet包:
<!-- servlet相关,在handler参数中中包含:HttpServletRequest request参数时,需要引入javax.servlet依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency> <!--form 设置为enctype="multipart/form-data",多文件上传,在applicationContext.xml中配置了bean multipartResolver时,需要依赖该包。-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency> <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
按照上边新建项目的web.xml、applicationContext.xml、ArticleController.java,提交'Post包含上传文件提交:',后台不能正常接收到参数,此时后台打印信息如下:
[DEBUG] POST "/article/update_with_post_file", parameters={}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_post_file(ArticleModel, MultipartFile, HttpServletRequest)
ArticleModel{id=null, categoryId=null, title='null', content='null', createTime=null, createUser='null', createUserId='null', modifyTime=null, modifyUser='null', modifyUserId='null'}
null
null,null,null
[DEBUG] View name 'index', model {article=ArticleModel{id=null, categoryId=null, title='null', content='null', createTime=null, createUser='null', createUserId='null', modifyTime=null, modifyUser='null', modifyUserId='null'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
[DEBUG] POST "/article/update_with_post_file", parameters={}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_post_file(ArticleModel, MultipartFile, HttpServletRequest)
ArticleModel{id=null, categoryId=null, title='null', content='null', createTime=null, createUser='null', createUserId='null', modifyTime=null, modifyUser='null', modifyUserId='null'}
null
null,null,null
[DEBUG] View name 'index', model {article=ArticleModel{id=null, categoryId=null, title='null', content='null', createTime=null, createUser='null', createUserId='null', modifyTime=null, modifyUser='null', modifyUserId='null'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
备注:
1)post包含上传文件的意思是:form表单中包含属性enctype="multipart/form-data"设置。
2)上边日志包含两次提交分别代表/WEB-INF/views/article/show.jsp页面中“Post包含上传文件提交:”下边的两个表单提交:第一个表单是jst方式的表单,第二个表单是纯html的表单。
从上边日志可以看到,按照新建项目的方式提交“Post包含上传文件”的表单,后台是接收不到文件和article参数,而且request中也接收不到id,title,content参数。
此时,需要修改applicationContext.xml和web.xml才能实现后端接收到file和参数信息:
applicationContext.xml中需要注册bean“multipartResolver”:
<!-- 配置文件上传解析器 enctype="multipart/form-data" -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 指定所上传文件的总大小不能超过20M。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
<property name="maxUploadSize" value="209715200"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="resolveLazily" value="true"/>
</bean>
web.xml需要加入以下filter“MultipartFilter”和引入listener“ContextLoaderListener”:
<!--
全局初始化数据,spring的监听器读取此配置文件,多个配置文件用分号分隔
如果在web.xml中不写任何参数配置信息,默认的路径是/WEB-INF/applicationContext.xml,在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml;
如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <!-- 文件上传与下载过滤器:form表单中存在文件时,该过滤器可以处理http请求中的文件,被该过滤器过滤后会用post方法提交,form表单需设为enctype="multipart/form-data"-->
<!-- 注意:必须放在HiddenHttpMethodFilter过滤器之前 -->
<filter>
<filter-name>MultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
<init-param>
<param-name>multipartResolverBeanName</param-name>
<!--spring中配置的id为multipartResolver的解析器-->
<param-value>multipartResolver</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<!--<servlet-name>myAppServletName</servlet-name>-->
<url-pattern>/*</url-pattern>
</filter-mapping>
此时重新提交两个表单,后台打印日志如下:
[DEBUG] Using MultipartResolver 'multipartResolver' for MultipartFilter
[DEBUG] Part 'file', size 9 bytes, filename='test.svg'
[DEBUG] POST "/article/update_with_post_file", parameters={masked}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_post_file(ArticleModel, MultipartFile, HttpServletRequest)
ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}
org.springframework.web.multipart.commons.CommonsMultipartFile@7df79c2d
1,算法与数据结构--综合提升篇(c++版),文章内容
[DEBUG] View name 'index', model {article=ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
[DEBUG] Cleaning up part 'file', filename 'test.svg' [DEBUG] Using MultipartResolver 'multipartResolver' for MultipartFilter
[DEBUG] Part 'file', size 20098 bytes, filename='试用期考核报告-员工版.xlsx'
[DEBUG] POST "/article/update_with_post_file", parameters={masked}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_post_file(ArticleModel, MultipartFile, HttpServletRequest)
ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}
org.springframework.web.multipart.commons.CommonsMultipartFile@448bda6e
1,算法与数据结构--综合提升篇(c++版),文章内容
[DEBUG] View name 'index', model {article=ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
[DEBUG] Cleaning up part 'file', filename '试用期考核报告-员工版.xlsx'
3)Put(不含上传文件)方式提交
在http://localhost:9090/article/1页面中:
对应show.jsp中内容:
按照上边新建项目的web.xml、applicationContext.xml、ArticleController.java,提交'Put方式提交:',后台不能正常接收到参数,此时提交后页面会跳转到405错误页面:
服务器打印日志信息如下:
DEBUG] POST "/article/update_with_put", parameters={masked}
[WARNING] Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
[DEBUG] Completed 405 METHOD_NOT_ALLOWED
[DEBUG] POST "/article/update_with_put", parameters={masked}
[WARNING] Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
[DEBUG] Completed 405 METHOD_NOT_ALLOWED
备注:
1)post不含上传文件的意思是:form表单中不包含属性enctype="multipart/form-data"设置。
2)上边日志包含两次提交分别代表/WEB-INF/views/article/show.jsp页面中“Put方式提交:”下边的两个表单提交:第一个表单是jst方式的表单,第二个表单是纯html的表单。
从错误页面和日志来查看,该错误原因是,springmvc默认不能以form post+<input type="hidden" name="_method" value="put"/>方式实现put提交方式。
解决该错误的方案:
修改web.xml添加HiddenHtmlMethodFilter filter。
<!--
注意:HiddenHttpMethodFilter必须作用于dispatcher前
请求method支持 put 和 delete 必须添加该过滤器
作用:可以过滤所有请求,并可以分为四种
使用该过滤器需要在前端页面加隐藏表单域
<input type="hidden" name="_method" value="请求方式(put/delete)">
post会寻找_method中的请求式是不是put 或者 delete,如果不是 则默认post请求
-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<!--servlet为springMvc的servlet名 -->
<servlet-name>myAppServletName</servlet-name>
<!--<url-pattern>/*</url-pattern>-->
</filter-mapping>
修改web.xml配置之后,重新提交两个表单,后台打印结果如下:
[DEBUG] PUT "/article/update_with_put", parameters={masked}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_put(ArticleModel)
ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}
[DEBUG] View name 'index', model {article=ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
[DEBUG] PUT "/article/update_with_put", parameters={masked}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_put(ArticleModel)
ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}
[DEBUG] View name 'index', model {article=ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
4)Put(包含上传文件)方式提交
在http://localhost:9090/article/1页面中:
对应show.jsp中内容:
因为是上传文件,而且handler方法使用HttpServletRequest参数,因此需要在pom.xml中引入上传文件依赖包,和servlet包:
<!-- servlet相关,在handler参数中中包含:HttpServletRequest request参数时,需要引入javax.servlet依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency> <!--form 设置为enctype="multipart/form-data",多文件上传,在applicationContext.xml中配置了bean multipartResolver时,需要依赖该包。-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency> <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
按照上边新建项目的web.xml、applicationContext.xml、ArticleController.java,提交'Put方式提交:',后台不能正常接收到参数,此时提交后页面会跳转到405错误页面:
服务器打印日志信息如下:
DEBUG] POST "/article/update_with_put_file", parameters={masked}
[WARNING] Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
[DEBUG] Completed 405 METHOD_NOT_ALLOWED [DEBUG] POST "/article/update_with_put_file", parameters={masked}
[WARNING] Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
[DEBUG] Completed 405 METHOD_NOT_ALLOWED
备注:
1)post不含上传文件的意思是:form表单中不包含属性enctype="multipart/form-data"设置。
2)上边日志包含两次提交分别代表/WEB-INF/views/article/show.jsp页面中“Put方式提交:”下边的两个表单提交:第一个表单是jst方式的表单,第二个表单是纯html的表单。
从错误页面和日志来查看,该错误原因是,springmvc默认不能以form post+<input type="hidden" name="_method" value="put"/>方式实现put提交方式。
解决该错误的方案:
1)为了实现put方式提交修改web.xml,添加HiddenHtmlMethodFilter filter;
2)为了实现上传文件需要修改web.xml,添加filter“MultipartFilter”和引入listener“ContextLoaderListener”;
3)为了实现上传文件需要修改applicaitonContext.xml,注册bean“multipartResolver”。
web.xml修改后内容为:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app>
<display-name>springmvcdemo</display-name>
<welcome-file-list>
<welcome-file>/index</welcome-file>
</welcome-file-list> <!--
全局初始化数据,spring的监听器读取此配置文件,多个配置文件用分号分隔
如果在web.xml中不写任何参数配置信息,默认的路径是/WEB-INF/applicationContext.xml,在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml;
如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <!-- 文件上传与下载过滤器:form表单中存在文件时,该过滤器可以处理http请求中的文件,被该过滤器过滤后会用post方法提交,form表单需设为enctype="multipart/form-data"-->
<!-- 注意:必须放在HiddenHttpMethodFilter过滤器之前 -->
<filter>
<filter-name>MultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
<init-param>
<param-name>multipartResolverBeanName</param-name>
<!--spring中配置的id为multipartResolver的解析器-->
<param-value>multipartResolver</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<!--<servlet-name>myAppServletName</servlet-name>-->
<url-pattern>/*</url-pattern>
</filter-mapping> <!--
注意:HiddenHttpMethodFilter必须作用于dispatcher前
请求method支持 put 和 delete 必须添加该过滤器
作用:可以过滤所有请求,并可以分为四种
使用该过滤器需要在前端页面加隐藏表单域
<input type="hidden" name="_method" value="请求方式(put/delete)">
post会寻找_method中的请求式是不是put 或者 delete,如果不是 则默认post请求
-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<!--servlet为springMvc的servlet名 -->
<servlet-name>myAppServletName</servlet-name>
<!--<url-pattern>/*</url-pattern>-->
</filter-mapping>
<!--结束后端数据输出到前端乱码问题-->
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--可以通过配置覆盖默认'_method'值 -->
<init-param>
<param-name>methodParam</param-name>
<param-value>_method</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <servlet>
<servlet-name>myAppServletName</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myAppServletName</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> </web-app>
applicationContext.xml修改后内容为:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描所有的 spring包下的文件;-->
<!--当然需要在spring配置文件里面配置一下自动扫描范围
<context:component-scan base-package="*"/>
*代表你想要扫描的那些包的目录所在位置。Spring 在容器初始化时将自动扫描 base-package 指定的包及其子包下的所有的.class文件,
所有标注了 @Repository 的类都将被注册为 Spring Bean。
-->
<context:component-scan base-package="com.dx.test"/>
<!--新增加的两个配置,这个是解决406问题的关键-->
<!--mvc注解驱动(可代替注解适配器与注解映射器的配置),默认加载很多参数绑定方法(实际开发时使用)-->
<context:annotation-config/>
<mvc:annotation-driven/>
<!--
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.dx.test.interceptors.LogInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
-->
<!--end--> <!--
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
</bean> <!-- 配置文件上传解析器 enctype="multipart/form-data" -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 指定所上传文件的总大小不能超过20M。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
<property name="maxUploadSize" value="209715200"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="resolveLazily" value="true"/>
</bean> <!--自己后加的,该BeanPostProcessor将自动对标注@Autowired的bean进行注入-->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"></bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<!--<ref bean="stringHttpMessageConverter"/>-->
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter"/>
<!--
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
-->
</list>
</property>
</bean> </beans>
修改后,重新提交两个表单,此时服务端打印日志如下:
[DEBUG] Using MultipartResolver 'multipartResolver' for MultipartFilter
[DEBUG] Part 'file', size 1181 bytes, filename='ImageVO.java'
[DEBUG] PUT "/article/update_with_put_file", parameters={masked}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_put_file(ArticleModel, MultipartFile, HttpServletRequest)
ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}
org.springframework.web.multipart.commons.CommonsMultipartFile@62032272
1,算法与数据结构--综合提升篇(c++版),文章内容
[DEBUG] View name 'index', model {article=ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
[DEBUG] Cleaning up part 'file', filename 'ImageVO.java'
[DEBUG] Using MultipartResolver 'multipartResolver' for MultipartFilter
[DEBUG] Part 'file', size 1732 bytes, filename='UploadImgParam.java'
[DEBUG] PUT "/article/update_with_put_file", parameters={masked}
[DEBUG] Mapped to com.dx.test.controller.ArticleController#update_with_put_file(ArticleModel, MultipartFile, HttpServletRequest)
ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}
org.springframework.web.multipart.commons.CommonsMultipartFile@32d8a82d
1,算法与数据结构--综合提升篇(c++版),文章内容
[DEBUG] View name 'index', model {article=ArticleModel{id=1, categoryId=null, title='算法与数据结构--综合提升篇(c++版)', content='文章内容'}, org.springframework.validation.BindingResult.article=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
[DEBUG] Forwarding to [/WEB-INF/views/index.jsp]
[DEBUG] Completed 200 OK
[DEBUG] Cleaning up part 'file', filename 'UploadImgParam.java'
备注:该web.xml和applicationContext.xml的配置方式,可以支持post方式提交、post+上传文件提交、put提交、put+上传文件提交。因此,推荐使用该配置。
总结:
1)form属性设置encrypt='mutilpart/form-data'时,如何正确配置web.xml才能以put方式提交表单?
1)为了实现put方式提交修改web.xml,添加HiddenHtmlMethodFilter filter;
2)为了实现上传文件需要修改web.xml,添加filter“MultipartFilter”和引入listener“ContextLoaderListener”;
3)为了实现上传文件需要修改applicaitonContext.xml,注册bean“multipartResolver”。
备注:
1)如果multipartResolver bean配置的是org.springframework.web.multipart.commons.CommonsMultipartResolver,因为CommonsMultipartResolver依赖commons-fileupload.jar和commons-io.jar,所以需要pom.xml引入这两个包。
2)handler方法使用HttpServletRequest request作为参数,则需要在pom.xml中引入servlet包。
2)留3个问题:
1)ContextLoaderListener在Web.xml中配置与不配置的区别:
为什么上边上传文件方式需要配置它,不配置会导致上传文件后端接收不到;
它在上传文件中是不是必须配置的吗?
2)web.xml 中的 ContextLoaderListener 是干什么用的?
3)web.xml 中的 DispatcherServlet 是如何加载的,它其什么作用?