我正在使用 Spring Boot 框架。我想通过 RabbitMQ 将对象从一个服务发送到另一个服务,如下所示:
服务A:
rabbitTemplate.convertAndSend("queue", createAccountRequestMessage);
B服务:
@RabbitListener(queues = "queue")
public void onAccountRequested(@Valid CreateAccountRequestMessage createAccountRequestMessage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG, long tag) throws IOException
{
}
在
CreateAccountRequestMessage
类中,我定义了一些验证注释,如 @NotEmpty
、@NotNull
等,但是当我从服务 A 向服务 B 发送错误消息时,@Valid
注释不起作用并且 CreateAccountRequestMessage
在调用 onAccountRequested
方法之前未验证对象。
您需要在
DefaultMessageHandlerMethodFactory
中设置验证器。
@Autowired
SmartValidator validator;
@Bean
public DefaultMessageHandlerMethodFactory messageHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setValidator(this.validator);
return factory;
}
然后您还需要指定
@Payload
注释以及 @Valid
注释。
@RabbitListener(queues = "queue")
public void onAccountRequested(@Valid @Payload CreateAccountRequestMessage
createAccountRequestMessage, Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG, long tag) throws IOException
{
}
现在
MethodArgumentNotValidException
将被抛出,消息将被丢弃,或者你可以将消息发送到死信交换。
我有同样的问题。除了 SmartValidator 之外,@Praveer 的答案效果很好。我在这里发布我的解决方案,它的灵感来自这篇文章https://blog.trifork.com/2016/02/29/spring-amqp-payload-validation/
@Configuration
@EnableRabbit
@Slf4j
public class CmsMQConfig implements RabbitListenerConfigurer {
@Value("${dw.rabbitmq.hosts}")
private String hosts;
@Value("${dw.rabbitmq.username}")
private String username;
@Value("${dw.rabbitmq.password}")
private String password;
@Value("${dw.rabbitmq.virtual-host}")
private String virtualHost;
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setMessageConverter(messageConverter());
return factory;
}
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(hosts);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
return connectionFactory;
}
@Bean
public Jackson2JsonMessageConverter messageConverter() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return new Jackson2JsonMessageConverter(mapper);
}
@Bean
public DefaultMessageHandlerMethodFactory defaultHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setValidator(amqpValidator());
return factory;
}
@Bean
public Validator amqpValidator() {
return new OptionalValidatorFactoryBean();
}
@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setContainerFactory(rabbitListenerContainerFactory());
registrar.setMessageHandlerMethodFactory(defaultHandlerMethodFactory());
}
}
我有类似的问题。我的消息是这样定义的:
public class ValidatedBook {
@NotBlank
public String name;
}
我像这样在适当的队列上发送消息:
{
"name":"asd" # should pass
}
{
"name":"" # should fail
}
{
# should fail
}
我有这样的听众:
@RabbitListener(queues = "queue2", errorHandler = "errorHandler")
void receiver(@Valid @Payload ValidatedBook object) {}
没有验证 ValidatedBook 对象,消息总是通过验证,即使我检查验证应该抛出异常,因为发现了问题:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<ValidatedBook>> things = validator.validate(book);
事情有一个约束被破坏了。
我的问题的解决方案是在类级别添加额外的注释
@Validated
:
@Component
@Validated
public class QueueReceiver {
@RabbitListener(queues = "queue2", errorHandler = "errorHandler")
void receiver(@Valid @Payload ValidatedBook object) {}
}
现在应该失败的消息 - 以 ConstraintViolationException 失败(不要进入方法),我可以很容易地在
ErrorHandler
类中捕获异常(* implements RabbitListenerErrorHandler
)。