SpringMVC数据校验、数据格式化处理、国际化设置

SpringMVC数据校验、数据格式化处理、国际化设置

1.数据验证

(1)使用JSR-303验证框架

JSR(Java Specification Requests),意思是Java 规范提案。JSR-303是JAVA EE 6中的一项子规范,叫做Bean Validation。JSR 303,Bean Validation规范 ,为Bean验证定义了元数据模型和API。默认的元数据模型是通过Annotations来描述的,使用规范定义的这些注解有效的替换了if-else冗长的校验代码。

引入依赖:

•validation-api

•hibernate-validator(附加了一些验证注解)

•jakarta.validation-api(Spring6以上引入此依赖)

JSR-303验证框架常用注解

使用案例:

@Data
public class SysUser {
    /*** 用户ID*/
    @NotNull(message = "用户id不能为空")
    private Long userId;
    /** 用户名*/
    @NotBlank(message = "用户名不能为空")
    @Length(max = 20, message = "用户名不能超过20个字符")
    @Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "用户昵称限制:最多20字符,包含文字、字母和数字")
    private String username;
    /** 手机号*/
    @NotBlank(message = "手机号不能为空")
    @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
    private String mobile;
    /**性别*/
    private String sex;
    /** 邮箱*/
    @NotBlank(message = "联系邮箱不能为空")
    @Email(message = "邮箱格式不对")
    private String email;
    /** 密码*/
    private String password;
    /*** 创建时间 */
    @Future(message = "时间必须是将来时间")
    private Date startTime;
}

在controller层方法参数列表前加上@Valid或@Validated注解

@PostMapping("/save/valid")
@ResponseBody
public Result save(@RequestBody @Valid User user) {
    if(userService.save(user)>0)
        return Result.ok();
    else
        return Result.fail();
}

@Valid@Validated的区别 :

区别 @Valid @Validated
来源 标准JSR-303规范 Spring's JSR-303规范,是标准JSR-303的一个变种。
分组验证 不支持 支持(使用注解的group属性设定)
声明位置 可以用在方法、构造函数、方法参数和成员属性(字段)上,支持嵌套验证。 可以用在类型、方法和方法参数上,但不能用在成员属性(字段)上,从而不支持嵌套验证功能。

@Valid嵌套验证

@Data
public class Food {
    @Valid
    private Drink drink;
    @NotNull
    private String type;
}

@Data
public class Drink {
    @NotNull
    private String name;

    @Size(min = 1,max = 10000)
    private String describ;

    @Digits(integer = 2,fraction = 2)
    private Double price;
}
//controller层
@PostMapping("/food")
public String addFood(@Valid Food food){
    return food.getDrink().getName();
}

数据验证全局异常定义案例:

@Component  
@ResponseBody  
@ControllerAdvice
public class GlobalExceptionHandler {    
    /**方法参数校验(接收参数加上@RequestBody注解才会有这种异常) */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("方法参数校验失败", e);
        return Result.fail(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());
    }
    @ExceptionHandler(BindException.class)
    public Result bindException(BindException ex, HttpServletRequest request) {
        log.error("数据绑定异常", ex);
        try {// 拿到@NotNull,@NotBlank和 @NotEmpty等注解上的message值
            String msg = Objects.requireNonNull(ex.getBindingResult().getFieldError()).getDefaultMessage();
            if (StringUtils.isNotEmpty(msg)) {
           return Result.fail(msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }        
    StringBuilder msg = new StringBuilder(); // 参数类型不匹配检验
        List<FieldError> fieldErrors = ex.getFieldErrors();
        fieldErrors.forEach((oe) ->
                msg.append("参数:[").append(oe.getObjectName()).append(".").append(oe.getField())
                        .append("]的传入值:[").append(oe.getRejectedValue()).append("]与预期的字段类型不匹配.")
        );
        return Result.fail(msg.toString());
    }
    /**ConstraintViolationException */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleConstraintViolationException(ConstraintViolationException e) {
        log.error("注解校验异常", e);
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        String message = violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";"));
        return Result.fail(message);
    }
}

自定义验证注解的步骤案例:

•定义注解接口

@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CardNoValidator.class)
public @interface CardNo {
    String message() default "{edu.cqie.ssm.cardNoErrorMessage}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

•创建ConstraintValidator接口实现类

public class CardNoValidator implements ConstraintValidator<CardNo, Object> {
    @Override
    public void initialize(CardNo constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        if (o == null) {
            return true;//身份证号未传递时,不做校验
    }
        return IdCardValidatorUtils.isValidate18Idcard(o.toString());
    }
}

•使用注解

@Data
public class User {
    /*** 用户ID*/
    @NotNull(message = "用户id不能为空")
    private Long userId;

    /** 用户名*/
    @NotBlank(message = "用户名不能为空")
    @Length(max = 20, message = "用户名不能超过20个字符")
    @Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "用户昵称限制:最多20字符,包含文字、字母和数字")
    private String username;

    @CardNo
    private String cardNo;
}

2.数据格式化

SpringMVC Formatter

•SpringMVC类型转换器提供了一个统一的ConversionService API以及一个强类型的Converter SPI,用于实现从一种类型另一种类型转换逻辑。例如,将Short强制转换为Long。

•在Spring MVC中,HTTP中的源数据都是String类型,数据绑定需要将String转换为其他类型,同时也可能需要将数据转换为具有本地格式的字符串样式进行展示,而Converter SPI不能直接满足这种格式要求。

•Spring 3 引入了一个方便的Formatter SPI,当在客户端环境(如Web应用程序)中工作并且需要解析和输出本地化字段值时,可以使用Formatter SPI。

(1)使用Converter转换
public class StringToDateConverter implements Converter<String, Date> {
    private String pattern;
    public StringToDateConverter(String pattern) {
        this.pattern = pattern;
    }
    @Override
    public Date convert(String source) {
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
            dateFormat.setLenient(false);
            return dateFormat.parse(source);
        } catch (ParseException e) {
            throw new IllegalArgumentException("invalid date format. Please use this pattern\"" + pattern + "\"");
        }
    }
}

注册bean

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="edu.cqie.ssm.converter.StringToDateConverter">
                <constructor-arg name="pattern" value="yyyy-MM-dd"/>
            </bean>
        </set>
    </property>
</bean>

<mvc:annotation-driven conversion-service="conversionService"/>
(2)使用Formatter转换
public class DateFormatter implements Formatter<Date> {
    private SimpleDateFormat sdf;
    public DateFormatter(String pattern) {
        this.sdf = new SimpleDateFormat(pattern);
        sdf.setLenient(false);
    }
    @Override
    public Date parse(String s, Locale locale) throws IllegalArgumentException {
        try {
            return this.sdf.parse(s);
        } catch (ParseException e) {
            throw new IllegalArgumentException("invalid date format. Please use this pattern\"" + this.sdf.toPattern() + "\"");
        }
    }
    @Override
    public String print(Date date, Locale locale) {
        return this.sdf.format(date);
    }
}


<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
        <set>
            <bean class="edu.cqie.ssm.formatter.DateFormatter">
                <constructor-arg name="pattern" value="yyyy-MM-dd"/>
            </bean>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="conversionService"/>
(3)内置Formatter转换器
类型 说明
NumberFormatter 实现 Number 与 String 之间的解析与格式化
CurrencyFormatter 实现 Number 与 String 之间的解析与格式化(带货币符号)
PercentFormatter 实现 Number 与 String 之间的解析与格式化(带百分数符号)
DateFormatter 实现 Date 与 String 之间的解析与格式化

两个格式化注解

•@NumberFormat

•@DateTimeFormat

使用注解格式化

@DateTimeFormat(pattern="yyyy/MM/dd")
private Date birthday;

@NumberFormat(style = NumberFormat.Style.CURRENCY )
private Double balance;//货币金额  ¥5000

@NumberFormat(pattern = "#,###.##")
private Double salary; //工资  10,000.00

@NumberFormat(style = NumberFormat.Style.PERCENT) 
private Double percent;//不加%按p*100来显示,加上按提交精度来显示

3.国际化

什么是国际化?

国际化(也叫 i18n),由于国际化英文单词是 internationalization,在 i 和 n 之间有 18 个字母,因此国际化又叫做 i18n。国际化是指程序在不做任何修改的情况下,就可以在不同的国家或地区和不同的语言环境下,按照当地的语言和格式习惯的显示字符,例如MyBatis官方网站等。

国际化设置场景:

•Spring标签国际化

•接口方法国际化

LocaleResolver

•AcceptHeaderLocaleResolver:根据请求头中的 Accept-Language 字段来确定当前的区域语言。

•SessionLocaleResolver:根据请求参数来确定区域语言,确定后会保存在 Session 中,只要 Session不变,Locale 对象就一直有效。

•CookieLocaleResolver:根据请求参数来确定区域语言,确定后会保存在Cookie中,只要Cookie不变Locale对象就一直有效。

•FixedLocaleResolver:配置时直接提供一个 Locale 对象,以后不能修改。


(1)AcceptHeaderLocaleResolver

步骤一:添加多语言配置文件


步骤二:修改****spring-mvc.xml

<bean id="messageSource"
          class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n.message"/>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>

步骤三:在页面中引用

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
    <title><spring:message code="login.title"/></title>
</head>
<body>
    <form action="toRegister" method="post">
        <div><spring:message code="login.content.title"/></div>
        <div>
            <span><spring:message code="login.account"/></span>
            <input type="tel" name="phone" maxlength="11" placeholder='<spring:message code="login.account.holder"/>'/>
        </div>
        <div>
            <span><spring:message code="login.password"/></span>
            <input type="password" name="pwd" minlength="8" maxlength="20" placeholder='<spring:message code="login.password.holder"/>'/>
        </div>
        <div>
            <input type="submit" value='<spring:message code="login.submit"/>'/>
        </div>
    </form>
</body>
</html>

步骤四:在接口中引用

@Controller
public class LoginController {
    @Autowired
    MessageSource messageSource;
    @GetMapping("/login")
    public String login() {
        String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale());
        String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale());
        System.out.println("username = " + username);
        System.out.println("password = " + password);
        return "login";
    }
}
(2)SessionLocaleResolver

**步骤一:添加多语言配置文件(与上面一样)

步骤二:修改****spring-mvc.xml

<bean id="messageSource"
          class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n.message"/>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <property name="paramName" value="locale"/>
        </bean>
    </mvc:interceptor>
</mvc:interceptors>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
</bean>

步骤三:在接口中使用

@Controller
public class LoginController {
    @Autowired
    MessageSource messageSource;
    @GetMapping("/login")
    public String login(String locale,HttpSession session) {
        if ("zh-CN".equals(locale)) {
            session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, new Locale("zh", "CN"));
        } else if ("en-US".equals(locale)) {
            session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, new Locale("en", "US"));
        }
        String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale());
        String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale());
        System.out.println("username = " + username);
        System.out.println("password = " + password);
        return "login";
    }
}
(3)CookieLocaleResolver

**步骤一:添加多语言配置文件(与上面一样)

步骤二:修改****spring-mvc.xml

<bean id="messageSource"
   class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n.message"/>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>

步骤三:在接口中使用

@GetMapping("/login")
public String login(String locale, HttpServletRequest req, HttpServletResponse resp) {
    CookieLocaleResolver resolver = new CookieLocaleResolver();
    if ("zh-CN".equals(locale)) {
        resolver.setLocale(req, resp, new Locale("zh", "CN"));
    } else if ("en-US".equals(locale)) {
        resolver.setLocale(req, resp, new Locale("en", "US"));
    }
    String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale());
    String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale());
    System.out.println("username = " + username);
    System.out.println("password = " + password);
    return "login";
}


    resolver.setLocale(req, resp, new Locale("zh", "CN"));
} else if ("en-US".equals(locale)) {
    resolver.setLocale(req, resp, new Locale("en", "US"));
}
String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale());
String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale());
System.out.println("username = " + username);
System.out.println("password = " + password);
return "login";

}

复制代码
相关推荐
Swift社区3 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht3 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht3 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20243 小时前
Swift 数组
开发语言
stm 学习ing4 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc5 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe6 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin6 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python