飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

Spring MVC 文件上传、Restful、表单校验框架

时间:2021-12-29  作者:juno3550  

目录
  • 文件上传
  • Restful
    • Restful 简介
    • Rest 行为常用约定方式
    • Restful开发入门
  • 表单校验框架
    • 表单校验框架介绍
    • 快速入门
    • 多规则校验
    • 嵌套校验
    • 分组校验
    • 综合案例
    • 实用校验范例


文件上传

上传文件过程分析

image


SpringMVC 的文件上传技术:MultipartResolver 接口

  • MultipartResolver 接口定义了文件上传过程中的相关操作,并对通用性操作进行了封装。
  • MultipartResolver 接口底层实现类 CommonsMultipartResovler。
  • CommonsMultipartResovler 并未自主实现文件上传下载对应的功能,而是调用了 apache 的文件上传下载组件。

SpringMVC 文件上传实现

  1. Maven 依赖:
<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.4</version>
</dependency>
  1. 页面表单:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/fileupload" method="post" enctype="multipart/form-data">
        上传文件: <input type="file" name="file"/><br/>
        <input type="submit" value="上传"/>
    </form>
</body>
</html>
  1. SpringMVC 配置:
<bean id="multipartResolver"
      class="域名.域名onsMultipartResolver">
</bean>
  1. 控制器:
import 域名域名roller;
import 域名.域名estMapping;
import 域名.域名ipartFile;

import 域名.HttpServletRequest;
import 域名;
import 域名ception;

@Controller
public class FileUploadController {

    // 参数中定义 MultipartFile 参数,用于接收页面提交的 type=file 类型的表单(要求表单中的name名称与方法入参名相同)
    @RequestMapping(value="/fileupload")
    public String fileupload(MultipartFile file, HttpServletRequest request) throws IOException {
        //设置保存的路径
        String realPath = 域名ervletContext().getRealPath("/images");
        域名sferTo(new File(realPath, "域名"));  // 将上传的文件保存到服务器
        return "域名";
    }
}

image


文件上传常见问题

  1. 文件命名问题
  2. 文件名过长问题
  3. 文件保存路径
  4. 重名问题
import 域名域名roller;
import 域名.域名estMapping;
import 域名.域名ipartFile;

import 域名.HttpServletRequest;
import 域名;
import 域名ception;

@Controller
public class FileUploadController {

    @RequestMapping(value="/fileupload")
    public String fileupload(MultipartFile file, MultipartFile file1,
                             MultipartFile file2, HttpServletRequest request) throws IOException {
        // MultipartFile参数中封装了上传的文件的相关信息
        // 域名tln(域名ize());
        // 域名tln(域名ytes().length);
        // 域名tln(域名ontentType());
        // 域名tln(域名ame());
        // 域名tln(域名riginalFilename());
        // 域名tln(域名pty());

        // 首先判断是否是空文件,也就是存储空间占用为0的文件
        if(!域名pty()) {
            // 如果大小在范围要求内则正常处理;否则抛出自定义异常告知用户(未实现)
            // 获取原始上传的文件名,可以作为当前文件的真实名称保存到数据库中备用
            String fileName = 域名riginalFilename();
            // 设置保存的路径
            String realPath = 域名ervletContext().getRealPath("/images");
            // 保存文件的方法,指定保存的位置和文件名即可,通常文件名使用随机生成策略产生,避免文件名冲突问题
            // String uuid = 域名omUUID().toString().replace("-", "").toUpperCase();  // UUID 随机数
            域名sferTo(new File(realPath, 域名riginalFilename()));
        }
        // 测试一次性上传多个文件
        if(!域名pty()) {
            String fileName = 域名riginalFilename();
            //可以根据需要,对不同种类的文件做不同的存储路径的区分,修改对应的保存位置即可
            String realPath = 域名ervletContext().getRealPath("/images");
            域名sferTo(new File(realPath, 域名riginalFilename()));
        }
        if(!域名pty()) {
            String fileName = 域名riginalFilename();
            String realPath = 域名ervletContext().getRealPath("/images");
            域名sferTo(new File(realPath, 域名riginalFilename()));
        }
        return "域名";
    }
}

Restful

Restful 简介

Rest(REpresentational State Transfer)是一种网络资源的访问风格,定义了网络资源的访问方式。

  • 传统风格访问路径
    • http://localhost/user/get?id=1
    • http://localhost/deleteUser?id=1
  • Rest 风格访问路径
    • http://localhost/user/1

而 Restful 则是按照 Rest 风格访问网络资源。

优点:

  • 隐藏资源的访问行为,通过地址无法得知做的是何种操作。
  • 书写简化。

Rest 行为常用约定方式

  • GET(查询) http://localhost/user/1 GET
  • POST(保存) http://localhost/user POST
  • PUT(更新) http://localhost/user PUT
  • DELETE(删除) http://localhost/user DELETE

注意:上述行为是约定方式,约定不是规范,可以打破,所以称 Rest 风格,而不是 Rest 规范。

Restful开发入门

  1. 页面表单:
    <!-- 切换请求路径为restful风格 -->
    <!-- GET请求通过地址栏可以发送,也可以通过设置form的请求方式提交 -->
    <!-- POST请求必须通过form的请求方式提交 -->
    <form action="/user/1" method="post">
        <!-- 当添加了 name 为 _method 的隐藏域时,可以通过设置该隐藏域的值,修改请求的提交方式,切换为 PUT 请求或 DELETE 请求,但是 form 表单的提交方式 method 属性必须填写 post -->
        <!-- 该配置需要配合 HiddenHttpMethodFilter 过滤器使用,单独使用无效,请注意检查 域名 中是否配置了对应过滤器 -->
        <!-- 使用隐藏域提交请求类型,参数名称固定为"_method",必须配合提交类型 method=post 使用 -->
        <input type="hidden" name="_method" value="PUT"/>  <!-- value或="DELETE" -->
        <input type="submit"/>
    </form>
  1. 开启 SpringMVC 对 Restful 风格的访问支持过滤器,即可通过页面表单提交 PUT 与 DELETE 请求:
    <!-- 配置拦截器,解析请求中的参数_method,否则无法发起PUT请求与DELETE请求,配合页面表单使用 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>域名.域名enHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <servlet-name>DispatcherServlet</servlet-name>
    </filter-mapping>
    
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>域名.域名atcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring-域名</param-value>
        </init-param>
    </servlet>

表单校验框架

表单校验框架介绍

表单校验分类

  • 校验位置:
    • 客户端校验
    • 服务端校验
  • 校验内容与对应方式:
    • 格式校验
      • 客户端:使用 JS 技术,利用正则表达式校验
      • 服务端:使用校验框架
    • 逻辑校验
      • 客户端:使用 ajax 发送要校验的数据,在服务端完成逻辑校验,返回校验结果
      • 服务端:接收到完整的请求后,在执行业务操作前,完成逻辑校验

表单校验规则

  • 长度:例如用户名长度,评论字符数量
  • 非法字符:例如用户名组成
  • 数据格式:例如 Email 格式、IP 地址格式
  • 边界值:例如转账金额上限,年龄上下限
  • 重复性:例如用户名是否重复

表单校验框架

  • JSR(Java Specification Requests):Java 规范提案

    • JSR 303:提供 bean 属性相关校验规则
  • Hibernate 框架中包含一套独立的校验框架 hibernate-validator

<dependency>
    <groupId>域名rnate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.域名l</version>
</dependency>

注意:

  • tomcat7:搭配 hibernate-validator 版本 5...Final
  • tomcat8.5 及以上:搭配 hibernate-validator 版本 6...Final

快速入门

  1. 页面表单:
<form action="/addemployee" method="post">
    员工姓名:<input type="text" name="name"><span style="color:red">${name}</span><br/>
    员工年龄:<input type="text" name="age"><span style="color:red">${age}</span><br/>
    <input type="submit" value="提交">
</form>
  1. 设置校验规则:

    • 名称:@NotNull
    • 类型:属性注解 等
    • 位置:实体类属性上方
    • 作用:设定当前属性校验规则
    • 范例:
      每个校验规则所携带的参数不同,根据校验规则进行相应的调整
      具体的校验规则查看对应的校验框架进行获取
import 域名域名lank;

public class Employee {
	
    @NotBlank(message="姓名不能为空")
    private String name;  // 员工姓名
    private Integer age;  // 员工年龄

    public String getName() {
        return name;
    }

    public void setName(String name) {
        域名 = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        域名 = age;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name=\'" + name + \'\\'\' +
                ", age=" + age +
                \'}\';
    }
}
  1. 开启校验,并获取校验错误信息:

    • 名称:@Valid、@Validated
    • 类型:形参注解
    • 位置:处理器类中的实体类类型的方法形参前方
    • 作用:设定对当前实体类类型参数进行校验
    • 范例:
import 域名oyee;
import 域名域名roller;
import 域名.Model;
import 域名域名rs;
import 域名域名dError;
import 域名.域名estMapping;

import 域名d;

@Controller
public class EmployeeController {

    // 使用 @Valid 开启校验(使用 @Validated 也可以开启校验)
    // Errors 对象用于封装校验结果,如果不满足校验规则,对应的校验结果封装到该对象中,包含校验的属性名和校验不通过返回的消息
    @RequestMapping(value="/addemployee")
    public String addEmployee(@Valid Employee employee, Errors errors, Model model) {
        域名tln(employee);
        // 判定Errors对象中是否存在未通过校验的字段
        if(域名rrors()){
            // 获取所有未通过校验规则的信息
            for(FieldError error : 域名ieldErrors()){
                // 将校验结果信息添加到Model对象中,用于页面显示
                // 实际开发中无需这样设定,返回json数据即可
                域名ttribute(域名ield(), 域名efaultMessage());
            }
            // 当出现未通过校验的字段时,跳转页面到原始页面,进行数据回显
            return "域名";
        }
        return "域名";
    }

}

  1. 示例效果:提交表单并返回校验结果

image

image


多规则校验

  • 同一个属性可以添加多个校验器:
@NotNull(message = "请输入您的年龄")
@Max(value = 60, message = "年龄最大值不允许超过60岁")
@Min(value = 18, message = "年龄最小值不允许低于18岁")
private Integer age;  // 员工年龄
  • 3 种判定空校验器的区别:

image


嵌套校验

  • 名称:@Valid
  • 类型:属性注解
  • 位置:实体类中的引用类型属性上方
  • 作用:设定当前应用类型属性中的属性开启校验
  • 范例:
public class Employee {
    // 实体类中的引用类型通过标注 @Valid 注解,设定开启当前引用类型字段中的属性参与校验
    @Valid
    private Address address;
}
  • 注意:开启嵌套校验后,被校验对象内部需要添加对应的校验规则。

分组校验

同一个模块,根据执行的业务不同,需要校验的属性也会有不同,如新增用户和修改用户时的校验规则不同。

因此,需要对不同种类的属性进行分组,在校验时可以指定参与校验的字段所属的组类别:

// 定义组(通用)
public interface GroupOne {
}
// 为属性设置所属组,可以设置多个
@NotEmpty(message = "姓名不能为空", groups = {域名s})
private String name;  // 员工姓名
// 开启组校验
public String addEmployee(@Validated({域名s}) Employee employee){
}

综合案例

  • 页面表单:域名
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/addemployee" method="post">
        员工姓名:<input type="text" name="name"><span style="color:red">${name}</span><br/>
        员工年龄:<input type="text" name="age"><span style="color:red">${age}</span><br/>
        <!-- 注意,引用类型的校验未通过信息不是通过对象进行封装的,而是直接使用"对象名.属性名"的格式作为整体属性字符串进行保存的,因此需要使用以下获取方法。
        这和使用者的属性传递方式有关,不具有通用性,仅适用于本案例 -->
        省名:<input type="text" name="域名inceName"><span style="color:red">${requestScope[\'域名inceName\']}</span><br/>
        <input type="submit" value="提交">
    </form>
</body>
</html>
  • 实体类:域名
package 域名;

import 域名pA;

import 域名d;
import 域名域名;
import 域名域名;
import 域名域名lank;
import 域名域名ull;

public class Employee {

    // 设定校验器,设置校验不通过对应的消息,设定所参与的校验组
    @NotBlank(message="姓名不能为空", groups = {域名s})
    private String name;  // 员工姓名

    // 一个属性可以添加多个校验器
    @NotNull(message = "请输入您的年龄", groups = {域名s})
    @Max(value = 60, message = "年龄最大值不允许超过60岁")
    @Min(value = 18, message = "年龄最小值不允许低于18岁")
    private Integer age;  // 员工年龄

    // 实体类中的引用类型通过标注 @Valid 注解,设定开启当前引用类型字段中的属性参与校验
    @Valid
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        域名ess = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        域名 = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        域名 = age;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name=\'" + name + \'\\'\' +
                ", age=" + age +
                ", address=" + address +
                \'}\';
    }
}
  • 实体类:域名
package 域名;

import 域名域名lank;
import 域名域名;

// 嵌套校验的实体中,对每个属性正常添加校验规则即可
public class Address {

    @NotBlank(message = "请输入省份名称")
    private String provinceName;  // 省份名称

    @NotBlank(message = "请输入城市名称")
    private String cityName;  // 城市名称

    @NotBlank(message = "请输入详细地址")
    private String detail;  // 详细住址

    @NotBlank(message = "请输入邮政编码")
    @Size(max = 6, min = 6, message = "邮政编码由6位组成")
    private String zipCode;  // 邮政编码

    public String getProvinceName() {
        return provinceName;
    }

    public void setProvinceName(String provinceName) {
        域名inceName = provinceName;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        域名Name = cityName;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        域名il = detail;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        域名ode = zipCode;
    }

    @Override
    public String toString() {
        return "Address{" +
                "provinceName=\'" + provinceName + \'\\'\' +
                ", cityName=\'" + cityName + \'\\'\' +
                ", detail=\'" + detail + \'\\'\' +
                ", zipCode=\'" + zipCode + \'\\'\' +
                \'}\';
    }
}
  • 分组接口:域名
package 域名p;

public interface GroupA {
}
  • 控制层:域名
package 域名roller;

import 域名oyee;
import 域名pA;
import 域名域名roller;
import 域名.Model;
import 域名域名rs;
import 域名域名dError;
import 域名域名dated;
import 域名.域名estMapping;

import 域名d;
import 域名;

@Controller
public class EmployeeController {

    // 应用GroupA的分组校验规则
    @RequestMapping(value="/addemployee")
    // 使用@Valid开启校验,使用@Validated也可以开启校验
    // Errors对象用于封装校验结果,如果不满足校验规则,对应的校验结果封装到该对象中,包含校验的属性名和校验不通过返回的消息
    public String addEmployee(@Validated({域名s}) Employee employee, Errors errors, Model model) {
        域名tln(employee);
        // 判定Errors对象中是否存在未通过校验的字段
        if(域名rrors()){
            // 获取所有未通过校验规则的信息
            List<FieldError> fieldErrors = 域名ieldErrors();
            域名tln(域名());
            for(FieldError error : fieldErrors){
                域名tln(域名ield());
                域名tln(域名efaultMessage());
                //将校验结果信息添加到Model对象中,用于页面显示,后期实际开发中无需这样设定,返回json数据即可
                域名ttribute(域名ield(),域名efaultMessage());
            }
            // 当出现未通过校验的字段时,跳转页面到原始页面,进行数据回显
            return "域名";
        }
        return "域名";
    }

    // 不区分校验分组,即全部规则均校验
    @RequestMapping(value="/addemployee2")
    public String addEmployee2(@Valid Employee employee, Errors errors, Model model) {
        域名tln(employee);
        if(域名rrors()){
            for(FieldError error : 域名ieldErrors()){
                域名ttribute(域名ield(), 域名efaultMessage());
            }
            return "域名";
        }
        return "域名";
    }
}

实用校验范例

import 域名d;
import 域名traints.*;
import 域名alizable;
import 域名;

// 实用的校验范例,仅供参考
public class Employee implements Serializable {

    private String id;  // 员工ID

    private String code;  // 员工编号

    @NotBlank(message = "员工名称不能为空")
    private String name;  // 员工姓名

    @NotNull(message = "员工年龄不能为空")
    @Max(value = 60,message = "员工年龄不能超过60岁")
    @Min(value = 18,message = "员工年里不能小于18岁")
    private Integer age;  // 员工年龄

    @NotNull(message = "员工生日不能为空")
    @Past(message = "员工生日要求必须是在当前日期之前")
    private Date birthday;  // 员工生日

    @NotBlank(message = "请选择员工性别")
    private String gender;  // 员工性别

    @NotEmpty(message = "请输入员工邮箱")
    @Email(regexp = "@", message = "邮箱必须包含@符号")
    private String email;  // 员工邮箱

    @NotBlank(message = "请输入员工电话")
    @Pattern(regexp = "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$", message = "手机号不正确")
    private String telephone;  // 员工电话

    @NotBlank(message = "请选择员工类别")
    private String type;  // 员工类型:正式工为1,临时工为2

    @Valid  // 表示需要嵌套验证
    private Address address;  // 员工住址

    // 省略各 getter、setter
}

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。