springboot整合JSR303的快速失败和国际化

版本说明

springboot 2.2.6.RELEASE
hibernate-validator 6.0.18.Final (与springboot版本对应)

解决了什么问题

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors
{"status":10001,"desc":"must be less than or equal to 2","data":null}
  1. hibernate-validator 默认 fast-fail = false,即会验证所有属性
  2. hibernate-validator 默认 读取ValidationMessages.properties配置文件,即失败提示message为英文

本文将要实现

  1. hibernate-validator 使用 fast-fail = true,即只要有一个属性验证失败,就不再继续验证
  2. hibernate-validator 根据默认配置语言和请求参数,决定读取哪个ValidationMessages_*.properties配置文件

代码实例

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo1</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<name>demo1</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

验证实体类

package com.example.demo.rest.vo;

import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;

public class UserVO {

	@Min(value = 6)
	private Integer id;
	
	@NotBlank
	private String username;
	
	@DecimalMax(value = "2")
	private String password;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

rest接口

package com.example.demo.rest.ctrl;

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.rest.vo.UserVO;

@RestController
@RequestMapping("/user")
public class UserController {
	
	@PostMapping("/createUser")
	public void createUser(@Validated UserVO userVO) {
		System.out.println(userVO);
	}
}

测试接口

package com.example.demo;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.nio.charset.Charset;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@SpringBootTest
class Demo1ApplicationTests {
	
	@Autowired
	private WebApplicationContext wac;
	
	private MockMvc mvc;
	
	@BeforeEach
	public void setup() {
		mvc = MockMvcBuilders.webAppContextSetup(wac).build();
	}

	@Test
	public void test() throws Exception {
		String result = mvc.perform(post("/user/createUser")
				.param("password", "3")
	//			.param("l", "fr"))
				.andExpect(status().isOk())
				.andReturn().getResponse().getContentAsString(Charset.forName("UTF-8"));
		System.out.println(result);
	}

}

核心代码

国际化类

package com.example.demo.rest.advice;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;

public class MyLocaleResolver implements LocaleResolver {

	@Override
	public Locale resolveLocale(HttpServletRequest request) {
		//此处可以根据项目需要修改代码,比如使用session或cookie
		String l = request.getParameter("l");
		Locale locale = null;
		if (StringUtils.isEmpty(l)) {
			locale = Locale.getDefault();
		} else {
			String[] split = l.split("_");
			if (split.length == 1) {
				locale = new Locale(split[0]);
			} else {
				locale = new Locale(split[0], split[1]);
			}
		}
		return locale;
	}

	@Override
	public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
		
	}

}

配置类

package com.example.demo.rest.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.LocaleResolver;

import com.example.demo.rest.advice.MyLocaleResolver;

@Configuration
@ConfigurationProperties(prefix = "spring.validator")
public class ValidatorConfig {

	private String failFast = "true";
	
	public String getFailFast() {
		return failFast;
	}

	public void setFailFast(String failFast) {
		this.failFast = failFast;
	}
	
	@Bean
	public LocalValidatorFactoryBean validator() {
		LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
		factoryBean.getValidationPropertyMap().put("hibernate.validator.fail_fast", failFast);
		return factoryBean;
	}
	
	@Bean
	public LocaleResolver localeResolver() {
		return new MyLocaleResolver();
	}
}

测试1

	@Test
	public void test() throws Exception {
		String result = mvc.perform(post("/user/createUser")
				.param("password", "3")
				.param("l", "fr")) //法语
				.andExpect(status().isOk())
				.andReturn().getResponse().getContentAsString(Charset.forName("UTF-8"));
		System.out.println(result);
	}

结果

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
{"status":10001,"desc":"ne peut pas être vide","data":null}

测试2

	@Test
	public void test() throws Exception {
		String result = mvc.perform(post("/user/createUser")
				.param("password", "3")
				.andExpect(status().isOk())
				.andReturn().getResponse().getContentAsString(Charset.forName("UTF-8"));
		System.out.println(result);
	}

结果

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
{"status":10001,"desc":"不能为空","data":null}

备注

本文使用到了springboot返回统一数据格式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值