• springMvc07 类型转换/格式化/数据校验


    前端给后端传递数据进行数据绑定时,会经过类型转换,数据格式化,以及数据校验的过程。

    1、类型转换

    SpringMVC框架提供了一个通用的类型转换模块,该类型转换模块位于包org.springframework.core.convert中,可以在处理方法的参数绑定中使用他们进行数据转换。

    SpringMVC框架中内置了很多的类型转换。

     

     3)自定义类型转换

    默认类型转换器并不是可以将用户提交的内容,转换为用户需要的所有类型。此时 ,就需要自定义类型转换器了

    例如:我们文本框里边要输入2021-01-01之后要让其转换为日期类型,则需要自定义类型转换器。

    自定义类型转换器的方式有3三种:

    1)Converter<S,T>:是SpringMVC最简单的一个转换器接口,可以实现任意类型间的相互转换,负责把S转为T

    2)ConverterFactory<S,R>:将一种类型的对象转换成另一个类型及其子类型对象。其中S为转换的源类型,R为目标类型的基类,T为R的子类。

    3)GenericConverter,转换方法参数中包含TypeDescriptor类型的参数,这个参数包含了上下文信息,能够根据上下文信息进行类型转换。

    举例:

    1)转换器实现

    package rui.tool;
    
    import org.springframework.core.convert.converter.Converter;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class StringToDateConverter implements Converter<String,Date> {
        //日期的格式,定义一个变量,允许配置Bean的时候动态的指定
        private String datePattern;
        public String getDatePattern() {
            return datePattern;
        }
    
        public void setDatePattern(String datePattern) {
            this.datePattern = datePattern;
        }
    
        @Override
        public Date convert(String s) {
            if(s==null || "".equals(s.trim())) {
                return null;
            }
            SimpleDateFormat sdf = new SimpleDateFormat(datePattern);
            Date result = null;
            try {
                result = sdf.parse(s);
            }
            catch (ParseException e)
            {
                e.printStackTrace();
                System.out.println("转换失败");
            }
            return result;
        }
    }

    2)SpringMVC配置

        <!--开启自动注册处理器映射器和处理器适配器-->
        <!--开启Spring的valid功能 validator="validator" -->
        <mvc:annotation-driven validator="validator" conversion-service="formatService" />
    
        <!--配置类型转换和格式化-->
        <bean id="formatService"
              class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
            <property name="converters">
                <list>
                    <bean class="rui.tool.StringToDateConverter">
                        <property name="datePattern" value="yyyy-MM-dd" />
                    </bean>
                </list>
            </property>
        </bean>
    

     

    2、数据格式化

    从Spring3.0开始,引入了格式化转换框架,该框架位于Spring-context.jar包中。

    Spring在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类,该类既具有类型转换功能,有具有格式化功能。

    Formatter和Converter基本功能一样, 是将一种类型转换成另一种类型, 但是,Formatter的源类型必须是一个String, 目标类型是java类型.

    通过FormattingConversionServiceFactoryBean即可以实现注册定义的转换器,也可以注册自定义的格式化器。

    Spring内置了很多的格式化转换器,也允许我们自定义,例如定义了一个坐标类Point,内部包含x,y属性,如果表单上输入2,3之后,能够将其转换为Point对象,则需要自己编写数据格式化器。

    1) 内置的格式化

    名称 功能
    NumberFormatter 实现Number与String之间的解析与格式化
    CurrencyFormatter 实现Number与String之间的解析与格式化(带货币符号)
    PercentFormatter 实现Number与String之间的解析与格式化(带百分数符号)
    DateFormatter 实现Date与String之间的解析与格式化
    NumberFormatAnnotationFormatterFactory @NumberFormat注解,实现Number与String之间的解析与格式化,可以通过指定style来指示要转换的格式(Style.Number/Style.Currency/Style.Percent),当然也可以指定pattern(如pattern=“#.##”(保留2位小数) ),这样pattern指定的格式会覆盖掉Style指定的格式
    JodaDateTimeFormatAnnotationFormatterFactory @DateTimeFormat注解,实现日期类型与String之间的解析与格式化这里的日期类型包括Date、Calendar、Long以及Joda的日期类型。必须在项目中添加Joda-Time包

    2)自定义格式化

    举例:

    1)格式化器实现

    package rui.tool;
    
    import org.springframework.format.Formatter;
    import rui.db.Model.Point;
    
    import java.text.ParseException;
    import java.util.Locale;
    
    public class StringToPointFormatter implements Formatter<Point> {
    
        @Override
        public Point parse(String s, Locale locale) throws ParseException {
            if(s==null || "".equals(s.trim())) {
                return null;
            }
    
            String[] array = s.split(",");
            if(array.length==2)
            {
                Point p = new Point();
                p.setX(Integer.parseInt(array[0]));
                p.setY(Integer.parseInt(array[1]));
                return p;
            }
            return null;
        }
    
        @Override
        public String print(Point point, Locale locale) {
            if(point!= null)
            {
                return  point.getX()+","+point.getY();
            }
            return "";
        }
    }

    2)SpringMvc配置

    类型转换和格式化可以放在一个Bean中配置。

        <!--开启自动注册处理器映射器和处理器适配器-->
        <!--开启Spring的valid功能 validator="validator" -->
        <mvc:annotation-driven validator="validator" conversion-service="formatService" />
    
        <!--配置类型转换和格式化-->
        <bean id="formatService"
              class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
            <property name="formatters">
                <list>
                    <bean class="rui.tool.StringToPointFormatter" />
                </list>
            </property>
        </bean>
    

    3)基于注解的格式化

    @DateTimeFormat可以对Date和Calendar等时间类型的属性进行标准。

    • DateTimeFormat.ISO.DATE: 格式yyyy-MM-dd
    • DateTimeFormat.ISO.DATE_TIME: 格式yyyy-MM-dd HH:mm:ss .SSSZ
    • DateTimeFormat.ISO.TIME: 格式HH:mm:ss .SSSZ
    • DateTimeFormat.ISO.NONE: 表示不使用ISO格式的时间。

    该标准拥有两个属性:

    • pattern。类型为String,使用自定义的时间格式化字符串。
    • style。类型为String,通过样式指定日期时间的格式,由两位字符组成,第1位表示日期的样式,第2位表示时间的格式:
      • S: 短日期/时间的样式;
      • M: 中日期/时间的样式;
      • L: 长日期/时间的样式;
      • F: 完整日期/时间的样式;
      • -: 忽略日期/时间的样式;

    @NumberFormat可对类似数字类型的属性进行标准,它拥有两个互斥的属性。

    • pattern。类型为String,使用自定义的数字格式化字符串,"##,###.##"。
    • style。类型为NumberFormat.Style,常用值:
      • Style.NUMBER正常数字类型
      • Style.PERCENT百分数类型
      • Style.CURRENCY 货币类型

    例子代码:

    // 域对象,实现序列化接口
    public class User implements Serializable{
        
        // 日期类型
        @DateTimeFormat(pattern="yyyy-MM-dd")
        private Date birthday;
        // 正常数字类型
        @NumberFormat(style=Style.NUMBER, pattern="#,###")  
        private int total;  
        // 百分数类型
        @NumberFormat(style=Style.PERCENT)  
        private double discount;  
        // 货币类型
        @NumberFormat(style=Style.CURRENCY)  
        private double money;  
        
    }
    

    填写数据和展示效果:

    3、数据校验

    SpringMVC提供了强大的数据验证功能,其中有两种方法可以验证输入

    1)利用Spring自定的validation校验框架。

    2)利用JSR303(Java验证规范)实现验证功能

    这里极少JSR303的使用方法。

    3.1、导入需要的包

            <!--JSR3.0校验-->
            <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
            <dependency>
                <groupId>org.hibernate.validator</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>7.0.0.Final</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
            <dependency>
                <groupId>javax.validation</groupId>
                <artifactId>validation-api</artifactId>
                <version>2.0.1.Final</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.jboss.logging/jboss-logging -->
            <dependency>
                <groupId>org.jboss.logging</groupId>
                <artifactId>jboss-logging</artifactId>
                <version>3.3.2.Final</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.fasterxml/classmate -->
            <dependency>
                <groupId>com.fasterxml</groupId>
                <artifactId>classmate</artifactId>
                <version>1.5.1</version>
            </dependency>

    3.2、springmvc.xml注册校验器

        <!--开启自动注册处理器映射器和处理器适配器-->
        <!--开启Spring的valid功能 validator="validator" -->
        <mvc:annotation-driven validator="validator" conversion-service="formatService" />
    
        <!--配置验证器-->
        <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
            <property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
        </bean>
    

    3.3、常用验证特性

    注解功能
    @Null 对象必须为null
    @NotNull 对象必须不为null,无法检查长度为0的字符串
    @NotBlank 字符串必须不为Null,且去掉前后空格长度必须大于0
    @AssertTrue 对象必须为true
    @AssertFalse 对象必须为false
    @Max(Value) 必须为数字,且小于或等于Value
    @Min(Value) 必须为数字,且大于或等于Value
    @DecimalMax(Value) 必须为数字( BigDecimal ),且小于或等于Value。小数存在精度
    @DecimalMin(Value) 必须为数字( BigDecimal ),且大于或等于Value。小数存在精度
    @Digits(integer,fraction) 必须为数字( BigDecimal ),integer整数精度,fraction小数精度
    @Size(min,max) 对象(Array、Collection、Map、String)长度必须在给定范围
    @Range(min,max) 验证数字是否介入min和max之间
    @Email 字符串必须是合法邮件地址
    @Past Date和Calendar对象必须在当前时间之前
    @Future Date和Calendar对象必须在当前时间之后
    @Pattern(regexp=“正则”) String对象必须符合正则表达式
    @Url 验证是否合法的Url地址
    @CreditCardNumber 信用卡校验

    3.4、例子

    1)在模型类上增加标准

    2)控制器内判断验证是否有问题

    • 给模型类前边增加@Valid标准,代表User对象绑定时会检查验证规则。
    • BindResult参数必须紧跟在有@Valid参数的后边,用来获取绑定之后的错误信息
    • 如果有验证错误,则重新返回到填写界面,需要把界面需要的数据都携带上。
       //用户注册提交
        @RequestMapping(value = "register", method = RequestMethod.POST)
        public String register(Model model, @Valid User user, BindingResult bindResult,
                               List<MultipartFile> headImgUpload,
                               HttpServletRequest request
        ) {
            System.out.println("执行TestController==2");
            System.out.println(bindResult.toString());
    
            //验证不通过
            if (bindResult.getFieldErrorCount() > 0) {
    
                //获取所有的错误
                List<ObjectError> errList = bindResult.getAllErrors();
                for(ObjectError err:errList)
                {
                    System.out.println(err.toString());
                }
    
                model.addAttribute("user", user);
                model.addAttribute("sexList", rui.tool.listHelper.getSex());
                model.addAttribute("deptList", rui.tool.listHelper.getSex());
                model.addAttribute("loveList", rui.tool.listHelper.getLove());
                return "/test/register";
            }
            
            model.addAttribute("user", user);
            return "forward:/test/showInfo";
        }

    3)前端JSP页面显示错误

    <div>用户账号:<f:input path="loginId" data-id="123" /><f:errors path="loginId"/> </div>
    

    如果是前后端分离的,需要自己获取错误描述信息,并通过JSON返回。

  • 相关阅读:
    网址收藏
    Linux创建swap文件
    vim命令大全
    char * 和字符数组
    JSR 203终于要出来啦
    对象关系技术的探讨
    最近编码更流畅了
    孤独终止的地方,就是广场开始的地方......
    不要奢望.NET能够跨平台
    实现了HTTP多线程下载
  • 原文地址:https://www.cnblogs.com/feihusurfer/p/15887120.html
Copyright © 2020-2023  润新知