SpringBoot Internationalization and Validation Fusion
Scene
When using the application interaction, it may be necessary to return different language data according to the client's language.
The front-end passes locale-related parameters to the back-end through parameters, request header, etc., and the back-end obtains parameters, and obtains different language-related text information to return to the front-end according to different locale.
Implementation principle
SpringBoot supports internationalization and Validation, mainly through the MessageSource interface and Validator.
International configuration
- Write international configuration files, such as
messages_en_US.properties
andmessages_zh_CN.properties
, juxtaposeresources/i18n
In the directory. - Configuration
or
to specify the location of the internationalization document, e.g.
=i18n/messages
。 - Configuration
LocaleResolver
To parse the current requested locale, the commonly used implementation isAcceptHeaderLocaleResolver
, it passes the request headeraccept-language
Get the current locale.
Validation Configuration
Introducedspring-boot-starter-validation
Reliance to support Validation functionality
<dependency> <groupId></groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
ConfigurationLocalValidatorFactoryBean
To use international verification messages, it needs to be injectedMessageSource
Example
Introduce dependencies
<dependency> <groupId></groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId></groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
International configuration files
existsrc/main/resources/i18n
Create two files in the directory:messages_en_US.properties
andmessages_zh_CN.properties
。
#messages_en_US.properties =Welcome to our website! #messages_zh_CN.properties =Welcome to our website!
Configure MessageSource
In Spring Boot configuration file (or
), configuration
MessageSource
To specify the location of the internationalization document.
If you plan to use Validation's default internationalization file, you don't actually need to specify the file separately for Validation, becauseLocalValidatorFactoryBean
Will automatically search。
However, you can configure your own internationalization files and letMessageSource
Services both your application messages and Validation messages.
# The internationalization file is placed in the src/main/resources/i18n directory and prefixed with messages=i18n/messages, =utf-8
Note: The above configuration assumes that your custom message file is located ini18n/
, and Validation's default message file is。
In fact,The file is located in the jar package of Hibernate Validator, so you don't need to explicitly include it in your resource directory. Spring Boot will automatically load it from the classpath.
Configure LocalValidatorFactoryBean
In your configuration class, create aLocalValidatorFactoryBean
the bean andMessageSource
Inject into it.
so,LocalValidatorFactoryBean
Will use SpringMessageSource
to parse the verification message.
import ; import ; import ; import ; import ; import ; @Configuration public class ValidateConfig { @Bean public LocalValidatorFactoryBean validatorFactoryBean(MessageSource messageSource) { LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); (messageSource); // Set up the HibernateValidator checker (); // Settings Fast exception return As long as there is a verification error, it will fail immediately, and other parameters are not in verification Properties properties = new Properties(); (".fail_fast", "true"); (properties); // Load configuration (); return factoryBean; } }
Use verification
import ; public class MyModel { @NotNull(message = "{}") private String field; // getters and setters }
In the file, you can add
=This field cannot be null.
And in Hibernate ValidatorThe default verification message is already included in the file, such as
{}
value.
Custom verification
-
Define constraint annotations: Create an annotation, use
@Constraint
Tags, and definesmessage
、groups
andpayload
property
import ; import ; import .*; @Documented @Target({, , }) @Retention() @Constraint(validatedBy = ) public @interface MyValidate { String message() default ""; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
- Implementing Constraint Verifier**: Create an implementation
ConstraintValidator
Interface class and rewriteisValid
method - exist
isValid
Use in the methodConstraintValidatorContext
**: If verification fails, useConstraintValidatorContext
ofbuildConstraintViolationWithTemplate
Method to buildConstraintViolation
import ; import ; import ; public class MyValidateContent implements ConstraintValidator<MyValidate, ParamVo> { @Override public void initialize(MyConstraint constraintAnnotation) { // Initialize the code (if needed) } @Override public boolean isValid(ParamVo paramVo, ConstraintValidatorContext constraintValidatorContext) { if ("N".equals(())) { if (() < 18) { buildMessage(constraintValidatorContext, "template1"); return false; } } else { if (() < 20) { buildMessage(constraintValidatorContext, "template2"); return false; } } return true; } private void buildMessage(ConstraintValidatorContext context, String key) { String template = ('{'+key+'}').intern(); (template) .addConstraintViolation(); } }
In this example, ifsex
yesN
andage
Less than18
, the validator will useConstraintValidatorContext
To build aConstraintViolation
。
Message template"{template1}"
Will be parsed when verification fails and replaced with youMyValidate
The default message defined in the annotation or you are inInternationalized messages defined in the file.
Make sure yoursMyValidate
Annotation defines amessage
Attributes, and you are inThere is a corresponding entry in the file, for example:
template1=Men should be greater than18 template2=Women should be greater than20
import ; import ; import ; import ; import ; import ; @Getter @Setter @MyValidate public class ParamVo { @NotBlank(message = "{}") private String sex; @NotNull(message = "age cannot be empty") private Integer age; @NotBlank(message = "{}") @Length(max = 3,message = "{}") private String name; }
Controller layer exception handling
import ; import ; import ; import ; import ; import ; import ; import ; import ; @RestControllerAdvice public class GlobalException { @ExceptionHandler() public ResponseEntity<Object> handleValidationExceptions(MethodArgumentNotValidException ex) { Map<String, String> errors = new HashMap<>(); BindingResult result = (); for (FieldError error : ()) { ((), ()); } // Here you can customize the returned error message structure according to actual needs Map<String, Object> response = new HashMap<>(); ("status", HttpStatus.BAD_REQUEST.value()); ("errors", errors); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } }
Internal method verification
import ; import ; import ; //@Validated @Validated public interface ServiceIntface { //Check the return value, check the parameter @NotNull Object hello(@NotNull @Min(10) Integer id, @NotNull String name); }
import .slf4j.Slf4j; import ; @Slf4j @Service public class ServiceImpl implements ServiceIntface { @Override public Object hello(Integer id, String name) { return null; } }
import ; import ; import ; import ; import ; import ; import ; import ; import ; @RestControllerAdvice public class GlobalException { @ExceptionHandler() public ResponseEntity<Object> handleValidationExceptions(ConstraintViolationException ex) { Map<String, String> errors = new HashMap<>(); Set<ConstraintViolation<?>> constraintViolations = (); for (ConstraintViolation<?> constraintViolation : constraintViolations) { String key = ().toString(); String message = (); (key, message); } // Here you can customize the returned error message structure according to actual needs Map<String, Object> response = new HashMap<>(); ("status", HttpStatus.BAD_REQUEST.value()); ("errors", errors); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } }
- Verify that written on the interface and throw an exception
- Verification is written in specific implementation and throws exceptions
Note
International use in code
Response in the code, manually get the getMessage method using MessageSource, that is, getMessage() in the spring container
# messages_en_US.properties =Welcome to our website! # messages_zh_CN.properties =Welcome to our website! #Define the message and use placeholders {0}, {1}, etc. to indicate the parameter position#=Welcome {0} to {1}
//Create a configuration class to configure LocaleResolver to resolve the current locale environment according to the request:import ; import ; import ; import ; import .; import ; @Configuration public class WebConfig implements WebMvcConfigurer { @Bean public LocaleResolver localeResolver() { SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver(); (); // Set the default language return sessionLocaleResolver; } }
//Create a controller to use international messagesimport ; import ; import ; import ; import ; import ; import ; @RestController @RequestMapping("/hello") public class HelloController { @Autowired private MessageSource messageSource; @GetMapping public String hello(HttpServletRequest request) { Locale locale = (Locale) (.LOCALE_RESOLVER_ATTRIBUTE); //("", new Object[]{"Zhang San", "China"}, ). return ("", null, locale); } }
Locale Getting
By default, the messageSource object registered by spring is ResourceBundleMessageSource, which will be read.Configuration.
The locale is obtained in the request throughLocaleResolver
Processing, defaultAcceptHeaderLocaleResolver
,passWebMvcAutoConfiguration
Inject, fromAccept-Language
Get locale information in the request header.
At this time, the front-end can pass different request headers in different locales to achieve the effect of switching languages
Accept-Language: en-Us Accept-Language: zh-CN
By default, the front-end requests do not need to be processed. If you agree to pass other information Local, use the custom I18nLocaleResolver to replace the defaultAcceptHeaderLocaleResolver
, rewriteresolveLocale
Methods can customize Locale's parsing logic.
import ; import ; import ; import ; import ; import ; import ; /** * */ @Configuration public class I18nConfig { @Bean public LocaleResolver localeResolver() { return new I18nLocaleResolver(); } /** * Get international information of request header * Use customI18nLocaleResolverReplace the defaultAcceptHeaderLocaleResolver,RewriteresolveLocaleMethods can be customizedLocaleAnalytical logic。 * * Use after customizationcontent-languagepassLocaleinformation,use_Divide languages and regions。 * content-language: en_US * content-language: zh_CN */ static class I18nLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest httpServletRequest) { String language = ("content-language"); Locale locale = (); if ((language)) { String[] split = ("_"); locale = new Locale(split[0], split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } } }
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.