Как я могу разобрать успех или выбросить ошибку с HttpBuilder-ng

Я создаю отличный клиент для моего java-приложения для автоматизации тестирования. Первоначально я написал сервис в httpBuilder, но не мог понять, как разобрать ответ. На не 200 ответах я получил исключение, которое я мог поймать и подтвердить в сообщении. Не найден, неверный запрос и т. Д. После обновления я могу проанализировать ответ, но каждый раз, когда я получаю ответ, отличный от 200, он пытается проанализировать его как мой объект, который затем выдает бесполезное исключение "missingProperty". Документ показывает, как анализировать ответ, используя response.parser <CONTENT_TYPE>, { config, fs ->...}и как переходить по коду статуса используя response.success{fs -> ...}, или же response.when(<CODE>){fs -> ...}, но не как разбирать только на успех и использовать другую логику на провал. Мой текущий код выглядит следующим образом:

import groovyx.net.http.ChainedHttpConfig
import groovyx.net.http.FromServer
import groovyx.net.http.HttpBuilder
import groovyx.net.http.NativeHandlers

import static groovyx.net.http.ContentTypes.JSON
import static groovyx.net.http.NativeHandlers.Parsers.json

class CarClient {

    private final HttpBuilder http

    CarClient() {
        http = HttpBuilder.configure {
            request.uri = "localhost:8080"
            request.encoder JSON, NativeHandlers.Encoders.&json
        }
    }

    List<Car> getCars(make) {
        http.get(List) {
            request.uri.path = "/cars/make/${make}"
            response.failure { fs ->
                println("request failed: ${fs}")
            }
            response.parser JSON, { ChainedHttpConfig config, FromServer fs ->
                json(config, fs).collect { x -> new Car(make: x."make", model: x."model") }
            }
        }
    }
}

class Car {
    def make
    def model
}

тогда мои спок тесты:

def "200 response should return list of cars"() {
  when:
  def result = client.getCars("honda")
  then:
  result.size == 3
  result[0].make == "honda"
  result[0].model == "accord"
}

def "404 responses should throw exception with 'not found'"() {
  when:
  client.getCars("ford")
  then:
  final Exception ex = thrown()
  ex.message == "Not Found"
}

В старой версии первый тест не пройден, а второй пройден. В новой версии первый тест проходит, а второй тест не проходит. Я никогда не вижу request failed:... сообщение тоже. я просто получаю groovy.lang.MissingPropertyException, когда я прохожу, я вижу, как он пытается загрузить not found ответ как объект автомобиля.

Бонус: почему я должен использовать явное сопоставление свойств, а не приведение к groovy, как в документе?

json(config, fs).collect { x -> x as Car }

обновление - для ясности, это не мой фактический источник. Я использую собственный внутренний API-интерфейс, работающий на WAS, который я не полностью контролирую. Я пишу бизнес-логику API, но ответ маршалируется / разбирается с использованием WAS и проприетарных библиотек, к которым у меня нет доступа. Имена были изменены, чтобы защитить невинных / мою работу. Это обходные пути, которые я пробовал со времени первого поста: это корректно вызывает блокировку сбоя на не-200 ответах, но синтаксический анализ завершается неудачно с IO - stream closed ошибка. Кроме того, любые исключения, которые я выбрасываю в блоке сбоя, оборачиваются в исключение RuntimeException, которое не позволяет мне получить доступ к информации. Я попытался обернуть его в исключение транспорта, как предложено в документе, но к тому времени, когда я его получаю, это все еще исключение RuntimeException.

    List<Car> getCars(make) {
        http.get(List) {
            request.uri.path = "/cars/make/${make}"
            response.failure { fs ->
              println("request failed: ${fs}")
              throw new AutomationException("$fs.statusCode : $fs.message")
            }
            response.success { FromServer fs ->
               new JsonSlurper().parse(new InputStreamReader(fs.getInputStream, fs.getCharset())).collect { x -> new Car(make: x."make", model: x."model") }
            }
        }
    }
}

Это правильно анализирует 200 ответов с записями, 200 без записей по-прежнему выбрасывает исключения из отсутствующих свойств. Как и предыдущий impl, AutomationException обернут и поэтому бесполезен.

    List<Car> getCars(make) {
        http.get(List) {
            request.uri.path = "/cars/make/${make}"
            response.parser JSON, { ChainedHttpConfig config, FromServer fs ->
            if (fs.statusCode == 200) { 
                json(config, fs).collect { x -> new Car(make: x."make", model: x."model") }
            } else {
              throw new AutomationException("$fs.statusCode : $fs.message")
            }
        }
    }
}

Что касается бонуса, руководство, за которым я следовал, показало неявное приведение json(config, fs) вывод на объект Car. Я должен явно установить реквизит нового объекта. Ничего страшного, но это заставляет меня задуматься, неправильно ли я что-то настроил.

1 ответ

Вы можете бросить исключение в failure обработчик, и он будет делать то, что вы ищете:

response.failure { fs ->
    throw new IllegalStateException('No car found')
}

Я не уверен, с каким сервером вы тестируете, поэтому я написал тест с использованием Ersatz:

import com.stehno.ersatz.ErsatzServer
import spock.lang.AutoCleanup
import spock.lang.Specification

import static com.stehno.ersatz.ContentType.APPLICATION_JSON

class CarClientSpec extends Specification {

    @AutoCleanup('stop')
    private final ErsatzServer server = new ErsatzServer()

    def 'successful get'() {
        setup:
        server.expectations {
            get('/cars/make/Toyota').responder {
                content '[{"make":"Toyota","model":"Corolla"}]', APPLICATION_JSON
            }
        }

        CarClient client = new CarClient(server.httpUrl)

        when:
        List<Car> cars = client.getCars('Toyota')

        then:
        cars.size() == 1
        cars.contains(new Car('Toyota', 'Corolla'))
    }

    def 'failed get'() {
        setup:
        server.expectations {
            get('/cars/make/Ford').responds().code(404)
        }

        CarClient client = new CarClient(server.httpUrl)

        when:
        client.getCars('Ford')

        then:
        def ex = thrown(IllegalStateException)
        ex.message == 'No car found'
    }
}

Обратите внимание, что мне нужно, чтобы ваш клиент имел настраиваемую базовую ссылку (и Car нужен @Canonical аннотация). Если вы еще не читали сообщение в блоге о REST с HttpBuilder-NG и Ersatz, я бы посоветовал сделать это, поскольку он дает хороший обзор.

Я не уверен, что вы имеете в виду в своем бонусном вопросе.

Другие вопросы по тегам