java Spring -在RabbitMQ listener中验证传入消息

ufj5ltwl  于 2023-05-05  发布在  Java
关注(0)|答案(3)|浏览(140)

使用Sping 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注解不起作用,并且在调用onAccountRequested方法之前没有验证CreateAccountRequestMessage对象。

t3psigkw

t3psigkw1#

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

eblbsuwk

eblbsuwk2#

我也有同样的问题。@Praveer的答案除了SmartValidator之外效果都很好。我在这里发布我的解决方案,它的灵感来自这篇文章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());
    }
}
x9ybnkn6

x9ybnkn63#

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

import jakarta.validation.constraints.NotBlank;

public class ValidatedBook {
    @NotBlank
    public String name;
}

我在适当的队列上发送了如下消息:

{
"name":"asd"   # should pass
}

{
"name":""      # should fail
}

{
               # should fail
}

我给听众下了这样的定义:

import jakarta.validation.Valid;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

@Component
@Validated
public class QueueReceiver {

    @RabbitListener(queues = "queue", errorHandler = "errorHandler")
    public void receive(@Valid @Payload ValidatedBook book) {}
}

未验证ValidatedBook对象,消息始终通过验证。
我检查了验证是否应该通过手动编写的验证抛出异常:

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    Validator validator = factory.getValidator();
    Set<ConstraintViolation<ValidatedBook>> things = validator.validate(book);

当name为空或null时,Book就是无效的,而@Valid什么也没做!
Solution我的问题是在类级别添加额外的注解@Validated

@Component
@Validated
public class QueueReceiver {
 @RabbitListener(queues = "queue", errorHandler = "errorHandler")
    void receiver(@Valid @Payload ValidatedBook object) {}
}

现在应该失败的消息-使用ConstraintViolationException(不输入方法)失败,我可以很容易地在ErrorHandler类(* implements RabbitListenerErrorHandler)中捕获异常。

相关问题