验证发布请求DTO bean的建议/最佳方法是什么? 如果验证失败,我需要发送自定义的错误消息,如
{
"code": "invalid_fields",
"fields": {
"email": "Required",
"password": "Required",
}
}
DTO模型
public class SignUpRequest {
@JsonProperty("email")
String email;
@JsonProperty("password")
String password;
public Result validate(){
}
}
控制器
@PostMapping(value = "/register")
public ResponseEntity<Object> signupRider(@RequestBody SignUpRequest signUpRequest) {
Result result = signUpRequest.validate();
return new ResponseEntity<>(x, HttpStatus.OK);
}
SignUpRequest DTO的方法有效。 进行验证的春季方式是什么?
感谢。
答案 0 :(得分:2)
您可以使用以下技巧。
在gradle / maven文件中添加以下依赖项
compile "javax.validation:validation-api:2.0.1.Final"
compile "org.hibernate.validator:hibernate-validator:6.0.9.Final"
Hibernate-validator是validation-api 2.0的实现
将验证的注释添加到控制器类
import org.springframework.validation.annotation.Validated;
@RestController
@RequestMapping(value = "/contact")
@Validated
public class ContactController{
}
将有效注释添加到方法参数
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping(value = "/contact")
@Validated
public class ContactController{
@PostMapping(value = "/register")
public ResponseEntity<Object> signupRider(@Valid @RequestBody SignUpRequest signUpRequest) {
Result result = signUpRequest.validate();
return new ResponseEntity<>(x, HttpStatus.OK);
}
}
将验证的注释添加到您的dto类
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Email;
@Validated
public class SignUpRequest {
@JsonProperty("email")
@Email
String email;
@JsonProperty("password")
@NotNull
String password;
}
使用RestControllerAdvice批注添加ExceptionTranslator
@RestControllerAdvice
public class ExceptionTranslator {
/**
* Exception handler for validation errors caused by method parameters @RequesParam, @PathVariable, @RequestHeader annotated with javax.validation constraints.
*/
@ExceptionHandler
protected ResponseEntity<?> handleConstraintViolationException(ConstraintViolationException exception) {
List<ApiError> apiErrors = new ArrayList<>();
for (ConstraintViolation<?> violation : exception.getConstraintViolations()) {
String value = (violation.getInvalidValue() == null ? null : violation.getInvalidValue().toString());
apiErrors.add(new ApiError(violation.getPropertyPath().toString(), value, violation.getMessage()));
}
return ResponseEntity.badRequest().body(apiErrors);
}
}
创建ApiError类
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiError {
@JsonIgnore
private int code;
private String field;
private String value;
private String message;
public ApiError(String message) {
this.message = message;
}
public ApiError(String field, String value, String message) {
this.field = field;
this.value = value;
this.message = message;
}
}
现在如果错过了密码字段,您将看到以下响应结构:
[
{
"field": "password",
"message": "must be filled"
}
]
如果您想使用某些自定义逻辑来验证字段,可以使用以下方法
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = ContactRequiredParametersValidator.class)
@Target({ METHOD, CONSTRUCTOR })
@Retention(RUNTIME)
@Documented
public @interface ContactRequiredParameters {
String message() default
"Email or phone must be filled";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
import org.apache.commons.lang.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraintvalidation.SupportedValidationTarget;
import javax.validation.constraintvalidation.ValidationTarget;
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ContactRequiredParametersValidator implements ConstraintValidator<ContactRequiredParameters, Object[]> {
@Override
public boolean isValid(Object[] value,
ConstraintValidatorContext context) {
if (value[0] == null) {
return true;
}
if (!(value[0] instanceof SignUpRequest)) {
throw new IllegalArgumentException(
"Illegal method signature, expected two parameters of type LocalDate.");
}
SignUpRequest contact = (SignUpRequest) value[0];
return StringUtils.isNotEmpty(contact.getPassword());
}
}
@PostMapping(value = "/register")
@ContactRequiredParameters
public ResponseEntity<Object> signupRider(@Valid @RequestBody SignUpRequest signUpRequest)
这就是全部。希望它有所帮助
答案 1 :(得分:0)
Spring boot支持使用validation-api
开箱即用的验证,@RestController
@RequiredArgsConstructor
public class TestController {
@PutMapping(value = "/", consumes = APPLICATION_JSON_VALUE)
@ResponseStatus(NO_CONTENT)
public void test(@Valid @RequestBody final SignUpRequest params) {
...
}
}
包含在spring web mvc starter中:
javax.validation.constraints.NotNull
您可以使用Write-Host ""
Write-Host "Note: Path must end with '\'"
Write-Host ""
# Var.
$sourceDir = Read-Host 'Source path'
$targetDir = Read-Host 'Destination path'
# Decl.
$tree = gci -Directory -Name -Recurse $sourceDir
# Check if $sourceDir exist
if(!(Test-Path -Path $sourceDir )){
"Source is not a valid path!" ; pause
exit 1
}
# Check (and create) $targetDir
if(!(Test-Path -Path $targetDir )){
mkdir $targetDir -Force
}
# Rebuild Tree
foreach ( $folders in $tree ) { mkdir $targetDir\$folders -Force }
# Copy Founded Files
$ftc = Get-ChildItem $sourceDir -Recurse | Select-String "test","test2" | Select Path |
foreach{
$targetFile = $targetDir + $_.FullName.SubString($sourceDir.Length);
Copy-Item $_ -destination $targetFile
}
等注释以及其他更复杂的注释来注释您的SignUpRequest。
如果i18n / l10n不太感兴趣,可以使用消息属性或硬编码字符串自定义错误消息。
答案 2 :(得分:0)
如果您想要提供注释之外的行为,您可以编写可以执行此操作的自定义注释,例如
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = NotPastValidator.class)
@Documented
public @interface NotPast {
String message() default "date must not be in the past";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
然后:
public class NotPastValidator implements ConstraintValidator<NotPast, LocalDate> {
@Override
public void initialize(final NotPast constraintAnnotation) {
// nothing to do.
}
@Override
public boolean isValid(final LocalDate value, final ConstraintValidatorContext context) {
// As the Bean Validation specification recommends, we consider null values as being valid.
return value == null || isDateNotPast(value);
}
private boolean isDateNotPast(final LocalDate value) {
return ...
}
}
最后只是注释你的领域:
@NotPast
当然,这仅仅是我之前使用的一些代码的示例,您需要适应您的需求。
如果您根本不想使用验证器API,您可以同样编写自己的代码以编程方式检查并在无效时抛出某种类型的自定义异常。然后可以将其捕获到控制器中,您可以发送您想要的响应,例如
@RestController
public class PaymentController {
@PostMapping(value ="/", consumes = APPLICATION_JSON_VALUE)
public void makePayment(@RequestBody final PaymentParams params) {
// validationService.validate(params);
}
@ExceptionHandler(MyValidationException.class)
public ResponseEntity<ExceptionDto> paymentCardException(final MyValidationException e) {
return status(BAD_REQUEST)
.contentType(APPLICATION_JSON)
.body(new ExceptionDto(e.getMessage));
}
}
我认为鉴于验证API得到了春天的良好支持,对我而言,在使用此堆栈时尽可能应用声明性验证是有意义的。自定义规则可能有点痛苦,但您可以使用基于某些注释的多方面方法,同样您可以在自己的服务中执行一些更复杂的验证。
答案 3 :(得分:0)
这是一个自定义验证。
@PostMapping
private ResponseEntity<?> addMessage(@RequestBody Message message) {
Map<String, String> response = new HashMap<>();
if (message.getInputMessage() == null || message.getInputMessage().equals("")) {
response.put("status", "E");
response.put("message", "input message can not be empty");
return ResponseEntity.ok(response);
}
int id = messageService.addMessage(message);
if (id <= 0) {
response.put("status", "E");
response.put("message", "add message has error");
return ResponseEntity.ok(response);
}
response.put("status", "S");
response.put("message", "success");
return ResponseEntity.ok(response);
}