我试图找到一个简单的例子,说明一个人如何在路由器中向蒸汽样本端点http://example.vapor.codes/json
发送请求,接收响应并将其映射到结构或类。
我已经在其他地方看到了Vapor 2的例子,但它们与Vapor 3不再相关,目前的Vapor 3 beta文档还不清楚。
就像是...
router.get("sample") { req in
//1. create client
//2. send get request to sample endpoint at http://example.vapor.codes/json
//3. handle response and map to a struct or class
}
我的目标是从端点上抓取一些东西,将其转换为结构或类,并在叶子视图中显示它。
{"array":[0,1,2,3],"dict":{"lang":"Swift","name":"Vapor"},"number":123,"string":"test"}
这是我的想法,我认为它是如何完成但我不明白如何处理响应和处理结构,以便我可以在我的home.leaf中使用它的html(我不关心叶子部分假设我已经完成了所有这些的配置并且已经导入了)。
router.get("example"){ req -> Future<View> in
struct ExampleData: Codable {
var array : [Int]
var dict : [String : String]
}
return try req.make(Client.self).get("http://example.vapor.codes/json").flatMap(to: ExampleData.self) { res in
//not sure what to do to set the values of the ExampleData
}
return try req.view().render("home", ExampleData())
}
}
我强烈建议你阅读下面的解释,但这是代码。
struct ExampleData: Codable {
var array : [Int]
var dict : [String : String]
}
// Register a GET /example route
router.get("example") { req -> Future<View> in
// Fetch an HTTP Client instance
let client = try req.make(Client.self)
// Send an HTTP Request to example.vapor.codes/json over plaintext HTTP
// Returns `Future<Response>`
let response = client.get("http://example.vapor.codes/json")
// Transforms the `Future<Response>` to `Future<ExampleData>`
let exampleData = response.flatMap(to: ExampleData.self) { response in
return response.content.decode(ExampleData.self)
}
// Renders the `ExampleData` into a `View`
return try req.view().render("home", exampleData)
}
Future<Expectation>
是围绕Expectation
的包装纸。期望可能成功或失败(带有错误)。
Future类型可以注册成功完成时执行的回调。我们在这里使用的其中一个回调是flatMap
。我们先来看看常规的map
。
如果你map
一个未来你改变未来的成功Expectation
并透明地通过错误条件。
let promise = Promise<String>()
let stringFuture = promise.future // Future<String>
let intFuture = stringFuture.map(to: Int.self) { string -> Int in
struct InvalidNumericString: Error {}
guard let int = Int(string) else { throw InvalidNumericString() }
return int // Int
}
intFuture.do { int in
print("integer: ", int)
}.catch { error in
print("error: \(error)")
}
如果我们使用有效的十进制整数格式字符串完成承诺,如"4"
,它将打印integer: 4
promise.complete("4")
如果我们在那里放置任何非数字字符,如"abc"
,它将在InvalidNumericString
错误中抛出一个错误,该错误将触发catch
块。
promise.complete("abc")
无论你做什么,从map
或flatMap
函数抛出的错误将通过其他转换透明地级联。改造未来只会改变Expectation
,只会在成功案例中触发。错误案例将从“基础未来”复制到新转变的未来。
如果没有完成承诺你没有承诺,map
块将永远不会被触发,而AnyError
条件将在catch
块中找到。
struct AnyError: Error {}
promise.fail(AnyError())
flatMap
与上面的例子非常相似。这是一个map
,尾随封闭返回Future<Expectation>
而不是Expectation
。
因此,如果我们将地图块重写为flatMap
,虽然不切实际,但我们最终会得到:
let intFuture = stringFuture.flatMap(to: Int.self) { string -> Future<Int> in
struct InvalidNumericString: Error {}
guard let int = Int(string) else { throw InvalidNumericString() }
return Future(int) // Int
}
intFuture
仍然是一个Future<Int>
,因为递归期货将从Future<Future<Int>>
扁平化到Future<Int>
。
response.content.decode
位读取Content-Type并查找此内容类型的默认Decoder
。然后,解码后的结构将作为Future<DecodedStruct>
返回,在这种情况下,此结构为ExampleData
。
异步返回内容的原因是因为内容可能尚未完全到达HTTP响应中。这是一个必要的抽象,因为我们可能正在接收超过100MB的文件,这可能会在可用内存少量的情况下崩溃(云)服务器。
回到原来的路线:
http://example.vapor.codes/json
提出请求Future<Response>
中的内容Future<View>
该框架将理解您将返回Future<View>
并将继续处理其他请求而不是等待结果。
收到JSON后,此请求将再次被接收并处理为您的Web浏览器将收到的响应。
Leaf建立在TemplateKit之上,它将异步等待未来。就像Vapor,Leaf和TemplateKit会很好地理解Futures,你可以传递Future而不是struct(反之亦然),如果有必要,他们会切换到另一个请求,直到未来完成。