mirror of https://github.com/halo-dev/halo
refactor: support locale-based validation messages based on users language (#6819)
#### What type of PR is this? /kind improvement /area core /milestone 2.20.x #### What this PR does / why we need it: 优化校验提示信息根据用户选择的语言代替 `Locale#getDefault()#getLanguage()` #### Does this PR introduce a user-facing change? ```release-note None ```pull/6822/head
parent
99db7a6101
commit
aab8806f0d
|
@ -59,7 +59,6 @@ import org.springframework.util.Assert;
|
|||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
|
@ -86,6 +85,7 @@ import run.halo.app.extension.ReactiveExtensionClient;
|
|||
import run.halo.app.extension.router.SortableRequest;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
import run.halo.app.infra.ValidationUtils;
|
||||
import run.halo.app.infra.exception.RateLimitExceededException;
|
||||
import run.halo.app.infra.exception.UnsatisfiedAttributeValueException;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
@ -298,8 +298,8 @@ public class UserEndpoint implements CustomEndpoint {
|
|||
() -> new ServerWebInputException("Request body is required."))
|
||||
)
|
||||
.doOnNext(emailReq -> {
|
||||
var bindingResult = new BeanPropertyBindingResult(emailReq, "form");
|
||||
validator.validate(emailReq, bindingResult);
|
||||
var bindingResult =
|
||||
ValidationUtils.validate(emailReq, validator, request.exchange());
|
||||
if (bindingResult.hasErrors()) {
|
||||
// only email field is validated
|
||||
throw new ServerWebInputException("validation.error.email.pattern");
|
||||
|
|
|
@ -2,6 +2,11 @@ package run.halo.app.infra;
|
|||
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@UtilityClass
|
||||
public class ValidationUtils {
|
||||
|
@ -15,4 +20,24 @@ public class ValidationUtils {
|
|||
public static final String PASSWORD_REGEX = "^[A-Za-z0-9!@#$%^&*]+$";
|
||||
|
||||
public static final Pattern PASSWORD_PATTERN = Pattern.compile(PASSWORD_REGEX);
|
||||
|
||||
/**
|
||||
* Validate the target object with given locale context.
|
||||
*/
|
||||
public static BindingResult validate(Object target, String objectName,
|
||||
Validator validator, ServerWebExchange exchange) {
|
||||
BindingResult bindingResult = new BeanPropertyBindingResult(target, objectName);
|
||||
try {
|
||||
LocaleContextHolder.setLocaleContext(exchange.getLocaleContext());
|
||||
validator.validate(target, bindingResult);
|
||||
return bindingResult;
|
||||
} finally {
|
||||
LocaleContextHolder.resetLocaleContext();
|
||||
}
|
||||
}
|
||||
|
||||
public static BindingResult validate(Object target, Validator validator,
|
||||
ServerWebExchange exchange) {
|
||||
return validate(target, "form", validator, exchange);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
|||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
|
@ -29,6 +28,7 @@ import run.halo.app.core.user.service.UserService;
|
|||
import run.halo.app.extension.GroupVersion;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.ExternalUrlSupplier;
|
||||
import run.halo.app.infra.ValidationUtils;
|
||||
import run.halo.app.infra.exception.AccessDeniedException;
|
||||
import run.halo.app.infra.exception.RequestBodyValidationException;
|
||||
import run.halo.app.security.authentication.twofactor.totp.TotpAuthService;
|
||||
|
@ -108,8 +108,9 @@ public class TwoFactorAuthEndpoint implements CustomEndpoint {
|
|||
private Mono<ServerResponse> deleteTotp(ServerRequest request) {
|
||||
var totpDeleteRequestMono = request.bodyToMono(PasswordRequest.class)
|
||||
.switchIfEmpty(Mono.error(() -> new ServerWebInputException("Request body required")))
|
||||
.doOnNext(
|
||||
passwordRequest -> this.validateRequest(passwordRequest, "passwordRequest"));
|
||||
.doOnNext(passwordRequest -> this.validateRequest(passwordRequest, "passwordRequest",
|
||||
request)
|
||||
);
|
||||
|
||||
var twoFactorAuthSettings =
|
||||
totpDeleteRequestMono.flatMap(passwordRequest -> getCurrentUser()
|
||||
|
@ -148,7 +149,8 @@ public class TwoFactorAuthEndpoint implements CustomEndpoint {
|
|||
private Mono<ServerResponse> toggleTwoFactor(ServerRequest request, boolean enabled) {
|
||||
return request.bodyToMono(PasswordRequest.class)
|
||||
.switchIfEmpty(Mono.error(() -> new ServerWebInputException("Request body required")))
|
||||
.doOnNext(passwordRequest -> this.validateRequest(passwordRequest, "passwordRequest"))
|
||||
.doOnNext(passwordRequest -> this.validateRequest(passwordRequest,
|
||||
"passwordRequest", request))
|
||||
.flatMap(passwordRequest -> getCurrentUser()
|
||||
.filter(user -> {
|
||||
var encodedPassword = user.getSpec().getPassword();
|
||||
|
@ -199,7 +201,7 @@ public class TwoFactorAuthEndpoint implements CustomEndpoint {
|
|||
private Mono<ServerResponse> configureTotp(ServerRequest request) {
|
||||
var totpRequestMono = request.bodyToMono(TotpRequest.class)
|
||||
.switchIfEmpty(Mono.error(() -> new ServerWebInputException("Request body required.")))
|
||||
.doOnNext(totpRequest -> this.validateRequest(totpRequest, "totp"));
|
||||
.doOnNext(totpRequest -> this.validateRequest(totpRequest, "totp", request));
|
||||
|
||||
var configuredUser = totpRequestMono.flatMap(totpRequest -> {
|
||||
// validate password
|
||||
|
@ -235,11 +237,11 @@ public class TwoFactorAuthEndpoint implements CustomEndpoint {
|
|||
return ServerResponse.ok().body(twoFactorAuthSettings, TwoFactorAuthSettings.class);
|
||||
}
|
||||
|
||||
private void validateRequest(Object target, String name) {
|
||||
var errors = new BeanPropertyBindingResult(target, name);
|
||||
validator.validate(target, errors);
|
||||
if (errors.hasErrors()) {
|
||||
throw new RequestBodyValidationException(errors);
|
||||
private void validateRequest(Object target, String name, ServerRequest request) {
|
||||
var bindingResult =
|
||||
ValidationUtils.validate(target, name, validator, request.exchange());
|
||||
if (bindingResult.hasErrors()) {
|
||||
throw new RequestBodyValidationException(bindingResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;
|
|||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
|
||||
import static run.halo.app.infra.ValidationUtils.validate;
|
||||
|
||||
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
|
||||
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
|
||||
|
@ -81,10 +82,9 @@ class PreAuthSignUpEndpoint {
|
|||
.map(SignUpData::of)
|
||||
.flatMap(signUpData -> {
|
||||
// sign up
|
||||
var bindingResult = new BeanPropertyBindingResult(signUpData, "form");
|
||||
var bindingResult = validate(signUpData, validator, request.exchange());
|
||||
var model = bindingResult.getModel();
|
||||
model.put("globalInfo", globalInfoService.getGlobalInfo());
|
||||
validator.validate(signUpData, bindingResult);
|
||||
if (bindingResult.hasErrors()) {
|
||||
return ServerResponse.ok().render("signup", model);
|
||||
}
|
||||
|
@ -120,8 +120,7 @@ class PreAuthSignUpEndpoint {
|
|||
.POST("/send-email-code", contentType(APPLICATION_JSON),
|
||||
request -> request.bodyToMono(SendEmailCodeBody.class)
|
||||
.flatMap(body -> {
|
||||
var bindingResult = new BeanPropertyBindingResult(body, "body");
|
||||
validator.validate(body, bindingResult);
|
||||
var bindingResult = validate(body, "body", validator, request.exchange());
|
||||
if (bindingResult.hasErrors()) {
|
||||
return Mono.error(new RequestBodyValidationException(bindingResult));
|
||||
}
|
||||
|
|
|
@ -118,8 +118,7 @@ public class SystemSetupEndpoint {
|
|||
.map(initialized -> !initialized)
|
||||
)
|
||||
.flatMap(body -> {
|
||||
var bindingResult = body.toBindingResult();
|
||||
validator.validate(body, bindingResult);
|
||||
var bindingResult = ValidationUtils.validate(body, validator, request.exchange());
|
||||
if (bindingResult.hasErrors()) {
|
||||
return handleValidationErrors(bindingResult, request);
|
||||
}
|
||||
|
@ -208,7 +207,7 @@ public class SystemSetupEndpoint {
|
|||
return redirectToConsole();
|
||||
}
|
||||
var body = new SetupRequest(new LinkedMultiValueMap<>());
|
||||
var bindingResult = body.toBindingResult();
|
||||
var bindingResult = new BeanPropertyBindingResult(body, "form");
|
||||
return ServerResponse.ok().render(SETUP_TEMPLATE, bindingResult.getModel());
|
||||
});
|
||||
}
|
||||
|
@ -243,10 +242,6 @@ public class SystemSetupEndpoint {
|
|||
public String getSiteTitle() {
|
||||
return formData.getFirst("siteTitle");
|
||||
}
|
||||
|
||||
public BindingResult toBindingResult() {
|
||||
return new BeanPropertyBindingResult(this, "form");
|
||||
}
|
||||
}
|
||||
|
||||
Flux<Unstructured> loadPresetExtensions(String username) {
|
||||
|
|
Loading…
Reference in New Issue