首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用HttpBuilder-ng解析成功或抛出错误

如何使用HttpBuilder-ng解析成功或抛出错误
EN

Stack Overflow用户
提问于 2017-11-02 05:39:49
回答 1查看 1.2K关注 0票数 1

我正在为我的java应用程序构建一个groovy rest客户端,以便与测试自动化一起使用。我最初用httpBuilder编写服务,但不知道如何解析响应。对于非200个响应,我得到了一个异常,我可以捕获它并在消息上断言。更新后,我可以解析响应,但只要我得到一个非200的响应,它就会尝试将其解析为我的对象,然后抛出一个无用的“missingProperty”异常。该文档展示了如何使用response.parser <CONTENT_TYPE>, { config, fs ->...}解析响应,以及如何使用response.success{fs -> ...}response.when(<CODE>){fs -> ...}在状态代码上进行分支,但没有说明如何只解析成功,而使用不同的逻辑解析失败。我当前的代码如下:

代码语言:javascript
复制
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
}

然后我的spock测试:

代码语言:javascript
复制
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类型转换?

代码语言:javascript
复制
json(config, fs).collect { x -> x as Car }

更新-为了澄清,这不是我的实际来源。我遇到了一个在WAS上运行的专有内部API,但我不能完全控制它。我正在编写API的业务逻辑,但响应是使用WAS和专有库进行编组/解组的,我无权访问这些库。为了保护无辜的人/我的工作,名字都被改了。这些是我从初始post开始尝试的变通方法:这会在非200响应上正确地触发失败块,但解析失败并出现IO - stream closed错误。此外,我在失败块中抛出的任何异常都会被包装在一个RuntimeException中,这将阻止我访问该信息。我试着按照文档中的建议将它包装在一个传输异常中,但当我收到它时,它仍然是一个RuntimeException。

代码语言:javascript
复制
    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是封装的,因此没有什么用处。

代码语言:javascript
复制
    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对象。我必须显式地设置新对象的道具。这不是什么大问题,但它让我怀疑我是否配置了其他错误的东西。

EN

回答 1

Stack Overflow用户

发布于 2017-11-02 21:08:59

您可以在failure处理程序中抛出一个异常,它将执行您正在寻找的操作:

代码语言:javascript
复制
response.failure { fs ->
    throw new IllegalStateException('No car found')
}

我不确定您测试的是哪台服务器,所以我使用Ersatz编写了一个测试

代码语言:javascript
复制
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'
    }
}

请注意,我必须让您的客户端拥有一个可配置的基本url (而Car需要@Canonical注释)。如果你还没有读过关于Take a REST with HttpBuilder-NG and Ersatz的博客文章,我建议你读一读,因为它提供了一个很好的概述。

我不确定你的奖金问题是什么意思。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47064107

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档