SpringMVC 参数映射与文件上传
handler参数映射:
接下来就是Spring的各个处理细节了,无论框架如何疯转其实我们处理请求的流程是不变的,设计到的操作也是固定的,举个例子,当我们要实现一个登陆功能时:
创建一个用于处理登录请求的Servlet
实现doget等其他http方法(一些情况可能根据业务需要限制请求方法)
从request对象中取出数据
处理编码
验证参数是否符合要求
对参数数据类型进行转换(需要时)
开始业务逻辑处理(登录)
可能需要操作session来完成
组织响应给数据,可能是html可能是json,
异常处理
Header与cookie的处理
整个SpringMVC其实就是帮我们对上面的操作进行封装,当然了SpringMVC也提供了更多的功能,如国际化..
Handler方法特殊参数
在handler方法中我们可以添加一下参数,用于获取一些特殊的对象:
- HttpServletRequest,HttpServletResponse
 - HttpSession
 - Model
 - ModelMap
 
 model 是框架帮我们创建好的的Model对象,若使用该参数作为返回的model,则需要修改方法返回值为String用于指定视图名称;
案例:
  @RequestMapping("/courseList2.action")
    public String courseList(Model model) {
        model.addAttribute("courses", courseService.selectCourseList());
        return "courses.jsp";
    }
    @RequestMapping("/courseList3.action")
    public String courseList(ModelMap model, HttpSession session,HttpServletRequest request,HttpServletResponse response) {
        System.out.println(request);
        System.out.println(response);
        System.out.println(session.getId());
        model.addAttribute("courses", courseService.selectCourseList());
        return "courses.jsp";
    }
请求参数映射:
在使用Servlet开发的过程中我们会频繁的调用request.getAttribute来获取请求参数,参数较少时还没什么,一旦参数较多的时候就会产生大量的冗余代码,SpringMVC提供了多种以简化获取参数的过程的方法
映射到handler方法的参数
在handler方法中添加与前台参数名称和类型匹配的参数,框架会自动解析参数传入handler方法中;
案例:
假设我们要修改课程信息的功能,首先要获取原始信息
@RequestMapping("/edit.action")
public String edit(Model model,Integer id) {
    Course course = courseService.selectCourseByID(id);
    model.addAttribute("course",course);
    return "edit.jsp";
}
支持的参数类型:
整形:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
@RequestParam
当前后台参数名称不匹配时可以@RequestParam注解进行自定义映射;
注解参数:
- value,name 两者都代表前台参数名称
 - requerid 该参数是否必须
 - defaultValue 参数默认值
 
案例:
@RequestMapping("/edit.action")
public String edit(Model model,@RequestParam("id") Integer iid) {
    Course course = courseService.selectCourseByID(iid);
    model.addAttribute("course",course);
    return "edit.jsp";
}
注意:参数类型可以是基础类型也可以是包装类型,建议使用包装类型,这样可以保证为获取到参数时不会因为null无法转换为基础类型而导致的异常;
edit.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head></head>
<form action="updateCourse.action" method="post">
    <input name="id" value="${course.id}" hidden="hidden"/>
    <input name="name" value="${course.name}"/>
    <input name="teachName" value="${course.teachName}"/>
  <%--    <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/>--%>
    <input name="score" type="number" value="${course.score}"/>
    <input name="hours" type="number" value="${course.hours}"/>
    <input type="submit">
</form>
<body>
</body>
</html>
映射到实体类
当参数个数非常多时上面的方法就显得麻烦了,SpringMVC支持将参数映射到一个实体类;
在handler方法中添加任意类型实体类作为参数; 同样的只有参数名称和实体属性一致时才能映射成功;
我们继续完善修改功能,现在要获取修改后的内容了:
@RequestMapping("/updateCourse.action")
public String update(Course course) {
    courseService.updateCourse(course);
    return "/courseList.action";
}
乱码过滤器
上面的例子中出现了中文乱码问题,请求方法为post, 只需要在request中设置编码方式即可,但是此时参数已经被框架解析了,我们在handler中通过request设置以及不生效了,所以我们需要在请求到达SpringMVC之前就进行处理,这就用到了以前学过的过滤器了;
好消息是SpringMVC以及提供了过滤器,我们只需要配置到web.xml中即可
<!--    编码过滤器-->
    <filter>
        <filter-name>encodingFilter</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>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
<!--    编码过滤器 END-->
需要注意的是,该过滤器只对post生效,如果是get乱码则还是需要修改tomcat的server.xml或是通过代码从ISO-8859-重新编码为UTF-8
String username = request.getParameter("username");
username = new String(username.getBytes("iso8859-1"), "utf-8"); //重新编码
参数类型转换
我们将edit.jsp中的开课标签取消注释,然后测试会发现系统给出了400异常,查看控制台可以看到以下信息:

意思是框架无法将String类型的请求参数转换为需要的Date类型,这就需要,这是因为日期格式多种多样,每个地区不同,所以这需要我们自己来实现转换;
编写转换器
实现convert接口即可作为转换器,该接口的两个两个泛型表示输入源类型和输出目标类型;
public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
        SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return sm.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}
mvc配置文件
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.kkb.converter.StringToDateConverter"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="conversionService"/>
使用注解注册转换器
使用@DateTimeFormat可以实现上面xml相同的配置:
public class Course {
  private Integer id;
  private String name;
  private String teachName;
  @DateTimeFormat(pattern = "yyyy-MM-dd")
  private Date startTime;
}
注意:注解作用在实体类对应的属性上,且配置文件必须添加<mvc:annotation-driven>
包装类型映射
当需要将参数映射到实体类的关联对象中时,也称为包装类型;
例如:在课程对象中有一个用户对象,表示这是某个用户的课程;前台需要同时传递课程对象的属性,和用户对象的属性,后台就需要要用一个包装类型来接收,即一个包装了用户对象的课程对象; 再说的简单点,即课程对象中包含一个用户对象;
在前台需要指出关联对象的属性名称,如:用户.name
实体:
public class Course {
    private Integer id;
    private String name;
    private String teachName;
    private Date startTime;
    private Integer score;
    private Integer hours;
    private User user;//新添加的User类属性
  set/get....
handler:
@RequestMapping("/updateCourse.action")
public String update(Course course) {
    courseService.updateCourse(course);
    return "/courseList.action";
}
jsp:
<form action="updateCourse.action" method="post">
    <input name="id" value="${course.id}" hidden="hidden"/>
    <input name="name" value="${course.name}"/>
    <input name="teachName" value="${course.teachName}"/>
    <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/>
    <input name="score" type="number" value="${course.score}"/>
    <input name="hours" type="number" value="${course.hours}"/>
    <input name="user.username"/>  <!-- 新添加的参数-->
    <input type="submit">
</form>
数组参数映射
一些情况下,某一参数可能会有多个值,例如要进行批量删除操作,要删除的id会有多个,那就需要将参数映射到一个数组中;HttPServletRequest原本就支持获取数组参数,SpringMVC仅是帮我们做了一个类型转换;
1.修改页面添加多选框:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
</head>
<body>
<form action="deleteCourses">
    <table border="1">
        <tr>
            <th>选择</th>
            <th>名称</th>
            <th>讲师</th>
            <th>开课日期</th>
            <th>学分</th>
            <th>课时</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${courses}" var="course">
            <tr>
                <td>
                    <input type="checkbox" name="ids" value="${course.id}">
                </td>
                <td>${course.name}</td>
                <td>${course.teachName}</td>
                <td>
                    <fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>
                </td>
                <td>${course.score}</td>
                <td>${course.hours}</td>
                <td>
                    <a href="editCourse?id=${course.id}">修改</a>
                </td>
            </tr>
        </c:forEach>
    </table>
	<input type="submit" value="批量删除">
</form>
</body>
</html>
2.handler方法:
@RequestMapping("/deleteCourses")
public String deleteCourses(Integer[] ids){
    courseService.deleteCourses(ids);
    return "/getCourses";
}
3.service方法:
public void deleteCourses(Integer[] ids) {
    for (Integer id : ids){
        courseMapper.deleteByPrimaryKey(id);
    }
}
list映射
当请求参数包含多个对象的属性数据,是需要使用list来接收,通常用在批量修改批量添加等;
list映射要求参数名称为对象属性[下标].属性名称,同时handler中使要用包装类型来接收;
以下是实现一个批量修改课程信息的功能;
1.修改页面中的td,使得每一个td都可以编辑:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
</head>
<body>
<form action="deleteCourses" id="fm">
    <table border="1">
        <tr>
            <th>选择</th><th>名称</th><th>讲师</th><th>开课日期</th><th>学分</th><th>课时</th><th>操作</th>
        </tr>
        <c:forEach items="${courses}" var="course" varStatus="status">
            <tr>
                <td><input type="checkbox" name="ids" value="${course.id}"></td>
                <td><input value="${course.name}" name="courses[${status.index}].name"/></td>
                <td><input value="${course.teachName}" name="courses[${status.index}].teachName"/></td>
                <td><input value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>' name="courses[${status.index}].startTime"/></td>
                <td><input value="${course.score}" name="courses[${status.index}].score"/></td>
                <td><input value="${course.hours}" name="courses[${status.index}].hours"/></td>
                <input hidden="hidden" name="courses[${status.index}].id" value="${course.id}">
                <td><a href="editCourse?id=${course.id}">修改</a></td>
            </tr>
        </c:forEach>
    </table>
    <input type="submit" value="批量删除">
    <input type="button" onclick='function updates() {
        document.getElementById("fm").action = "updateCourses"
        document.getElementById("fm").submit()
    }
    updates()' value="批量修改">
</form>
</body>
</html>
2.包装类型:
public class RequestPack {
    //用于接收参数列表的list
    private List<Course> courses;
    public List<Course> getCourses() {
        return courses;
    }
    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }
    public RequestPack(List<Course> courses) {
        this.courses = courses;
    }
}
3.handler方法:
@RequestMapping("/updateCourses")
public String updateCourses(RequestPack data){
    courseService.updateCourses(data.getCourses());
    return "/getCourses";
}
4.service方法:
public void updateCourses(List<Course> courses) {
    for (Course course:courses){
        courseMapper.updateByPrimaryKey(course);
    }
}
强调:list只能映射到包装类型中,无法直接映射到handler参数上
错误案例:
@RequestMapping("/updateCourses")
public String updateCourses(RequestPack data, ArrayList<Course> courses){
    courseService.updateCourses(data.getCourses());
    return "/getCourses";
}
文件上传
文件上传是web项目中非常常见的需求,SpringMVC使用了apache开源的两个库用于处理文件上传,所以在编写代码前我们需要先导入下面两个依赖包:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version>
</dependency>
假设需要实现一个上传图片的功能,需要现在数据库中添加一个字段用于存储图片的路径,同时不要忘记修改pojo以及mapper文件,使之与数据库字段对应
1.页面增加input 用于提交文件,并修改表单的enctype为multipart/form-data
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head></head>
<form action="updateCourse" method="post" enctype="multipart/form-data">
    <input name="id" value="${course.id}" hidden="hidden"/>
    名称:<input name="name" value="${course.name}"/>
    <br/>
    讲师:<input name="teachName" value="${course.teachName}"/>
    <br/>
    <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/>
    <br/>
    学分:<input name="score" type="number" value="${course.score}"/>
    <br/>
    课时:<input name="hours" type="number" value="${course.hours}"/>
    <br/>
    <c:if test="${course.pic != null}">
        <img src="${pageContext.servletContext.contextPath}${course.pic}" style="width: 100px;height: 100px">
    </c:if>
    图片:<input name="picFile" type="file"/><br/> <!-- 新增input-->
    <br/>
    <input type="submit">
</form>
<body>
</body>
</html>
2.在mvc配置文件中添加multipart解析器,(页面上传文件都是以,multipart编码方式)
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
3.handler方法中添加MultipartFile类型的参数
@RequestMapping("/updateCourse")
public String updateCourse(Course course, MultipartFile picFile) throws IOException {
    /***
     * 1.获得文件,取出文件后缀
     * 2.生成唯一标识,
     * 3.写入文件到指定路径
     * 4.存储文件路径到数据库
     */
    System.out.println(picFile.getName());
    String suffix = picFile.getOriginalFilename().substring(picFile.getOriginalFilename().lastIndexOf("."));
    String fileName = UUID.randomUUID() + suffix;
    String basepath = getClass().getClassLoader().getResource(".").getPath();
    System.out.println(basepath);
    picFile.transferTo(new File(basepath+"../../images/"+fileName));
    course.setPic("/images/"+fileName);
    courseService.update(course);
    return  "/getCourses";
}
注意:实际开发中都是存储到文件服务器,不会放在项目里
4.静态资源处
若web.xml中DispatcherServlet的URLmapping 为/ 则还需要在SpringMVC中添加静态资源配置
<mvc:resources mapping="/images/**" location="/images/"/>
<!--当请求地址为/images/开头时(无论后面有多少层目录),作为静态资源 到/images/下查找文件-->
若URLMapping为*.action 或类似其他的时则无需处理,因为Tomcat会直接查找webapp下的资源,不会交给DispatcherServlet
请求限制
一些情况下我们可能需要对请求进行限制,比如仅允许POST,GET等...
RequestMapping注解中提供了多个参数用于添加请求的限制条件
- value 请求地址
 - path 请求地址
 - method 请求方法
 - headers 请求头中必须包含指定字段
 - params 必须包含某个请求参数
 - consumes 接受的数据媒体类型 (与请求中的contentType匹配才处理)
 - produce 返回的媒体类型 (与请求中的accept匹配才处理)
 
案例:
@RequestMapping(value = "/editCourse",method = RequestMethod.POST,headers = {"id"},params = {"name"},consumes = {"text/plain"})
为了简化书写,MVC还提供了集合路径和方法限制的注解,包括常见的请求方法:
PostMapping
GetMapping
DeleteMapping
PutMapping
例:
@PostMapping("/editCourse")
												
											SpringMVC 参数映射与文件上传的更多相关文章
- SpringMVC:学习笔记(8)——文件上传
		
SpringMVC--文件上传 说明: 文件上传的途径 文件上传主要有两种方式: 1.使用Apache Commons FileUpload元件. 2.利用Servlet3.0及其更高版本的内置支持. ...
 - SpringMVC注解方式与文件上传
		
目录: springmvc的注解方式 文件上传(上传图片,并显示) 一.注解 在类前面加上@Controller 表示该类是一个控制器在方法handleRequest 前面加上 @RequestMap ...
 - SSM框架之SpringMVC(5)文件上传
		
SpringMVC(5)文件上传 1.实现文件上传的前期准备 1.1.文件上传的必要前提 A form 表单的 enctype 取值必须是: multipart/form-data(默认值是:appl ...
 - SpringMVC 通过commons-fileupload实现文件上传
		
目录 配置 web.xml SpringMVC配置文件 applicationContext.xml 文件上传 Controller 上传实现一 上传实现二 测试 依赖 配置 web.xml < ...
 - SpringMVC 使用MultipartFile实现文件上传(转)
		
http://blog.csdn.net/kouwoo/article/details/40507565 一.配置文件:SpringMVC 用的是 的MultipartFile来进行文件上传 所以我们 ...
 - SpringMVC源码分析--文件上传
		
SpringMVC提供了文件上传的功能,接下来我们就简单了解一下SpringMVC文件上传的开发及大致过程. 首先需要在springMVC的配置文件中配置文件上传解析器 <bean id=&qu ...
 - SSM-SpringMVC-32:SpringMVC中灌顶传授文件上传
		
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 我将用自认为最简单的语言,描述Springmvc的文件上传,来将老夫毕生功力灌顶传授给你 首先文件上传,又简至 ...
 - 初学SpringMVC,使用MVC进行文件上传
		
最近在做一个文件上传的功能,走了不少弯路,话不多说,直接上代码: 导入各种jar包,首先是applicationContext.xml配置文件中: <!-- 配置文件解析器 --> < ...
 - SpringMVC札集(08)——文件上传
		
自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onL ...
 
随机推荐
- Vuex入门实践(上)
			
一.前言 vuex被称为是专为vue应用程序开发的的状态管理模式.它的作用使用一句话描述就是:让组件之间可以共享数据 话不多少,先抛开概念,我们写一个简单的示例感受一波. 二.项目开发环境 项目开发环 ...
 - Linux安装MySQL及基本操作(Centos)
			
安装: 系统:CentOS-7-x86_64-DVD-1810.iso 安装命令: wget http://repo.mysql.com/mysql-community-release-el7-5.n ...
 - 2019牛客暑期多校第六场题解ABDJ
			
A.Garbage Classification 传送门 题意:给你两个串,第一个串s由小写字母组成,第二个串t由dwh组成,长度为26,分别表示字母a到z代表的字符.现在要你判断: 如果字符串中‘h ...
 - ArcEngine 创建要素,删除要素,生成网格,渲染图层(VB)
			
示例代码:https://github.com/yu969890202/ArcEngine/tree/master/WinFrom_ArcEngine_PointDistribution博客后面有两张 ...
 - Bonny手机APP试用体验
			
在上周四(即6月13日)下午,应王建民老师的邀请,我参观了学长学姐们的软件设计评比以及专业交流的活动,看到了形形色色学长学姐设计出的软件我觉得非常有趣,并对学长学姐们设计的软件的种类与功能感到由衷的钦 ...
 - 集合下篇—Map和Set 源码分析
			
Map Map不同于Collection集合,Map存放的是键值对,且键不能重复 1 .HashMap (底层是哈希表,Java中用链表的数组实现,存取顺序不一致) 这篇博客主要讲集合的,哈希表这样的 ...
 - npm 安装出现 run `npm audit fix` to fix them, or `npm audit` for details 解决办法
			
1.npm audit fix 2. npm audit fix --force 3.npm audit 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链 ...
 - PTA - 拓扑排序
			
一个项目由若干个任务组成,任务之间有先后依赖顺序.项目经理需要设置一系列里程碑,在每个里程碑节点处检查任务的完成情况,并启动后续的任务.现给定一个项目中各个任务之间的关系,请你计算出这个项目的最早完工 ...
 - Scrapy解析器xpath
			
一.使用xpath 不在scrapy框架中通过response from scrapy.http import HtmlResponse HtmlResponse->TextResponse-& ...
 - python练习-8.12
			
注:本代码是<python核心编程(第二版)>的第八章8-12的练习题的代码实现. 完成的功能:用户给出起始和结束的数字后给出一个下面的表格,分别显示出两个数字间所有整型的十进制.二进制. ...