Spring - 在 RabbitMQ 侦听器中验证传入消息

问题描述 投票:0回答:3

我正在使用 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
方法之前未验证对象。

java spring validation spring-mvc rabbitmq
3个回答
8
投票

您需要在

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
将被抛出,消息将被丢弃,或者你可以将消息发送到死信交换。


2
投票

我有同样的问题。除了 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());
    }
}

0
投票

我有类似的问题。我的消息是这样定义的:

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
)。

© www.soinside.com 2019 - 2024. All rights reserved.