Spring Cloud @SqsListener MessageConversionException: невозможно преобразовать из [java.lang.String] для GenericMessage

При попытке получить сообщение SQS я вижу следующее исключение:

org.springframework.messaging.converter.MessageConversionException: 

Cannot convert from [java.lang.String] to [com.example.demo.Foo] for GenericMessage [payload={}, headers={LogicalResourceId=my-queue, ApproximateReceiveCount=1, SentTimestamp=1529021258825, ReceiptHandle=xxxx, Visibility=org.spri[email protected]47ce6922, SenderId=xxxx, lookupDestination=my-queue, ApproximateFirstReceiveTimestamp=1529021264456, MessageId=xxxx}]
    at org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver.resolveArgument(PayloadArgumentResolver.java:144)
    at org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:116)
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:137)
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:109)
    at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMatch(AbstractMethodMessageHandler.java:515)
    at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessageInternal(AbstractMethodMessageHandler.java:473)
    at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessage(AbstractMethodMessageHandler.java:409)
    at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer.executeMessage(SimpleMessageListenerContainer.java:205)
    at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer$MessageExecutor.run(SimpleMessageListenerContainer.java:342)
    at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer$SignalExecutingRunnable.run(SimpleMessageListenerContainer.java:397)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Код Spring Boot выглядит следующим образом:

@Configuration
@EnableSqs
public class AmazonSqsConfiguration {

    @Bean
    public AmazonSQS amazonSQSAsync() {
        return AmazonSQSAsyncClientBuilder.standard()
            .withRegion(Regions.US_WEST_2)
            .build();
    }
}

@Service
public class MyService {

    // Throws MessageConversionException
    @SqsListener("my-queue")
    public void listen(Foo payload) {

    }

    // Works fine
    @SqsListener("my-queue")
    public void listen(String payload) {

    }
}

Я использую org.springframework.cloud:spring-cloud-aws-messaging:2.0.0.RC2

У меня есть библиотеки Jackson 2 на моем пути к классам, поэтому PayloadArgumentResolver пытается использовать MappingJackson2MessageConverter для десериализации полезной нагрузки моего сообщения. Однако поскольку в сообщении SQS отсутствует заголовок contentType, а для strictContentTypeMatch установлено значение true, canConvertFrom возвращает значение false.

https://github.com/spring-projects/spring-framework/blob/f5e8f4983f7653169f3da8a3287499fce93cadd4/spring-messaging/src/main/java/org/springframework/messaging/converter/AbstractMessageConverter.java#L237

Я не вижу, как можно установить заголовок contentType для сообщений SQS - я что-то упустил?

Должно ли Spring Cloud QueueMessageHandler устанавливать для strictContentTypeMatch значение true?

https://github.com/spring-cloud/spring-cloud-aws/blob/6a7c3c31709d4239131b27936de29385df414d41/spring-cloud-aws-messaging/src/main/java/org/springframework/cloud/aws/messaging/listener/QueueMessageHandler.java#L217

Ответ 1

Столкнулся с той же проблемой, и я бы ответил на вопрос одним из двух способов в зависимости от того, кто генерирует сообщение

  • .Да, в сообщении можно установить contentType, и если вы управляете генерируемыми сообщениями, это предпочтительнее. В консоли AWS, когда вы отправляете сообщение вручную, есть вкладка для "атрибутов сообщения". Вы бы добавили атрибут с именем contentType и значением application/json. Вызовы AWS SDK должны позволять вам делать то же самое из кода приложения.

  • Для сообщений, которые AWS генерирует без определенного типа содержимого, например, события S3, вам действительно необходимо установить для strictContentMatch значение false. Это задокументировано здесь: http://cloud.spring.io/spring-cloud-static/spring-cloud-aws/2.0.0.RELEASE/multi/multi__messaging.html#_consuming_aws_event_messages_with_amazon_sqs

Документ вводит в заблуждение, потому что в нем написано "без заголовка MIME-типа", но фактическое имя заголовка - contentType, как вы узнали сами.