Vapor 3 Beta示例端点请求

Cof*_*ime 8 beta server-side swift vapor

我试图找到一个简单的例子,说明一个人如何在路由器中向蒸汽样本端点发送请求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

}
Run Code Online (Sandbox Code Playgroud)

我的目标是从端点获取一些东西,将其转换为结构或类,并在叶子视图中显示它.

{"array":[0,1,2,3],"dict":{"lang":"Swift","name":"Vapor"},"number":123,"string":"test"}
Run Code Online (Sandbox Code Playgroud)

这是我的想法,我认为它是如何完成但我不明白如何处理响应和处理结构,以便我可以在我的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())

}
Run Code Online (Sandbox Code Playgroud)

}

Joa*_*isO 22

示例代码

我强烈建议你阅读下面的解释,但这是代码.

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)
}
Run Code Online (Sandbox Code Playgroud)

期货

A 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)")
}
Run Code Online (Sandbox Code Playgroud)

如果我们使用有效的十进制整数格式化字符串完成承诺,就像"4"它将打印一样integer: 4

promise.complete("4")
Run Code Online (Sandbox Code Playgroud)

如果我们在那里放置任何非数字字符,"abc"就会在错误中抛出一个错误,InvalidNumericString这将触发该catch块.

promise.complete("abc")
Run Code Online (Sandbox Code Playgroud)

无论你做什么,从一个map或flatMap函数抛出的错误将通过其他转换透明地级联.转变未来将改变Expectation唯一,只有在成功案例中才会触发.错误案例将从"基础未来"复制到新转变的未来.

如果不是完成承诺而不是承诺,map则永远不会触发AnyError该catch块,而是在块中找到条件.

struct AnyError: Error {}
promise.fail(AnyError())
Run Code Online (Sandbox Code Playgroud)

flatMap与上面的例子非常相似.这是map尾随闭包返回Future<Expectation>而不是Expectation.

因此,如果我们将地图块重写为a 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
}
Run Code Online (Sandbox Code Playgroud)

intFuture仍然是一个Future<Int>因为递归期货将从平面Future<Future<Int>>变为平局Future<Int>.

内容

该response.content.decode位读取Content-Type并查找Decoder此Content Type 的默认值.然后,解码的结构将作为a返回Future<DecodedStruct>,在这种情况下,此结构是ExampleData.

内容异步返回的原因是因为内容可能尚未完全到达HTTP响应中.这是一个必要的抽象,因为我们可能正在接收超过100MB的文件,这可能会在可用内存少量的情况下崩溃(云)服务器.

逻辑

回到原来的路线:

  • 首先做一个客户
  • 提出要求 http://example.vapor.codes/json
  • 从Future<Response>异步中读取内容
  • 异步将结果渲染到视图中
  • 归还 Future<View>

框架将理解您将返回a Future<View>并将继续处理其他请求而不是等待结果.

收到JSON后,此请求将再次被接收并处理为您的Web浏览器将收到的响应.

Leaf建立在TemplateKit之上,它将异步等待未来.就像Vapor,Leaf和TemplateKit会很好地理解Futures,你可以传递Future而不是struct(反之亦然),如果有必要,他们会切换到另一个请求,直到未来完成.