zoukankan      html  css  js  c++  java
  • 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=" 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")
    
  • 相关阅读:
    (一)Python入门-2编程基本概念:18字符串-驻留机制-内存分析-字符串同一判断-值相等判断
    (一)Python入门-2编程基本概念:07内置数据类型-基本算数运算符
    (一)Python入门-2编程基本概念:08整数-不同进制-其他类型转换成整数
    (一)Python入门-2编程基本概念:09浮点数-自动转换-强制转换-增强赋值运算符
    (一)Python入门-2编程基本概念:10时间表示-unix时间点-毫秒和微妙-time模块
    (一)Python入门-2编程基本概念:11布尔值-比较运算符-逻辑运算符及短路问题
    (一)Python入门-2编程基本概念:12同一运算符-整数缓存问题
    (一)Python入门-2编程基本概念:01程序的构成
    (一)Python入门-2编程基本概念:02对象的基本组成和内存示意图
    有关位运算的基础知识总结
  • 原文地址:https://www.cnblogs.com/yangyuanhu/p/12297329.html
Copyright © 2011-2022 走看看