2014-06-30 8 views
4

Когда я пытаюсь получить данные Amazon удостоверения, как этотSpray client - обрабатывать ответ с неожиданным типом контента как application/json?

val pipeline: HttpRequest => Future[IdentityData] = sendReceive ~> unmarshal[IdentityData] 
pipeline(Get("http://169.254.169.254/latest/dynamic/instance-identity/document")) 

с соответствующим классом случая и форматировщиком, я получаю следующее исключение

UnsupportedContentType (ожидаемый 'применение/JSON')

потому что амазонки отметьте их ответ как текст/простой тип контента. Они также не заботятся о параметре заголовка Accept. Есть ли простой способ сказать спрей-json игнорировать это на unmarshalling?

ответ

3

Если вы хотите извлечь некоторую IdentityData (который является примером класса с определенным jsonFormat) от амазонки ответа, который является действительным JSON, но с text/plain типа контекста, вы можете просто извлечь текстовые данные, анализировать его в JSON и конвертировать в ваших данных, например:

entity.asString.parseJson.convertTo(identityDataJsonFormat) 
+0

Не думайте делать это так просто. Спасибо за напоминание, чтобы проверить простые варианты :-) –

+0

После долгих расстройств это действительно помогло. Большое спасибо! –

5

После копания в распылительную списке писем я написал функцию, которая работает

def mapTextPlainToApplicationJson: HttpResponse => HttpResponse = { 
    case [email protected] HttpResponse(_, entity, _, _) => 
    r.withEntity(entity.flatMap(amazonEntity => HttpEntity(ContentType(MediaTypes.`application/json`), amazonEntity.data))) 
    case x => x 
} 

и использовали его в трубопроводе

val pipeline: HttpRequest => Future[IdentityData] = sendReceive ~> mapTextPlainToApplicationJson ~> unmarshal[IdentityData] 
pipeline(Get("http://169.254.169.254/latest/dynamic/instance-identity/document")) 

Отличная вещь здесь вы можете перехватить & изменить любой HttpResponse, если ваша функция перехвата имеет соответствующую подпись.

+0

Там нет необходимости писать свою функцию, то есть 'mapHttpResponseEntity' директива – 4lex1v

+0

А также нет необходимости в соответствии с HttpResponse объекта и применить' withEntity', потому что ему проверит ваш аргумент arg и применит его, если он отличается (включая пустой случай с сущностью), поэтому вы можете просто выполнить '_.withEntity (..)' – 4lex1v

+0

Это решение хорошо, но его можно улучшить, убедившись, что 'HttpCharset 'из исходного ответа' ContentType' сохраняется. Установите новое значение типа содержимого в ContentType (MediaTypes. \ Applicaiton/json \ ', amazonEntity.contentType.charset)' –

1

Я придумал более простое/более чистое решение @ yevgeniy-mordovkin.

def setContentType(mediaType: MediaType)(r: HttpResponse): HttpResponse = { 
    r.withEntity(HttpEntity(ContentType(mediaType), r.entity.data)) 
} 

Использование:

val pipeline: HttpRequest => Future[IdentityData] = (
     sendReceive 
    ~> setContentType(MediaTypes.`application/json`) 
    ~> unmarshal[IdentityData] 
) 
pipeline(Get("http://169.254.169.254/latest/dynamic/instance-identity/document")) 
+0

Это будет работать только в том случае, если производитель заботится о том, о чем вы просите. В моем случае это не так. –

+0

Я думаю, что вы неправильно читаете мой пост. Я делаю то же самое, что и вы (установите тип содержимого _after_, получив ответ), только код немного чище. Малое улучшение, ничего радикального. – vadipp