Vapor 3 Beta Example Endpoint Request

半城伤御伤魂 提交于 2019-12-03 15:08:12
JoannisO

Example code

I strongly recommend you read the explaination below, but this is the code.

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)
}

Futures

A Future<Expectation> is a wrapper around the Expectation. The expectation can be successful or failed (with an Error).

The Future type can register callbacks which are executed on successful completion. One of these callbacks that we use here is flatMap. Let's dive into a regular map, first.

If you map a Future you transform the future's successful Expectation and transparently pass through error conditions.

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)")
}

If we complete the promise with a valid decimal integer formatted string like "4" it'll print integer: 4

promise.complete("4")

If we place any non-numeric characters in there like "abc" it'll throw an error inside the InvalidNumericString error which will be triggering the catch block.

promise.complete("abc")

No matter what you do, an error thrown from a map or flatMap function will cascade transparently through other transformations. Transforming a future will transform the Expectation only, and only be triggered on successful cases. Error cases will be copied from the "base future" to the newly transformed future.

If instead of completing the promise you fail the promise, the map block will never be triggered and the AnyError condition will be found in the catch block instead.

struct AnyError: Error {}
promise.fail(AnyError())

flatMap works very similarly to the above example. It's a map where the trailing closure returns a Future<Expectation> rather than Expectation.

So If we'd rewrite the map block to be a flatMap, although impractical, we'll end up with this:

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 is still a Future<Int> because the recursive futures will be flattened from Future<Future<Int>> to just Future<Int>.

Content

The response.content.decode bit reads the Content-Type and looks for the default Decoder for this Content Type. The decoded struct will then be returned as a Future<DecodedStruct>, in this case this struct is ExampleData.

The reason the content is returned asynchronously is because the content may not have completely arrived in the HTTP response yet. This is a necessary abstraction because we may be receiving files upwards of 100MB which could crash (cloud) servers with a small amount of memory available.

Logic

Back to the original route:

  • First make a client
  • Make a request to http://example.vapor.codes/json
  • Read the content from the Future<Response> asynchronously
  • Render the results into the view asynchronously
  • Return the Future<View>

The framework will understand that you're returning a Future<View> and will continue processing other requests rather than waiting on the results.

Once the JSON is received, this request will be picked up again and processed into a response which your web browser will receive.

Leaf is built on top of TemplateKit which will await the future asynchronously. Just like Vapor, Leaf and TemplateKit will understand Futures well enough that you can pass a Future instead of a struct (or vice versa) and they'll switch to anothe request until the future is completed, if necessary.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!