1

私は、Springブート、SpringデータREST、Spring HATEOAS、Hibernate、Spring検証を使用してアプリケーションを作成しています。春のデータの検証エラーのある空のメッセージREST

this guideの後にSpELをサポートするための独自の検証を作成しました。

だから、私は検証をしました:

public class SpELClassValidator implements ConstraintValidator<ValidateClassExpression, Object> { 
    private Logger log = LogManager.getLogger(); 

    private ValidateClassExpression annotation; 
    private ExpressionParser parser = new SpelExpressionParser(); 

    public void initialize(ValidateClassExpression constraintAnnotation) { 
     annotation = constraintAnnotation; 
     parser.parseExpression(constraintAnnotation.value()); 
    } 

    public boolean isValid(Object value, ConstraintValidatorContext context) { 
     try {   
      StandardEvaluationContext spelContext = new StandardEvaluationContext(value); 
      return (Boolean) parser.parseExpression(annotation.value()).getValue(spelContext); 
     } catch (Exception e) { 
      log.error("", e); 
      return false; 
     } 

    } 
} 

と私の注釈:バリの

@Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.ANNOTATION_TYPE }) 
@Retention(RetentionPolicy.RUNTIME) 
@Constraint(validatedBy = { SpELClassValidator.class }) 
@Documented 
@Repeatable(ValidateClassExpressions.class) 
public @interface ValidateClassExpression { 

    String message() default "{expression.validation.message}"; 

    Class<?>[] groups() default {}; 

    Class<? extends Payload>[] payload() default {}; 

    String value(); 

} 

構成:

@Bean 
public MessageSource messageSource() { 
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 
    messageSource.setBasenames("classpath:/i18n/messages"); 
    // messageSource.setDefaultEncoding("UTF-8"); 
    // set to true only for debugging 
    messageSource.setUseCodeAsDefaultMessage(false); 
    messageSource.setCacheSeconds((int) TimeUnit.HOURS.toSeconds(1)); 
    messageSource.setFallbackToSystemLocale(false); 
    return messageSource; 
} 

/** 
* Enable Spring bean validation 
* https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation 
* 
* @return 
*/ 
@Bean 
public LocalValidatorFactoryBean validator() { 
    LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); 
    factoryBean.setValidationMessageSource(messageSource()); 
    return factoryBean; 
} 

@Bean 
public MethodValidationPostProcessor methodValidationPostProcessor() { 
    MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor(); 
    methodValidationPostProcessor.setValidator(validator()); 
    return methodValidationPostProcessor; 
} 

..and RESTリポジトリのための定義されたバリデータ:

私がやっている闊歩を使用して

@Transactional 
@RepositoryRestResource(excerptProjection = LicensePlateProjection.class) 
@PreAuthorize("isAuthenticated()") 
public interface LicensePlateRepository 
     extends PagingAndSortingRepository<LicensePlate, Long>, RevisionRepository<LicensePlate, Long, Integer> { 

    public LicensePlate findByLicensePlate(String licencePlate); 

:私のリポジトリを使用して

@RestControllerAdvice 
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler { 

    @Override 
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, 
      HttpHeaders headers, HttpStatus status, WebRequest request) { 
     throw new RuntimeException(ex); 
    } 

    @Override 
    protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, 
      HttpHeaders headers, HttpStatus status, WebRequest request) { 
     throw new RuntimeException(ex); 
    } 

    @Override 
    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, 
      HttpHeaders headers, HttpStatus status, WebRequest request) { 
     throw new RuntimeException(ex); 
    } 

} 

:私もこのクラスをした構成に

@Entity 
// Validate the number of seats if the bus is a minibus 
@ValidateClassExpression(value = "#this.isMiniBus() == true ? #this.getSeats()<=17 : true", message = "{Expression.licenseplate.validminibus}") 
public class LicensePlate extends AbstractEntity { 
    private static final long serialVersionUID = -6871697166535810224L; 

    @NotEmpty 
    @ColumnTransformer(read = "UPPER(licensePlate)", write = "UPPER(?)") 
    @Column(nullable = false, unique = true) 
    private String licensePlate; 

    // The engine euro level (3,4,5,6) 
    @Range(min = 0, max = 6) 
    @NotNull 
    @Column(nullable = false, columnDefinition = "INTEGER default 0") 
    private int engineEuroLevel = 0; 

    @NotNull(message = "{NotNull.licenseplate.enginetype}") 
    @Enumerated(EnumType.STRING) 
    @Column(nullable = false) 
    private EngineType engineType = EngineType.DIESEL; 

    // If the bus has the particulate filter 
    @NotNull(message = "{NotNull.licenseplate.particulatefilter}") 
    @Column(nullable = false, columnDefinition = "BOOLEAN default false") 
    private boolean particulateFilter = false; 

    // Number of seats 
    @NotNull 
    @Range(min = 1, max = 99) 
    @Column(nullable = false, columnDefinition = "INTEGER default 50") 
    private int seats = 50; 

    // If the vehicle is a minibus 
    @NotNull 
    @Column(nullable = false, columnDefinition = "BOOLEAN default false") 
    private boolean miniBus = false; 

    @NotNull(message = "{NotNull.licenseplate.country}") 
    // The country of the vehicle 
    @ManyToOne(fetch = FetchType.LAZY, optional = false) 
    private Country country; 

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
    private List<Note> notes = new ArrayList<>(); 

    public LicensePlate() { 
    } 

    public String getLicensePlate() { 
     return licensePlate; 
    } 

    public void setLicensePlate(String licensePlate) { 
     this.licensePlate = licensePlate; 
    } 

    public int getEngineEuroLevel() { 
     return engineEuroLevel; 
    } 

    public void setEngineEuroLevel(int engineEuroLevel) { 
     this.engineEuroLevel = engineEuroLevel; 
    } 

    public int getSeats() { 
     return seats; 
    } 

    public void setSeats(int seats) { 
     this.seats = seats; 
    } 

    public boolean isMiniBus() { 
     return miniBus; 
    } 

    public void setMiniBus(boolean miniBus) { 
     this.miniBus = miniBus; 
    } 

    public EngineType getEngineType() { 
     return engineType; 
    } 

    public void setEngineType(EngineType engineType) { 
     this.engineType = engineType; 
    } 

    public boolean isParticulateFilter() { 
     return particulateFilter; 
    } 

    public void setParticulateFilter(boolean particulateFilter) { 
     this.particulateFilter = particulateFilter; 
    } 

    public Country getCountry() { 
     return country; 
    } 

    public void setCountry(Country country) { 
     this.country = country; 
    } 

    @Override 
    public String toString() { 
     return "LicensePlate [licensePlate=" + licensePlate + ", engineEuroLevel=" + engineEuroLevel + ", engineType=" 
       + engineType + ", particulateFilter=" + particulateFilter + ", seats=" + seats + ", miniBus=" + miniBus 
       + "]"; 
    } 

    public List<Note> getNotes() { 
     return notes; 
    } 

    public void setNotes(List<Note> notes) { 
     this.notes = notes; 
    } 

} 

このは私のBeanですこのjsonのPOST:

{"licensePlate":"asdfg","engineEuroLevel":"4","particulateFilter":true,"seats":18,"miniBus":true,"country":"http://localhost:8080/api/v1/countries/1"} 

私はミニバスを確認し、検証ルールは以下の17の議席を持ってきたので、私は検証エラーが表示されるはずです、instad私はこれを参照してください。HTTP 400エラー(この戻りコードで

{ 
    "errors": [] 
} 

が正しいとし)。

私はJUnitのテストケースを作成し、私は右のメッセージが表示ことを指摘しました:

@Test 
@WithMockUser(roles = "ADMIN") 
public void validateMinibusWithMoreThan17SeatsFails() { 
    assertEquals(1, countryRepository.count()); 

    LicensePlate plate = new LicensePlate(); 
    plate.setLicensePlate("AA123BB"); 
    plate.setEngineEuroLevel(3); 
    plate.setMiniBus(true); 
    plate.setSeats(18); 
    plate.setCountry(countryRepository.findFirstByOrderByIdAsc()); 

    Set<ConstraintViolation<LicensePlate>> constraintViolations = validator.validate(plate); 
    assertEquals(1, constraintViolations.size()); 
    ConstraintViolation<LicensePlate> constraintViolation = constraintViolations.iterator().next(); 
    assertEquals("I veicoli di tipo minibus possono avere al massimo 17 posti (16 passeggeri più il conducente).", 
      constraintViolation.getMessage()); 
} 

をだから私は問題はREST/MVC部にあると思います。リクエストをデバッグし、クラスorg.springframework.data.rest.core.RepositoryConstraintViolationExceptionを確認しました。

org.springframework.data.rest.core.ValidationErrors: 1 errors 
Error in object 'LicensePlate': codes [ValidateClassExpression.LicensePlate,ValidateClassExpression]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [LicensePlate.,]; arguments []; default message [],org.springframework.valid[email protected]520b6a25]; default message [I veicoli di tipo minibus possono avere al massimo 17 posti (16 passeggeri più il conducente).] 

私は間違いを犯していますどこで見ることができない。コンストラクタで私は私のエラーが正しいと私は、エラーメッセージと右の構造を見ることができます参照してください。他の(また)カスタムバリデーターでは、私は正しいメッセージを見る。私は誰かが問題を解決するために正しい方向に私を置くためにも?

+0

こんにちは! )あなたは[登録]しましたか(https://docs.spring。io/spring-data/rest/docs/current/reference/html /#validation)あなたのバリデータは? SDR検証(例:https://github.com/Cepr0/sdr-validation-demo)を見たくないのですか? – Cepr0

+0

@ Cepr0はい、ありました。私は私の質問を更新しました。私はあなたの例をチェックして、同じことをしているようです。 – drenda

+0

標準バリデータ( '@Autowired private Validator validator;')のみを登録しましたが、カスタムバリデーションではありません。 'validatingListener.addValidator(" beforeSave "、new SpELClassValidator());'。または私は間違っている?... – Cepr0

答えて

0

私は、クラスレベル制約の制約違反が特定のプロパティを示していないため、Spring MVCはエラーメッセージの表示場所を知らないと考えています。

HVの@ScriptAssertは、エラーを報告するプロパティを指定するためのreportOn()属性を提供します。

カスタム制約については、ConstraintValidatorContextで公開されているAPIを使用して、カスタマイズされた制約違反とプロパティパスを作成することで同じことができます。

+0

あなたは正しいです。私は特定のプロパティもなく、Spring mvcがCoinstraintValidationエラーを表示できたと推測しました。 BTW reportOn()はそのリリースからのみ利用可能であるため、Hibernate Validatorをversion> = 5.4に更新しなければなりませんでした。 – drenda

関連する問題