参考资料

  1. 参考: 妥当性チェックのエラーメッセージ出力方法 (需翻墙)
  2. springMVC之@InitBinder的用法1
  3. springMVC之@InitBinder的用法2
  4. springMVC之@InitBinder 和 Validator
  5. Spring MVCにおけるフォームバリデーションの適用事例【後編】

目录

  • 一. 前期准备
    • 1.1 自定义校验注解
    • 1.2 国际化资源文件
    • 1.3 application配置文件
    • 1.4 国际化配置文件
    • 1.5 待校验Bean
  • 二. 实现Validator接口
  • 三. @InitBinder校验Get请求
    • 3.1 前端
    • 3.2 controller层
    • 3.3 全局捕获BindException异常
    • 3.4 效果
  • 四. @InitBinder校验Post请求
    • 4.1 前端
    • 4.2 controller层
    • 4.3 全局捕获MethodArgumentNotValidException异常
    • 4.4 效果
  • 五. 注意事项

一. 前期准备

1.1 自定义校验注解

import javax.validation.Constraint;
import javax.validation.OverridesAttribute;
import javax.validation.Payload;
import javax.validation.constraints.Size;
import javax.validation.ReportAsSingleViolation;
import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@Documented
@Constraint(validatedBy = {})
@ReportAsSingleViolation
@Size
public @interface ValidateSize {String msgArgs() default "";String message() default "{1006E}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};// 覆盖重写@Size注解中的属性@OverridesAttribute(constraint = Size.class, name = "min")int min() default 0;@OverridesAttribute(constraint = Size.class, name = "max")int max() default Integer.MAX_VALUE;
}
import javax.validation.Constraint;
import javax.validation.constraints.NotEmpty;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import java.lang.annotation.*;@Documented
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
@NotEmpty
@ReportAsSingleViolation
public @interface ValidateNotEmpty {String msgArgs() default "";String message() default "{1001E}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}

1.2 国际化资源文件

⏹messages_zh.properties

1001E=请输入{msgArgs}。
1007E={0}和{1}的大小关系不正确。

⏹messages_ja.properties

1001E={msgArgs}を入力してください。
1007E={0}と{1}の大小関係が逆らいました。

⏹置于i18n文件夹下

1.3 application配置文件

spring:messages:# 指定国际化文件所在目录和文件前缀basename: i18n/messagesencoding: UTF-8

1.4 国际化配置文件

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;import javax.annotation.Resource;
import java.util.Locale;@Configuration
public class InternationalConfig implements WebMvcConfigurer {// 默认解析器,用来设置当前会话默认的国际化语言@Beanpublic LocaleResolver localeResolver() {SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();// 指定当前项目的默认语言是中文sessionLocaleResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);return sessionLocaleResolver;}// 默认拦截器,用来指定切换国际化语言的参数名@Beanpublic LocaleChangeInterceptor localeChangeInterceptor() {LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();/*设置国际化请求参数为language设置完成之后,URL中的 ?language=zh 表示读取国际化文件messages_zh.properties*/localeChangeInterceptor.setParamName("language");return localeChangeInterceptor;}// 将我们自定义的国际化语言参数拦截器放入Spring MVC的默认配置中@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(localeChangeInterceptor());}
}

1.5 待校验Bean

import lombok.Data;import javax.validation.groups.Default;@Data
public class Test4Entity {@ValidateNotEmpty(msgArgs = "ID项目", groups = {Default.class})private String id;@ValidateSize(msgArgs = "地址项目", max = 6, groups = {Default.class})private String address;@ValidateSize(msgArgs = "兴趣项目", max = 5, groups = {Default.class})private String hobby;
}
import lombok.Data;import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;@Data
public class Test16Form {@ValidateNotEmpty(msgArgs = "姓名")private String name;private Date birthday;private BigDecimal money;private Integer fromNumber;private Integer toNumber;// 校验List集合@Validprivate List<Test4Entity> tableList;
}

二. 实现Validator接口

import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;import java.util.HashMap;
import java.util.Locale;
import java.util.Map;@Component
public class FromToValidator implements Validator {@Overridepublic boolean supports(Class<?> clazz) {// 只支持指定Bean类型的校验return Test16Form.class.equals(clazz);}@Overridepublic void validate(Object target, Errors errors) {Test16Form form = (Test16Form) target;// 获取from和to的数字Integer fromNumber = form.getFromNumber();Integer toNumber = form.getToNumber();// 有任何一方为空,就不行校验if (ObjectUtils.isEmpty(fromNumber) || ObjectUtils.isEmpty(toNumber)) {return;}// 模拟从缓存或者session或者数据库中获取国际化消息Map<String, Object[]> languageErrorParamMap = new HashMap<String, Object[]>() {{put("zh", new Object[] { "开始数字", "结束数字" });put("ja", new Object[] { "スタートの数字", "エンドの数字" });}};// 获取当前设置地区的语言Locale locale = LocaleContextHolder.getLocale();String language = locale.getLanguage();Object[] errorParam = languageErrorParamMap.get(language);// 当from数字 大于 to数字的时候,进行业务校验if (fromNumber > toNumber) {/*参数1: bean中被校验住的属性名参数2: 国际化资源文件中的key参数3: error消息的参数参数4: 默认消息*/errors.rejectValue("fromNumber", "1007E", errorParam, "");}}
}

三. @InitBinder校验Get请求

3.1 前端

⏹test16.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<div><button id="getBtn">发送get请求</button>
</body>
<script type="text/javascript" th:src="@{/js/public/jquery-3.6.0.min.js}"></script>
<script>let languageFlag = false;$("#getBtn").click(function() {languageFlag = !languageFlag;const urlSearchParams = new URLSearchParams();urlSearchParams.append("money", "10000");urlSearchParams.append("fromNumber", "20");urlSearchParams.append("toNumber", "10");urlSearchParams.append("language", languageFlag ? "zh" : "ja");const url = `/test16/receiveGet?${urlSearchParams.toString()}`;$.ajax({url,type: 'GET',success: function (data, status, xhr) {console.log("请求成功");console.log(data);},error: function (xhr, status, error) {console.warn("请求失败");// 获取后台全局异常捕获中返回的json响应const errorJson = xhr.responseJSON;console.log(errorJson);}});});
</script>
</html>

3.2 controller层

@Controller
@RequestMapping("/test16")
public class Test16Controller {// 注入我们自定义的校验器@Resourceprivate FromToValidator fromToValidator;@InitBinderpublic void initBinder(WebDataBinder binder) {// 去除字符串前后的空格binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));// 使用我们自定义的校验器binder.addValidators(fromToValidator);}@GetMapping("/init")public ModelAndView init() {ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName("test16");return modelAndView;}// 校验@GetMapping("/receiveGet")@ResponseBodypublic void receiveGet(@Validated Test16Form form) {System.out.println(form);}
}

3.3 全局捕获BindException异常

  • Get请求被被校验住之后,会抛出BindException异常
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@ControllerAdvice
public class GlobalExceptionHandler {@Resourceprivate MessageSource messageSource;@ExceptionHandler(BindException.class)// 通过注解指定了响应的状态码,前台$.ajax会在error函数的xhr响应中接收错误json@ResponseStatus(HttpStatus.BAD_REQUEST)@ResponseBodypublic List<Map<String, String>> BindExceptionHandle(BindException errors) {// 存放所有error信息的ListList<Map<String, String>> errorList = new ArrayList<>();for(FieldError err : errors.getFieldErrors()){// 根据当前的FieldError对象从国际化资源文件中获取信息String msg = this.messageSource.getMessage(err, LocaleContextHolder.getLocale());// 封装错误信息Map<String, String> errorMap = new HashMap<String, String>() {{put("field", err.getField());put("msg", msg);}};errorList.add(errorMap);}return errorList;}
}

3.4 效果

四. @InitBinder校验Post请求

4.1 前端

⏹test16.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<div><button id="postBtn">发送post请求</button><br>
</div>
</body>
<script type="text/javascript" th:src="@{/js/public/jquery-3.6.0.min.js}"></script>
<script>let languageFlag = false;$("#postBtn").click(function() {languageFlag = !languageFlag;const urlSearchParams = new URLSearchParams();urlSearchParams.append("language", languageFlag ? "zh" : "ja");// 待校验的list对象const tableList = [{id: null,address: '测试address123',hobby: '测试hobby123'},{id: 110,address: '测试',hobby: '测试AAAAAAAAAA'},{id: 120}];// 待校验的bean对象const paramObj = {money: "10000",fromNumber: "20",toNumber: "10",tableList};$.ajax({url: `/test16/receivePost?${urlSearchParams.toString()}`,type: 'POST',data: JSON.stringify(paramObj),// 指定向后台提交json数据contentType : 'application/json;charset=utf-8',// 指定后台返回json数据给前台dataType: 'json',success: function (data, status, xhr) {console.log("请求成功");console.log(data);},error: function (xhr, status, error) {console.warn("请求失败");const errorJson = xhr.responseJSON;console.log(errorJson);}});});</script>
</html>

4.2 controller层

@Controller
@RequestMapping("/test16")
public class Test16Controller {// 注入我们自定义的校验器@Resourceprivate FromToValidator fromToValidator;@InitBinderpublic void initBinder(WebDataBinder binder) {// 去除字符串前后的空格binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));// 使用我们自定义的校验器binder.addValidators(fromToValidator);}@GetMapping("/init")public ModelAndView init() {ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName("test16");return modelAndView;}// 校验@PostMapping("/receivePost")@ResponseBodypublic void receivePost(@RequestBody @Validated Test16Form form) {System.out.println(form);}
}

4.3 全局捕获MethodArgumentNotValidException异常

  • Post请求被被校验住之后,会抛出MethodArgumentNotValidException异常
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@ControllerAdvice
public class GlobalExceptionHandler {@Resourceprivate HttpServletResponse response;@Resourceprivate MessageSource messageSource;@ExceptionHandler(MethodArgumentNotValidException.class)@ResponseBodypublic List<Map<String, String>> HandleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {// 存放所有error信息的ListList<Map<String, String>> errorList = new ArrayList<>();List<FieldError> errors = ex.getFieldErrors();for(FieldError err : errors){// 根据当前的FieldError对象从国际化资源文件中获取信息String msg = this.messageSource.getMessage(err, LocaleContextHolder.getLocale());Map<String, String> errorMap = new HashMap<String, String>() {{put("field", err.getField());put("msg", msg);}};errorList.add(errorMap);}// 通过response对象指定了响应的状态码,前台$.ajax会在error函数的xhr响应中接收错误jsonresponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);return errorList;
}

4.4 效果

五. 注意事项

当前端传入的数据无法通过校验规则的时候,会抛出相应的异常。
我们可通过FieldError对象getMessage方法中获取出相应的错误信息

String msg = this.messageSource.getMessage(err, LocaleContextHolder.getLocale());

错误消息是根据FieldError的code,从国际化资源文件中获取,通过code获取错误消息需要遵循如下的优先顺规则

  1. errorCode.对象名.属性名
  2. errorCode.属性名
  3. errorCode.类型
  4. errorCode

1为最优先,4的优先顺最低


也就是说,如果国际化资源文件中有如下errorCode的话,会显示优先顺最高的

1007E={0}和{1}的大小关系不正确。
1007E.test16Form.fromNumber=我是测试内容,我的优先顺最高

SpringBoot @InitBinder注解实现Bean国际化校验相关推荐

  1. SpringBoot - @InitBinder注解详解

    写在前面 @InitBinder注解可以作用在被@Controller注解的类的方法上,表示为当前控制器注册一个属性编辑器,用于对WebDataBinder进行初始化,且只对当前的Controller ...

  2. SpringBoot @InitBinder注解绑定请求参数

    参考资料 springMVC之@InitBinder 和 Validator springMVC之@InitBinder的用法1 springMVC之@InitBinder的用法2 目录 一. 作用 ...

  3. java分组校验_SpringBoot @Validated注解实现参数分组校验的方法实例

    前言 在前后端分离开发的时候我们需要用到参数校验,前端需要进行参数校验,后端接口同样的也需要,以防传入不合法的数据. 1.首先还是先导包,导入pom文件. org.springframework.bo ...

  4. Springboot + redis + 注解 + 拦截器来实现接口幂等性校验

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:wangzaiplus www.jianshu.com/p/ ...

  5. springboot + redis + 注解 + 拦截器 实现接口幂等性校验

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 来源:https://www.jianshu.com/p/6189275403ed 一.概念 ...

  6. Springboot自定义注解实现用户登录状态校验(一)

    Springboot自定义注解实现用户登录状态校验(一) 拦截器方式 定义注解类 import java.lang.annotation.*;/*** @author:小飞猪* @date:2020/ ...

  7. SpringBoot @Validated注解实现参数校验

    1. 前言 做web开发有一点很烦人就是要校验参数,基本上每个接口都要对参数进行校验,比如一些格式校验 非空校验都是必不可少的.如果参数比较少的话还是容易 处理的一但参数比较多了的话代码中就会出现大量 ...

  8. java懒加载注解_在springboot中实现个别bean懒加载的操作

    懒加载---就是我们在spring容器启动的是先不把所有的bean都加载到spring的容器中去,而是在当需要用的时候,才把这个对象实例化到容器中. @Lazy 在需要懒加载的bean上加上@Lazy ...

  9. springboot validated注解数据校验 异常处理

    springboot validated 数据校验 validated 数据校验 简单的写一下这个用法啊,清晰的本篇文章就记录这个注解的一个用法. validated 数据校验 我们一般的数据校验是怎 ...

最新文章

  1. 用c语言链表做一个词典,电子字典C语言链表版
  2. mysql004操作表.增删改
  3. outlook存档邮件_如何在Outlook 2013中存档电子邮件
  4. 微软发布ML.NET 1.0,可一键添加机器学习模型
  5. hilbert曲线序编码matlab,Hilbert曲线扫描矩阵的生成算法及其MATLAB程序代码
  6. 带有Hibernate OGM的NoSQL –第二部分:查询数据
  7. app 图标规格参考表
  8. 动态规划——雇佣员工(hdu1158)
  9. Thinkphp3.2 中使用find_in_set
  10. 1900页Python系列PPT分享二:Python序列(列表、元组、字典、集合)(154页)
  11. asp.net core 系列 3 依赖注入服务
  12. 电脑怎么分成两个屏幕,显示不同的内容
  13. phpstudy教程之自带ftp server使用方法详解(图文)
  14. 【Go语言】深入浅出chan(各种实例场景+分析)
  15. 摩斯密码基础知识介绍
  16. markdown编辑器推荐(附官网)
  17. pvs安装配置_配置警告下一代插件以与PVS-Studio集成
  18. 深度解析:电商直播基地运营及盈利模式
  19. MATLAB - contour函数
  20. 官媒痛批“精神鸦片”,曾拿百万年终奖的腾讯游戏员工要失业了吗?

热门文章

  1. OI生涯回忆录 2017.9.10~2018.11.11
  2. ChatGpt 能成为恋爱大师吗?
  3. Analysis for INFO 212 Data Science Programming I Assignment 2
  4. 【Vue+DRF生鲜电商】27.支付宝公钥,私钥,沙箱环境配置
  5. 学习Java day08 面向对象
  6. Python最简单的函数(无参无返回值)2021-08-27
  7. 【墙裂收藏】40000字 Matplotlib 实操干货,真的全!
  8. 鸿蒙玺绶有用么,玺是什么意思 带玺字的男孩名字 用玺字起名的寓意
  9. 区块链应用:跨境贸易平台构想
  10. NRF2401无线通信