Весенняя проверка модели запроса после загрузки - PullRequest
0 голосов
/ 17 мая 2018

Каков рекомендуемый / лучший способ проверки объекта запроса DTO? Если проверка не удалась, мне нужно отправить настроенное сообщение об ошибке, например

{
"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 имеет метод validate. Каков весенний способ проверки?

Спасибо.

Ответы [ 4 ]

0 голосов
/ 07 августа 2019

Это пользовательская проверка.

@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);
}
0 голосов
/ 17 мая 2018

Вы можете использовать следующую технику.

  1. добавить следующие зависимости в ваш файл 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

  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. Добавление ExceptionTranslator с аннотацией RestControllerAdvice

    @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;
    }
    

    }

Теперь, если поле пароля пропущено, вы увидите следующую структуру ответа:

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

Если вы хотите использовать некоторую пользовательскую логику для проверки ваших полей, вы можете использовать следующий подход

  1. Создать специальный класс аннотаций

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. Создать пользовательский валидатор

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 к вашему методу в контроллере

            @PostMapping(value = "/register")
            @ContactRequiredParameters
            public ResponseEntity<Object> signupRider(@Valid @RequestBody                     SignUpRequest signUpRequest)

Вот и все. Надеюсь, это поможет

0 голосов
/ 17 мая 2018

Если вы хотите, чтобы поведение выходило за рамки предоставленных аннотаций, вы можете написать пользовательскую аннотацию, которая может сделать это, например,

@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 валидации хорошо поддерживается Spring, для меня имеет смысл применять декларативныйпроверки, где это возможно, при использовании этого стека.Пользовательские правила могут быть немного болезненными, но вы можете использовать многогранный подход с некоторыми аннотациями на основе, и в равной степени вы можете выполнять некоторые более сложные проверки в своем собственном сервисе.

0 голосов
/ 17 мая 2018

Spring boot поддерживает проверку из коробки, используя validation-api, который входит в комплект поставки Spring Web MVC Starter:

@RestController
@RequiredArgsConstructor
public class TestController {

    @PutMapping(value = "/", consumes = APPLICATION_JSON_VALUE)
    @ResponseStatus(NO_CONTENT)
    public void test(@Valid @RequestBody final SignUpRequest params) {
        ...
    }
}

Вы можете аннотировать свой SignUpRequest, используя аннотации, такие как javax.validation.constraints.NotNull и другие, более сложные.

сообщения об ошибках можно настроить с помощью свойств сообщений или жестко закодированных строк, если i18n / l10n менее интересен для вас.

Пример здесь: https://spring.io/guides/gs/validating-form-input/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...