gpt4 book ai didi

spring - Spring Data REST 中带有验证错误的空消息

转载 作者:行者123 更新时间:2023-12-04 13:03:31 25 4
gpt4 key购买 nike

我正在使用 Spring Boot、Spring Data REST、Spring HATEOAS、Hibernate、Spring Validation 创建一个应用程序。

我创建了我自己的验证来支持 SpEL 跟随 this guide .

所以我有我的验证器:

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

..并为 REST 存储库定义了验证器:
    @Configuration
public class RestConfig extends RepositoryRestConfigurerAdapter {
@Autowired
private Validator validator;

public static final DateTimeFormatter ISO_FIXED_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
.withZone(ZoneId.of("Z"));

@Bean
public RootResourceProcessor rootResourceProcessor() {
return new RootResourceProcessor();
}

@Override
public void configureExceptionHandlerExceptionResolver(ExceptionHandlerExceptionResolver exceptionResolver) {

}

@Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
super.configureValidatingRepositoryEventListener(validatingListener);
}
}

这是我的 bean :
    @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;
}

}

在配置上,我也有这个类:
@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);
}

}

使用我的存储库:
@Transactional
@RepositoryRestResource(excerptProjection = LicensePlateProjection.class)
@PreAuthorize("isAuthenticated()")
public interface LicensePlateRepository
extends PagingAndSortingRepository<LicensePlate, Long>, RevisionRepository<LicensePlate, Long, Integer> {

public LicensePlate findByLicensePlate(String licencePlate);

使用 Swagger 我正在做这个 json 的 POST:
{"licensePlate":"asdfg","engineEuroLevel":"4","particulateFilter":true,"seats":18,"miniBus":true,"country":"http://localhost:8080/api/v1/countries/1"}

因为我有检查小巴少于 17 个座位的验证规则,我应该看到一个验证错误,我看到这个:
 {
"errors": []
}

带有 HTTP 400 错误(此返回代码是正确的)。

我必须指出,我创建了 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.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@520b6a25]; default message [I veicoli di tipo minibus possono avere al massimo 17 posti (16 passeggeri più il conducente).]

我看不出我在哪里犯了错误。使用其他(也)自定义验证器,我看到了正确的消息。我也有人把我放在正确的方向来解决问题?

最佳答案

我认为 Spring MVC 不知道在哪里显示错误消息,因为违反类级约束的约束不指示任何特定属性。

高压 @ScriptAssert提供 reportOn()用于指定要报告错误的属性的属性。

对于您的自定义约束,您可以通过使用通过 ConstraintValidatorContext 公开的 API 创建自定义约束违规和属性路径来执行相同的操作。 .

关于spring - Spring Data REST 中带有验证错误的空消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46670224/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com