Spring boot post请求模型验证

时间:2018-05-17 12:00:38

标签: spring-mvc spring-boot

验证发布请求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的方法有效。 进行验证的春季方式是什么?

感谢。

4 个答案:

答案 0 :(得分:2)

您可以使用以下技巧。

  1. 在gradle / maven文件中添加以下依赖项

    compile "javax.validation:validation-api:2.0.1.Final"
    compile "org.hibernate.validator:hibernate-validator:6.0.9.Final"
    
  2. Hibernate-validator是validation-api 2.0的实现

    1. 将验证的注释添加到控制器类

      import org.springframework.validation.annotation.Validated;
      @RestController
      @RequestMapping(value = "/contact")
      @Validated
      public class ContactController{
      

      }

    2. 将有效注释添加到方法参数

      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);
      }
      }
      
    3. 将验证的注释添加到您的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;
      
      }
      
    4. 使用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);
          }
      } 
      
    5. 创建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;
      }
      

      }

    6. 现在如果错过了密码字段,您将看到以下响应结构:

      [
        {
          "field": "password",
          "message": "must be filled"
        }
      ]
      

      如果您想使用某些自定义逻辑来验证字段,可以使用以下方法

      1. 创建特定的注释类
      2. 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 {};
        }

        1. 创建自定义验证程序
        2. 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());
              }
          }

          1. 将@ContactRequiredParameters注释添加到控制器
          2. 中的方法

                        @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不太感兴趣,可以使用消息属性或硬编码字符串自定义错误消息。

此处示例:https://spring.io/guides/gs/validating-form-input/

答案 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);
}