SpringBoot @InitBinder注解实现Bean国际化校验

news2024/9/23 23:25:58

参考资料

  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/messages
    encoding: 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 {

    // 默认解析器,用来设置当前会话默认的国际化语言
    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
        // 指定当前项目的默认语言是中文
        sessionLocaleResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
        return sessionLocaleResolver;
    }

    // 默认拦截器,用来指定切换国际化语言的参数名
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {

        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        /*
            设置国际化请求参数为language
            设置完成之后,URL中的 ?language=zh 表示读取国际化文件messages_zh.properties
         */
        localeChangeInterceptor.setParamName("language");
        return localeChangeInterceptor;
    }
    
    // 将我们自定义的国际化语言参数拦截器放入Spring MVC的默认配置中
    @Override
    public 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集合
    @Valid
    private 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 {

    @Override
    public boolean supports(Class<?> clazz) {

        // 只支持指定Bean类型的校验
        return Test16Form.class.equals(clazz);
    }

    @Override
    public 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 {

	// 注入我们自定义的校验器
    @Resource
    private FromToValidator fromToValidator;

    @InitBinder
    public 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")
    @ResponseBody
    public 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 {

    @Resource
    private MessageSource messageSource;

    @ExceptionHandler(BindException.class)
    // 通过注解指定了响应的状态码,前台$.ajax会在error函数的xhr响应中接收错误json
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public List<Map<String, String>> BindExceptionHandle(BindException errors) {
		
		// 存放所有error信息的List
        List<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 {

	// 注入我们自定义的校验器
    @Resource
    private FromToValidator fromToValidator;

    @InitBinder
    public 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")
    @ResponseBody
    public 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 {

    @Resource
    private HttpServletResponse response;

    @Resource
    private MessageSource messageSource;

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public List<Map<String, String>> HandleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
		
		// 存放所有error信息的List
        List<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响应中接收错误json
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        return errorList;
}

4.4 效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/57173.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【spark】第一章——Spark简介及环境配置

文章目录1. Spark 概述1.1 Spark 是什么1.2 Spark and Hadoop1.3 Spark or Hadoop1.4 Spark 核心模块2. Spark 快速上手2.1 创建 Maven 项目2.1.1 增加 Scala 插件2.1.2 增加依赖关系2.1.3 WordCount2.1.4 异常处理3. Spark 运行环境3.1 Local 模式3.1.1 解压缩文件3.1.2 启动 …

MATLAB源码-GRABIT从图像文件中提取数据点。

源码链接&#xff1a; https://download.csdn.net/download/tgs2033/87238015https://download.csdn.net/download/tgs2033/87238015 GRABIT从图像文件中提取数据点。 GRABIT启动用于从图像文件中提取数据的GUI程序。它能够读取BMP、JPG、TIF、GIF和PNG文件&#xff08;IMREAD…

12月3日:thinkphp模型与数据库相同的部分

定义 定义一个模型类 <?phpnamespace app\index\model; use think\Model;//定义一个User模型类 class User extends Model{//默认主键为自动识别&#xff0c;如果需要指定&#xff0c;可以设置属性//protected $pk uid; //$pk代表主键&#xff0c;primary key的缩写 } …

[附源码]Python计算机毕业设计Django基于JAVA技术的旅游信息交互系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

[附源码]Python计算机毕业设计Django基于Java酒店管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

特征解耦,torch.cumprod(),np.random.seed(),plt.scatter

1.infoGAN 通常&#xff0c;我们学到的特征是混杂在一起的&#xff0c;如上图所示&#xff0c;这些特征在数据空间中以一种复杂的无序的方式进行编码&#xff0c;但是如果这些特征是可分解的&#xff0c;那么这些特征将具有更强的可解释性&#xff0c;我们将更容易的利用这些特…

BI-SQL丨MEGRE

MEGRE MEGRE语句&#xff0c;在SQL的生态圈中&#xff0c;一直都隶属于一个比较重要的位置。 要知道&#xff0c;在实际的项目应用中&#xff0c;我们经常需要从上游数据源&#xff0c;进行原始数据的抽取、清洗、存储、分析等操作&#xff0c;特别是在存储这一环节&#xff…

SpringCloud Ribbon / Feign

文章目录什么是Ribbon&#xff1f;Ribbon的作用&#xff1f;什么是Feign&#xff1f;Feign的作用&#xff1f;什么是Ribbon&#xff1f; Spring Cloud Ribbon 是基于Netflix Ribbon实现的一套客户端负载均衡的工具. Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供…

介绍HTTP

介绍 HTTP HTTP 协议用于客户端和服务器端之间的通信。请求访问资源的一端被称为客户端&#xff0c; 而提供资源响应的一端被称为服务器端。 HTTP 是一种不保存状态的协议&#xff0c;即无状态&#xff08;stateless&#xff09; 协议&#xff0c;它不对之前发生过的请求和响…

Kotlin高仿微信-第54篇-扫一扫

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

安卓APP源码和报告——音乐播放器

课 程 设 计 报 告 院 系&#xff1a;专 业&#xff1a;题 目&#xff1a;科 目&#xff1a;学 生&#xff1a;指导教师&#xff1a;完成时间&#xff1a;目 录 1. 引言1 1.1 目的1 1.2 背景1 2. 需求分析1 3. 系统设计1 3.1总体设计1 3.2功能设计1 4. 系统开发2 4.1…

秋招经验分享:最终我还是选择了百度

点击进入—>3D视觉工坊学习交流群自我介绍感谢工坊的邀请&#xff0c;来做这次秋招经验的分享。本科和研究生都是自动化专业&#xff0c;研究生期间做移动机器人的定位方向&#xff0c;现在是百度的一名算法工程师&#xff0c;很喜欢现在的工作环境和氛围&#xff0c;强烈推…

【LeetCode每日一题】——72.编辑距离

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 字符串 二【题目难度】 困难 三【题目编号】 72.编辑距离 四【题目描述】 给你两个单词 word…

【Linux】常用的Linux命令(初学者必读)

一、学习Linux的原因 开源&#xff0c;免费系统迭代更新系统性能稳定安全性高多任务&#xff0c;多用户耗资源少内核小应用领域广泛使用及入门容易 二、Linux常用的命令 我使用的Linux环境是在 腾讯云服务器上的Centos 7和 Xshell。 下面我把常用的一些命令分成了几个部分&am…

VPS8505 微功率隔离电源专用芯片2.3-6VIN/24V/1A 功率管 替代金升阳模块

文章目录 前言一、是什么&#xff1f;二、特点三、应用领域四、简化应用五、引脚及功能六、参数测试电路 总结前言 隔离电源市场&#xff0c;一直被塑封模块产品占领&#xff0c;之前国内无专业 做隔离芯片的厂家&#xff0c;市场以模块厂进口芯片方案为主&#xff1b;…

深入 Java 线程池:从设计思想到源码解读

为什么需要线程池 我们知道创建线程的常用方式就是 new Thread() &#xff0c;而每一次 new Thread() 都会重新创建一个线程&#xff0c;而线程的创建和销毁都需要耗时的&#xff0c;不仅会消耗系统资源&#xff0c;还会降低系统的稳定性。在 jdk1.5 的 JUC 包中有一个 Execut…

从实用角度浅析前端全链路质量监控中台技术方案

大厂技术 高级前端 Node进阶点击上方 程序员成长指北&#xff0c;关注公众号回复1&#xff0c;加入高级Node交流群感谢作者陈煮酒的投稿。前言无论是纯前端业务还是服务端业务&#xff0c;线上质量的保障都是我们的底线要求&#xff0c;也是我们日常需要花费很多精力关注的环…

【大数据入门核心技术-Zookeeper】(五)ZooKeeper集群搭建

目录 一、准备工作 1、集群分布 2、创建数据目录 3、下载安装包 二、解压安装 1、解压 2、修改配置文件zoo.cfg 3、添加myid配置 4、分发zk文件夹和分别新建myid 5、配置环境变量 6、三台机器分别启动zookeeper服务 一、准备工作 1、集群分布 服务器IP主机名myid的…

有损压缩与无损压缩

有损压缩与无损压缩数据压缩有损压缩无损压缩有损压缩与无损压缩的区别Which One to Use?Final Words有损压缩、无损压缩&#xff08;图片、音频、视频&#xff09;图片文件格式音频文件格式视频文件格式数据压缩 数据压缩&#xff08;Data Compression&#xff09;是减小任何…

教你6招轻松搞定 网站被木马反复篡改

提到网络被恶意篡改&#xff0c;应该让很多做了百度竞价的企业官网怀恨已久了吧&#xff1f;这类行为的目的就是通过这些受害网站获得排名并跳转到违法网站&#xff0c;达到不法的目的。对于企业来说不但损失了百度竞价的费用&#xff0c;还对企业形象造成很大的影响。甚至直接…