Когда я пытаюсь получить сообщение 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.springframework.cloud.aws.messaging.listener.QueueMessageVisibility@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 выглядит следующим образом:
@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.
Я не понимаю, как можно установить заголовок contentType для сообщений SQS - я что-то упустил?
Должен ли Spring Cloud QueueMessageHandler устанавливать для strictContentTypeMatch значение true?




Загляните в ту же проблему, и я отвечу на вопрос одним из двух способов, в зависимости от того, кто создает сообщение.
Да, можно установить 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, как вы сами обнаружили.
чтобы иметь согласованный преобразователь как в методах отправки, так и в методах прослушивания:
/** Provides a deserialization template for incoming SQS messages */
@Bean
public QueueMessageHandlerFactory queueMessageHandlerFactory(MessageConverter messageConverter) {
var factory = new QueueMessageHandlerFactory();
factory.setArgumentResolvers(singletonList(new PayloadArgumentResolver(messageConverter)));
return factory;
}
/** Provides a serialization template for outgoing SQS messages */
@Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSQSAsync, MessageConverter messageConverter) {
return new QueueMessagingTemplate(amazonSQSAsync, (ResourceIdResolver) null, messageConverter);
}
/** Provides JSON converter for SQS messages */
@Bean
protected MessageConverter messageConverter(ObjectMapper objectMapper) {
var converter = new MappingJackson2MessageConverter();
converter.setObjectMapper(objectMapper);
// Serialization support:
converter.setSerializedPayloadClass(String.class);
// Deserialization support: (suppress "contentType=application/json" header requirement)
converter.setStrictContentTypeMatch(false);
return converter;
}
См. Подробности в исходном принятом ответе. Кредиты для @wrschneider.
ПРИМЕЧАНИЕ: приведенный выше пример вводит и устанавливает ObjectMapper. Это необязательно для согласованности контроллеров HTTP REST, если таковые имеются.
Импорт:
import static java.util.Collections.singletonList;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.config.QueueMessageHandlerFactory;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver;
Применение:
@SqsListener("my-queue")
public void listen(Foo payload) {
}
public void send(Foo dto) {
queueMessagingTemplate.convertAndSend(url, dto);
}
Вы исправили проблему?