- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 Spring Boot 2.1.11、Spring DATA REST、Hibernate。我正在尝试使 JRS 303 与 SDR 一起工作,以便获得验证错误的 JSON 响应。
到目前为止,它有效,但只有当我执行 POST 时,当我执行 PATCH 时,我会得到意外的响应。看来 ConstraintViolationException 已按照描述进行了包装 here .
为了给出一个完整的场景,这是我的配置:
自定义配置.java:
@Configuration
@EnableRetry
@EnableTransactionManagement
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
// To activate the Spring Data Envers repository factory
@EnableJpaRepositories(basePackages = "server.repositories", repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
public class CustomConfiguration {
public static CustomConfiguration INSTANCE;
@PostConstruct
public void init() {
INSTANCE = this;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public static SpringSecurityAuditorAware springSecurityAuditorAware() {
return new SpringSecurityAuditorAware();
}
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:/i18n/messages");
messageSource.setUseCodeAsDefaultMessage(false);
messageSource.setCacheSeconds((int) TimeUnit.HOURS.toSeconds(1));
messageSource.setFallbackToSystemLocale(false);
return messageSource;
}
@Bean
public MessageSourceAccessor messageSourceAccessor() {
return new MessageSourceAccessor(messageSource());
}
@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;
}
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
@Bean
FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
FilterRegistrationBean<ForwardedHeaderFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new ForwardedHeaderFilter());
return bean;
}
}
GlobalRepositoryRestConfigurer.java
@Configuration
public class GlobalRepositoryRestConfigurer implements RepositoryRestConfigurer {
@Autowired(required = false)
private Jackson2ObjectMapperBuilder objectMapperBuilder;
@Autowired
private Validator validator;
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.getCorsRegistry().addMapping(corsMapping).exposedHeaders(corsExposedHeaders).allowedOrigins(corsAllowedOrigins)
.allowedHeaders(corsAllowedHeaders).allowedMethods(corsAllowedMethod).maxAge(corsMaxAge);
}
@Override
public void configureConversionService(ConfigurableConversionService conversionService) {
}
@Bean
public ValidationExceptionSerializer validationExceptionSerializer() {
return new ValidationExceptionSerializer();
}
@Bean
public CustomValidationExceptionSerializer customValidationExceptionSerializer() {
return new CustomValidationExceptionSerializer();
}
@Bean
public ConstraintViolationExceptionSerializer constraintViolationExceptionSerializer() {
return new ConstraintViolationExceptionSerializer();
}
@Bean
public Module customJacksonModule() {
SimpleModule customJacksonModule = new SimpleModule();
customJacksonModule.addSerializer(ConstraintViolationException.class, constraintViolationExceptionSerializer());
customJacksonModule.addSerializer(ValidationException.class, validationExceptionSerializer());
customJacksonModule.addSerializer(it.rebus.server.exceptions.ValidationException.class, customValidationExceptionSerializer());
return customJacksonModule;
}
@Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
}
@Override
public void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
messageConverters.add(new ResourceHttpMessageConverter());
}
}
WebMvcConfiguration.java
@Configuration
@EnableHypermediaSupport(type = { HypermediaType.HAL })
public class WebMvcConfiguration implements WebMvcConfigurer {
private Validator validator;
@Bean
public LocaleResolver localeResolver() {
return new SmartLocaleResolver();
}
public class SmartLocaleResolver extends CookieLocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String acceptLanguage = request.getHeader("Accept-Language");
if (acceptLanguage == null || acceptLanguage.trim().isEmpty()) {
return super.determineDefaultLocale(request);
}
return request.getLocale();
}
}
@Autowired
public WebMvcConfiguration(Validator validator) {
this.validator = validator;
}
@Override
public Validator getValidator() {
return validator;
}
@Bean
public CustomErrorAttributes myCustomErrorAttributes() {
return new CustomErrorAttributes();
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping(corsMapping).exposedHeaders(corsExposedHeaders).allowedOrigins(corsAllowedOrigins)
.allowedHeaders(corsAllowedHeaders).allowedMethods(corsAllowedMethod).maxAge(corsMaxAge);
}
}
RequestBodyValidationProcessor.java
@ControllerAdvice
@Log4j2
public class RequestBodyValidationProcessor extends RequestBodyAdviceAdapter {
@Autowired
private LocalValidatorFactoryBean validator;
@Override
public boolean supports(final MethodParameter methodParameter, final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType) {
final Annotation[] parameterAnnotations = methodParameter.getParameterAnnotations();
for (final Annotation annotation : parameterAnnotations) {
if (annotation.annotationType().equals(Valid.class)) {
return true;
}
}
return false;
}
@Override
public Object afterBodyRead(final Object body, final HttpInputMessage inputMessage, final MethodParameter parameter,
final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType) {
final Object obj = super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(obj);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
return obj;
}
}
ApplicationExceptionHandler.java
@RestControllerAdvice
@Log4j2
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {
@Autowired
private ErrorLogRepository errorLogRepository;
@Autowired
private MessageSource messageSource;
private MessageSourceAccessor messageSourceAccessor = null;
@PostConstruct
public void postConstruct() {
Assert.notNull(messageSource, "MessageSource must not be null!");
this.messageSourceAccessor = new MessageSourceAccessor(messageSource);
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status,
WebRequest request) {
HttpServletRequest httpServlet = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
log.error(String.format("MethodArgumentNotValidException caused from client with ip %s. Error: %s", AppUtils.getRemoteIp(httpServlet),
ExceptionUtils.getRootCauseMessage(ex)));
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
buildGenericError(ex, ExceptionCode.ERROR_CODE, httpServlet, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
@Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status,
WebRequest request) {
HttpServletRequest httpServlet = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
log.error(String.format("HttpMediaTypeNotSupportedException caused from client with ip %s. Error: %s", AppUtils.getRemoteIp(httpServlet),
ExceptionUtils.getRootCauseMessage(ex)));
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
buildGenericError(ex, ExceptionCode.ERROR_CODE, httpServlet, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status,
WebRequest request) {
HttpServletRequest httpServlet = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
log.error(String.format("HttpMessageNotReadableException caused from client with ip %s. Error: %s", AppUtils.getRemoteIp(httpServlet),
ExceptionUtils.getRootCauseMessage(ex)));
if (ExceptionUtils.getRootCauseMessage(ex).contains("Duplicate entry")) {
return response(HttpStatus.CONFLICT, new HttpHeaders(), buildIntegrityError(ex, httpServlet, HttpStatus.CONFLICT, LocaleContextHolder.getLocale()));
} else {
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
buildGenericError(ex, ExceptionCode.ERROR_CODE, httpServlet, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
HttpServletRequest httpServlet = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
log.error("", ex);
return response(HttpStatus.INTERNAL_SERVER_ERROR, new HttpHeaders(),
buildGenericError(ex, ExceptionCode.INTERNAL_ERROR, httpServlet, HttpStatus.INTERNAL_SERVER_ERROR, LocaleContextHolder.getLocale()));
}
@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<?> handleConflictException(DataIntegrityViolationException ex, HttpServletRequest request, Locale locale) throws Exception {
if (ex instanceof RepositoryConstraintViolationException) {
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
new RepositoryConstraintViolationExceptionMessage((RepositoryConstraintViolationException) ex, messageSourceAccessor));
}
return response(HttpStatus.CONFLICT, new HttpHeaders(), buildIntegrityError(ex, request, HttpStatus.CONFLICT, locale));
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<?> handleValidationException(ConstraintViolationException ex, HttpServletRequest request, Locale locale) throws Exception {
try {
ResponseEntity<ConstraintViolationException> response = new ResponseEntity<ConstraintViolationException>(ex, new HttpHeaders(),
HttpStatus.BAD_REQUEST);
return response;
} catch (Exception e) {
log.error("", e);
}
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(), "");
}
@ExceptionHandler(TransactionSystemException.class)
public ResponseEntity<?> handleTransactionSystemException(TransactionSystemException ex, HttpServletRequest request, Locale locale) throws Exception {
if (ex.getCause() instanceof RollbackException) {
RollbackException rollbackException = (RollbackException) ex.getCause();
if (rollbackException.getCause() instanceof ApplicationExceptionInterface) {
ApplicationExceptionInterface finalException = (ApplicationExceptionInterface) rollbackException.getCause();
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(), buildGenericError(rollbackException.getCause(),
ExceptionCode.fromCode(finalException.getCode()), request, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
}
return response(HttpStatus.INTERNAL_SERVER_ERROR, new HttpHeaders(),
buildGenericError(ex, ExceptionCode.INTERNAL_ERROR, request, HttpStatus.INTERNAL_SERVER_ERROR, LocaleContextHolder.getLocale()));
}
@ExceptionHandler(InternalException.class)
public ResponseEntity<?> handleInternalException(InternalException ex, HttpServletRequest request, Locale locale) throws Exception {
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
buildGenericError(ex, ExceptionCode.fromCode(ex.getCode()), request, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<?> handleFileUpload(MaxUploadSizeExceededException ex, HttpServletRequest request, Locale locale) throws Exception {
log.error(String.format("Received a file too big from %s. Error: %s", AppUtils.getRemoteIp(request), ExceptionUtils.getRootCauseMessage(ex)));
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(), buildIntegrityError(ex, request, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
private JsonException buildIntegrityError(final Throwable exception, final HttpServletRequest request, final HttpStatus httpStatus, Locale locale) {
return buildIntegrityError(exception, request.getRequestURI(), httpStatus, locale);
}
private JsonException buildIntegrityError(final Throwable exception, String requestUri, final HttpStatus httpStatus, Locale locale) {
String finalMessage = "";
String rootMsg = ExceptionUtils.getRootCauseMessage(exception);
Optional<Map.Entry<String, ExceptionCode>> entry = constraintCodeMap.entrySet().stream().filter((it) -> rootMsg.contains(it.getKey())).findAny();
if (entry.isPresent()) {
finalMessage = messageSource.getMessage(entry.get().getValue().getCode(), new Object[] {}, locale);
} else {
finalMessage = messageSource.getMessage(ExceptionCode.INTEGRITY_VIOLATION.getCode(), new Object[] { rootMsg }, locale);
}
JsonException jsonException = new JsonException();
jsonException.setError(httpStatus.getReasonPhrase());
jsonException.setStatus(httpStatus.value());
jsonException.setException(exception.getClass().getName());
jsonException.setMessage(finalMessage);
jsonException.setPath(requestUri);
return jsonException;
}
private JsonException buildGenericError(final Throwable exception, final ExceptionCode exceptionCode, final HttpServletRequest request,
final HttpStatus httpStatus, Locale locale) {
String rootMsg = ExceptionUtils.getRootCauseMessage(exception);
String finalMessage = "";
Object[] args = new Object[]{rootMsg};
if (exception instanceof ApplicationExceptionInterface) {
args = ((ApplicationExceptionInterface) exception).getArgs();
}
try {
// Not storing in DB ValidationExceptions
if (!(exception instanceof ValidationException)) {
try {
ErrorLog errorLog = dbStoreException(exception);
String dbCode = messageSource.getMessage(ExceptionCode.ERROR_CODE.getCode(), new Object[]{errorLog.getCode()}, locale);
finalMessage = dbCode + " " + MessageUtils.getMessage(locale, exceptionCode.getCode(), args);
} catch (Exception e) {
finalMessage = messageSource.getMessage(exceptionCode.getCode(), args, locale);
}
} else {
finalMessage = messageSource.getMessage(exceptionCode.getCode(), args, locale);
}
} catch (Exception e) {
finalMessage = messageSource.getMessage(exceptionCode.getCode(), args, locale);
}
JsonException jsonException = new JsonException();
jsonException.setError(httpStatus.getReasonPhrase());
jsonException.setStatus(httpStatus.value());
jsonException.setException(exception.getClass().getName());
jsonException.setMessage(finalMessage);
jsonException.setPath(request.getRequestURI());
if (exception instanceof ApplicationExceptionInterface) {
jsonException.setErrorCode(((ApplicationExceptionInterface) exception).getCode());
}
return jsonException;
}
private static <T> ResponseEntity<T> response(HttpStatus status, HttpHeaders headers, T body) {
Assert.notNull(headers, "Headers must not be null!");
Assert.notNull(status, "HttpStatus must not be null!");
return new ResponseEntity<T>(body, headers, status);
}
private ErrorLog dbStoreException(Throwable throwable) {
HttpServletRequest httpServlet = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ErrorLog errorLog = new ErrorLog();
errorLog.setTitle(ExceptionUtils.getRootCauseMessage(throwable));
errorLog.setText(ExceptionUtils.getStackTrace(throwable));
errorLog.setLocation(AppUtils.getExceptionPosition(throwable));
errorLog.setRemoteAddress(AppUtils.getRemoteIp(httpServlet));
return errorLogRepository.save(errorLog);
}
}
我正在使用从存储库公开的保存/更新方法:
@Transactional
public interface ContactRepository extends JpaRepository<Contact, Long>, RevisionRepository<Contact, Long, Integer> {
}
这是未使用的 Controller ,因为我使用 SDR 通过存储库公开端点:
@RepositoryRestController
@Log4j2
public class ContactController extends RevisionController<Contact> {
@Autowired
private LocalValidatorFactoryBean validator;
@PersistenceContext
private EntityManager entityManager;
@Autowired
private ContactRepository contactRepository;
@Autowired
private ContactService contactService;
@Autowired
private NoteService noteService;
@Autowired
private MessageSource messageSource;
@Autowired
private MediaService mediaService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@SuppressWarnings("rawtypes")
@Autowired
private PagedResourcesAssembler pagedResourcesAssembler;
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(validator);
}
@PreAuthorize("permitAll()")
@GetMapping(path = "/contacts/types")
public ResponseEntity<?> getContactTypes(Locale locale) {
return ResponseEntity.ok(AppUtils.listToResourcesList(Arrays.asList(PersonType.values())));
}
@GetMapping(path = "/contacts/{id:[0-9]+}")
public ResponseEntity<?> findOne(@PathVariable("id") long id, Locale locale, PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_BACK_OFFICE','ROLE_ACCOUNTANT')")
@PostMapping(path = "/contacts/searches")
public ResponseEntity<?> search(@RequestBody(required = true) List<Filter> filters, Pageable pageable, Locale locale,
PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_ACCOUNTANT')")
@PostMapping(path = "/contacts/{id}/enableWallet")
public ResponseEntity<?> enableWallet(@PathVariable("id") long contactId, HttpServletRequest request, Locale locale,
PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_ACCOUNTANT')")
@PostMapping(path = "/contacts/{id}/balanceThreshold")
public ResponseEntity<?> balanceThreshold(@PathVariable("id") long contactId, @RequestBody(required = true) BigDecimal balanceThreshold, Locale locale,
PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_ACCOUNTANT','ROLE_BACK_OFFICE')")
@SuppressWarnings("unchecked")
@GetMapping(path = "/contacts/{id}/movements")
public ResponseEntity<?> getMovements(@PathVariable("id") long contactId, @RequestParam(value = "from", required = false) Instant from,
@RequestParam(value = "until", required = false) Instant until, @RequestParam(value = "description", required = false) String description,
Pageable pageable, Locale locale, PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_BACK_OFFICE','ROLE_ACCOUNTANT')")
@GetMapping(path = "/contacts/{id}/notes")
public ResponseEntity<?> getNotes(@PathVariable(value = "id") long id, Pageable pageable, Locale locale,
PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_BACK_OFFICE','ROLE_ACCOUNTANT')")
@GetMapping(path = "/contacts/{id}/auditLogs")
public ResponseEntity<?> getAuditLogs(@PathVariable(value = "id") long id, Pageable pageable, Locale locale,
PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_BACK_OFFICE','ROLE_ACCOUNTANT')")
@GetMapping(path = "/contacts/{id}/media")
public ResponseEntity<?> getMedia(@PathVariable(value = "id") long id, Pageable pageable, Locale locale,
PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_BACK_OFFICE','ROLE_ACCOUNTANT')")
@PostMapping(path = "/contacts/{id}/notes")
public ResponseEntity<?> addNote(@PathVariable(value = "id") long id, @Valid @RequestBody(required = true) Note note, Locale locale,
PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_BACK_OFFICE','ROLE_ACCOUNTANT')")
@GetMapping(path = "/contacts/{id}/revisions")
public ResponseEntity<?> findRevisions(@PathVariable(value = "id") Long id, Pageable pageable) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_BACK_OFFICE','ROLE_ACCOUNTANT')")
@GetMapping(path = "/contacts/{id}/revisions/{revid}")
public ResponseEntity<?> getChanges(@PathVariable(value = "id") Long id, @PathVariable(value = "revid") Integer revId, Pageable pageable) {
//
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_BACK_OFFICE','ROLE_ACCOUNTANT')")
@PostMapping(path = "/contacts/{id}/media", consumes = {"multipart/form-data"})
public ResponseEntity<?> addMedia(@PathVariable("id") long id, @RequestPart("files") List<MultipartFile> files, HttpServletRequest request, Locale locale,
PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("permitAll()")
@PostMapping(path = "/contacts/resetPassword")
public ResponseEntity<?> resetPassword(@RequestBody(required = true) String username, Locale locale, PersistentEntityResourceAssembler resourceAssembler) {
//
}
@PreAuthorize("permitAll()")
@PostMapping(path = "/contacts/register")
public ResponseEntity<?> register(@Valid @RequestBody(required = true) Contact contact, HttpServletRequest request, PersistentEntityResourceAssembler resourceAssembler) {
//
}
}
这是 bean 的第一部分:
@Entity
@EntityListeners(ContactListener.class)
@Table(indexes = {@Index(name = "idx_enabled", columnList = "enabled"), @Index(name = "idx_name", columnList = "name")})
@ScriptAssert.List({
//CHECK TAX CODE VALIDITY
@ScriptAssert(lang = "javascript", script = "_.taxCode != null && _.taxCode != '' && _.personType=='NATURAL_PERSON'?_.isTaxCodeValid(_.taxCode,_.country):true", alias = "_", reportOn = "taxCode", message = "{contact.invalid.taxcode}"),
//CHECK VAT NUMBER VALIDITY
@ScriptAssert(lang = "javascript", script = "_.vatNumber != null && _.vatNumber != '' && _.personType=='LEGAL_PERSON'?_.isVatNumberValid(_.vatNumber,_.country):true", alias = "_", reportOn = "vatNumber", message = "{contact.invalid.vatNumber}")
})
@Data
@EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
@AllArgsConstructor
@Builder
@ToString(callSuper = true)
public class Contact extends AbstractEntity {
@Builder.Default
@Audited
@NotNull
@Column(nullable = false, columnDefinition = "VARCHAR(255) DEFAULT 'LEGAL_PERSON'")
@Enumerated(EnumType.STRING)
private PersonType personType = PersonType.LEGAL_PERSON;
@Audited
@NotEmpty
@Size(min = 3, max = 255)
@Column(nullable = false)
private String name;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Account account;
@Audited
@NotBlank
private String address;
但是,当我执行 POST 保存存在一些错误的实体时,我得到了如下内容:
{"errors":[{"entity":"Contact","property":"address","invalidValue":null,"message":"Il campo non può essere vuoto. Inserire un valore valido e ripetere l'operazione."},{"entity":"Contact","property":"personType","invalidValue":null,"message":"Il campo non può essere vuoto. Inserire un valore valido e ripetere l'operazione."},{"entity":"Contact","property":"city","invalidValue":null,"message":"Il campo non può essere vuoto. Inserire un valore valido e ripetere l'operazione."}]}
那很好。当我对表单中的一些错误进行修补时,我得到了这样的答复:
{"timestamp":"2020-01-08T19:42:31.633+0000","status":400,"error":"Bad Request","exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"Cod. errore [494-577]. Cod. errore [ConstraintViolationException: Validation failed for classes [it.test.server.model.accounts.Contact] during update time for groups [javax.validation.groups.Default, ]\nList of constraint violations:[\n\tConstraintViolationImpl{interpolatedMessage='{contact.invalid.taxcode}', propertyPath=taxCode, rootBeanClass=class it.test.server.model.accounts.Contact, messageTemplate='{contact.invalid.taxcode}'}\n]].","path":"/api/v1/contacts/5752","errorCode":null}
这是错误的。第一种情况是从 @ExceptionHandler(DataIntegrityViolationException.class)
的方法捕获异常,第二种情况是从 handleHttpMessageNotReadable()
方法捕获异常。
您有什么提示可以指出我解决问题的正确方法吗?
最佳答案
您可以使用 ProblemHandling
,它扩展了 ValidationAdviceTrait
,它使用 MethodArgumentNotValidAdviceTrait
、ConstraintViolationAdviceTrait
。您可以使用 ProblemBuilder 自定义消息,其中包含标题、状态代码、详细信息等。您还可以使用 handleException(ExceptionClass ex, NativeWebRequest request) 处理其他异常
.它与POST和PATCH两种方法都可以正常工作。
@ControllerAdvice
public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
private static final String FIELD_ERRORS_KEY = "fieldErrors";
private static final String MESSAGE_KEY = "message";
private static final String PATH_KEY = "path";
private static final String VIOLATIONS_KEY = "violations";
@Value("${jhipster.clientApp.name}")
private String applicationName;
private final Logger log = LoggerFactory.getLogger(ExceptionTranslator.class);
/**
* Post-process the Problem payload to add the message key for the front-end if needed.
*/
@Override
public ResponseEntity<Problem> process(@Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
log.debug("process invalid input(s) for entity: {}, request: {}",entity,request);
List<String> messages = new ArrayList<>();
String messageStr = "";
if (entity == null) {
return entity;
}
Problem problem = entity.getBody();
try {
((ConstraintViolationProblem) problem).getViolations().forEach(m-> messages.add(m.getMessage()));
messageStr = messages.toString();
log.debug("Error message: {}",messageStr);
} catch (ClassCastException e) {
log.debug("Cannot cast Problem to ConstraintViolationProblem");
messageStr = problem.getDetail();
log.debug("Error message detail: {}",messageStr);
}
if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) {
return entity;
}
ProblemBuilder builder = Problem.builder()
.withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType())
.withStatus(problem.getStatus())
.withTitle(problem.getTitle())
.withDetail(messageStr)
.with(PATH_KEY, request.getNativeRequest(HttpServletRequest.class).getRequestURI());
if (problem instanceof ConstraintViolationProblem) {
builder
.with(VIOLATIONS_KEY, ((ConstraintViolationProblem) problem).getViolations())
.with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION)
.withDetail(messageStr);
} else {
builder
.withCause(((DefaultProblem) problem).getCause())
.withDetail(messageStr)
.withInstance(problem.getInstance());
problem.getParameters().forEach(builder::with);
if (!problem.getParameters().containsKey(MESSAGE_KEY) && problem.getStatus() != null) {
builder.with(MESSAGE_KEY, "error.http." + problem.getStatus().getStatusCode()).withDetail(messageStr);
}
}
return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
}
@Override
public ResponseEntity<Problem> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
log.debug("Handle invalid method arguments: {}",ex.toString());
BindingResult result = ex.getBindingResult();
List<FieldErrorVM> fieldErrors = result.getFieldErrors().stream()
.map(f -> new FieldErrorVM(f.getObjectName().replaceFirst("DTO$", ""), f.getField(), f.getCode()))
.collect(Collectors.toList());
List<String> messages = new ArrayList<>();
fieldErrors.forEach(m -> messages.add("Please provide a valid value for " + m.getField()));
log.debug("Error message: {}", messages);
Problem problem = Problem.builder()
.withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
.withTitle("Method argument not valid")
.withStatus(defaultConstraintViolationStatus())
.with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION)
.with(FIELD_ERRORS_KEY, fieldErrors)
.withDetail(messages.toString())
.build();
return create(ex, problem, request);
}
}
<小时/>
POST、PATCH 的响应如下
{
"type": "https://www.jhipster.tech/problem/constraint-violation",
"title": "Method argument not valid",
"status": 400,
"detail": "[Please provide a valid value for category]",
"path": "/api/categories",
"message": "error.validation",
"fieldErrors": [
{
"objectName": "botCategory",
"field": "category",
"message": "Pattern"
}
]
}
<小时/>
@PatchMapping("/endpoint")
public ResponseEntity<yourResponseDTO> create(@Valid @RequestBody yourRequestDTO requestDTO)
{...}
@PostMapping("/endpoint")
public ResponseEntity<yourResponseDTO> update(@Valid @RequestBody yourRequestDTO requestDTO)
{...}
public class yourRequestDTO {
private Long id;
@NotNull(message = ErrorConstants.INVALID_NAME)
@Size(max = 50, message = ErrorConstants.INVALID_SIZE_50)
@Pattern(regexp = Constants.BOTCATEGORY_REGEX, message = ErrorConstants.INVALID_BOT_CATEGORY_PATTERN)
private String category;
@Size(max = 100, message = ErrorConstants.INVALID_SIZE_100)
private String description;
}
相关导入
import io.github.jhipster.web.util.HeaderUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.NativeWebRequest;
import org.zalando.problem.DefaultProblem;
import org.zalando.problem.Problem;
import org.zalando.problem.ProblemBuilder;
import org.zalando.problem.Status;
import org.zalando.problem.spring.web.advice.ProblemHandling;
import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait;
import org.zalando.problem.violations.ConstraintViolationProblem;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.PersistenceException;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
关于java - Bean 验证 (JSR-303) 错误未使用 Spring DATA REST 序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59655160/
我已经使用 vue-cli 两个星期了,直到今天一切正常。我在本地建立这个项目。 https://drive.google.com/open?id=0BwGw1zyyKjW7S3RYWXRaX24tQ
您好,我正在尝试使用 python 库 pytesseract 从图像中提取文本。请找到代码: from PIL import Image from pytesseract import image_
我的错误 /usr/bin/ld: errno: TLS definition in /lib/libc.so.6 section .tbss mismatches non-TLS reference
我已经训练了一个模型,我正在尝试使用 predict函数但它返回以下错误。 Error in contrasts<-(*tmp*, value = contr.funs[1 + isOF[nn]])
根据Microsoft DataConnectors的信息我想通过 this ODBC driver 创建一个从 PowerBi 到 PostgreSQL 的连接器使用直接查询。我重用了 Micros
我已经为 SoundManagement 创建了一个包,其中有一个扩展 MediaPlayer 的类。我希望全局控制这个变量。这是我的代码: package soundmanagement; impo
我在Heroku上部署了一个应用程序。我正在使用免费服务。 我经常收到以下错误消息。 PG::Error: ERROR: out of memory 如果刷新浏览器,就可以了。但是随后,它又随机发生
我正在运行 LAMP 服务器,这个 .htaccess 给我一个 500 错误。其作用是过滤关键字并重定向到相应的域名。 Options +FollowSymLinks RewriteEngine
我有两个驱动器 A 和 B。使用 python 脚本,我在“A”驱动器中创建一些文件,并运行 powerscript,该脚本以 1 秒的间隔将驱动器 A 中的所有文件复制到驱动器 B。 我在 powe
下面的函数一直返回这个错误信息。我认为可能是 double_precision 字段类型导致了这种情况,我尝试使用 CAST,但要么不是这样,要么我没有做对...帮助? 这是错误: ERROR: i
这个问题已经有答案了: Syntax error due to using a reserved word as a table or column name in MySQL (1 个回答) 已关闭
我的数据库有这个小问题。 我创建了一个表“articoli”,其中包含商品的品牌、型号和价格。 每篇文章都由一个 id (ID_ARTICOLO)` 定义,它是一个自动递增字段。 好吧,现在当我尝试插
我是新来的。我目前正在 DeVry 在线学习中级 C++ 编程。我们正在使用 C++ Primer Plus 这本书,到目前为止我一直做得很好。我的老师最近向我们扔了一个曲线球。我目前的任务是这样的:
这个问题在这里已经有了答案: What is an undefined reference/unresolved external symbol error and how do I fix it?
我的网站中有一段代码有问题;此错误仅发生在 Internet Explorer 7 中。 我没有在这里发布我所有的 HTML/CSS 标记,而是发布了网站的一个版本 here . 如您所见,我在列中有
如果尝试在 USB 设备上构建 node.js 应用程序时在我的树莓派上使用 npm 时遇到一些问题。 package.json 看起来像这样: { "name" : "node-todo",
在 Python 中,您有 None单例,在某些情况下表现得很奇怪: >>> a = None >>> type(a) >>> isinstance(a,None) Traceback (most
这是我的 build.gradle (Module:app) 文件: apply plugin: 'com.android.application' android { compileSdkV
我是 android 的新手,我的项目刚才编译和运行正常,但在我尝试实现抽屉导航后,它给了我这个错误 FAILURE: Build failed with an exception. What wen
谁能解释一下?我想我正在做一些非常愚蠢的事情,并且急切地等待着启蒙。 我得到这个输出: phpversion() == 7.2.25-1+0~20191128.32+debian8~1.gbp108
我是一名优秀的程序员,十分优秀!