mirror of https://github.com/vapor/docs.git
request and response
This commit is contained in:
parent
5b254d4964
commit
054bdbdd1b
|
|
@ -71,6 +71,9 @@ menu:
|
||||||
http:
|
http:
|
||||||
name: HTTP
|
name: HTTP
|
||||||
items:
|
items:
|
||||||
guide-droplet:
|
guide-request:
|
||||||
|
text: Request
|
||||||
|
relativeUrl: http/request.html
|
||||||
|
guide-response:
|
||||||
text: Response
|
text: Response
|
||||||
relativeUrl: http/response.html
|
relativeUrl: http/response.html
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
---
|
||||||
|
currentMenu: http-request
|
||||||
|
---
|
||||||
|
|
||||||
|
# Request
|
||||||
|
|
||||||
|
The most common part of the `HTTP` library we'll be interacting with is the `Request` type. Here's a look at some of the most commonly used attributes in this type.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
public var method: Method
|
||||||
|
public var uri: URI
|
||||||
|
public var parameters: Node
|
||||||
|
public var headers: [HeaderKey: String]
|
||||||
|
public var body: Body
|
||||||
|
public var data: Content
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
The HTTP `Method` associated with the `Request`, ie: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`.
|
||||||
|
|
||||||
|
#### URI
|
||||||
|
|
||||||
|
The associated `URI` of the request. We will use this to access attributes about the `uri` the request was sent to.
|
||||||
|
|
||||||
|
For example, given the following uri: `http://vapor.codes/example?query=hi#fragments-too`
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let scheme = request.uri.scheme // http
|
||||||
|
let host = request.uri.host // vapor.codes
|
||||||
|
|
||||||
|
let path = request.uri.path // /example
|
||||||
|
let query = request.uri.query // query=hi
|
||||||
|
let fragment = request.uri.fragment // fragments-too
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
The url parameters associated with the request. For example, if we have a path registered as `hello/:name/age/:age`, we would be able to access those in our request, like so:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let name = request.parameters["name"] // String?
|
||||||
|
let age = request.parameters["age"]?.int // Int?
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, to automatically throw on `nil` or invalid variable, you can also `extract`
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let name = try request.parameters.extract("name") as String
|
||||||
|
let age = try request.parameters.extract("age") as Int
|
||||||
|
```
|
||||||
|
|
||||||
|
These extract functions can cast to any `NodeInitializable` type, including your own custom types. Make sure to check out [Node](https://github.com/vapor/node) for more info.
|
||||||
|
|
||||||
|
> Note: Vapor also provides type safe routing in the routing section of our docs.
|
||||||
|
|
||||||
|
#### Headers
|
||||||
|
|
||||||
|
These are the headers associated with the request. If you are preparing an outgoing request, this can be used to add your own keys.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let contentType = request.headers["Content-Type"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Or for outgoing requests:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let request = Request ...
|
||||||
|
request.headers["Content-Type"] = "application/json"
|
||||||
|
request.headers["Authorization"] = ... my auth token
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Extending Headers
|
||||||
|
|
||||||
|
We generally seek to improve code bases by removing stringly typed code where possible. We can add variables to the headers using generic extensions.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
extension KeyAccessible where Key == HeaderKey, Value == String {
|
||||||
|
var customKey: String? {
|
||||||
|
get {
|
||||||
|
return self["Custom-Key"]
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self["Custom-Key"] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With this pattern implemented, our string `"Custom-Key"` is contained in one section of our code. We can now access like this:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let customKey = request.headers.customKey
|
||||||
|
|
||||||
|
// or
|
||||||
|
|
||||||
|
let request = ...
|
||||||
|
request.headers.customKey = "my custom value"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Body
|
||||||
|
|
||||||
|
This is the body associated with the request and represents the general data payload. You can view more about body in the associated [docs](./body.md)
|
||||||
|
|
||||||
|
For incoming requests, we'll often pull out the associated bytes like so:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let rawBytes = request.body.bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
Generally when we're sending or receiving requests, we're using them as a way to transport content. For this, Vapor provides a convenient `data` variable associated with the request that prioritizes content in a consistent way.
|
||||||
|
|
||||||
|
For example, say I receive a request to `http://vapor.codes?hello=world`.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let world = request.data["hello"].string
|
||||||
|
```
|
||||||
|
|
||||||
|
This same code will work if I receive a JSON request, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"hello": "world"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Will still be accessible through data.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let world = request.data["hello"].string
|
||||||
|
```
|
||||||
|
|
||||||
|
This also applies to multi-part requests and can even be extended to new types such as XML or YAML through middleware.
|
||||||
|
|
||||||
|
If you'd prefer to access given types more explicitly, that's totally fine. The `data` variable is purely opt-in convenience for those who want it.
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
To access JSON directly on a given request, use the following:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let json = request.json["hello"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Query
|
||||||
|
|
||||||
|
The same applies to query convenience:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let query = request.query["hello"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Paths
|
||||||
|
|
||||||
|
Key paths work on most Vapor types that can have nested key value objects. Here's a couple examples of how to access given the following json:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": "some metadata",
|
||||||
|
"artists" : {
|
||||||
|
"href": "http://someurl.com",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "Van Gogh",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mozart"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We could access the data in the following ways:
|
||||||
|
|
||||||
|
##### Metadata
|
||||||
|
|
||||||
|
Access top level values
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let type = request.data["metadata"].string // "some metadata"
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Items
|
||||||
|
|
||||||
|
Access nested values
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let items = request.data["artists", "items"] // [["name": "Van Gogh"], ["name": "Mozart"]]
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Mixing Arrays and Objects
|
||||||
|
|
||||||
|
Get first artists
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let first = request.data["artists", "items", 0] // ["name": "Van Gogh"]
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Array Item
|
||||||
|
|
||||||
|
Get key from array item
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let firstName = request.data["artists", "items", 0, "name"] // "Van Gogh"
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Array Comprehension
|
||||||
|
|
||||||
|
We can also smartly map an array of keys, for example, to just get the names of all of the artists, we could use the following
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let names = request.data["artists", "items", "name"] // ["Van Gogh", "Mozart"]
|
||||||
|
```
|
||||||
119
http/response.md
119
http/response.md
|
|
@ -4,4 +4,121 @@ currentMenu: http-response
|
||||||
|
|
||||||
# Response
|
# Response
|
||||||
|
|
||||||
Sometimes a custom `Respons`e object needs to be created instead of using something that is `ResponseRepresentable`. For instance, if you need to manually assign cookies.
|
When building endpoints, we'll often be returning responses for requests. If we're making outgoing requests, we'll be receiving them.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
public let status: Status
|
||||||
|
public var headers: [HeaderKey: String]
|
||||||
|
public var body: Body
|
||||||
|
public var data: Content
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Status
|
||||||
|
|
||||||
|
The http status associated with the event, for example `.ok` == 200 ok.
|
||||||
|
|
||||||
|
#### Headers
|
||||||
|
|
||||||
|
These are the headers associated with the request. If you are preparing an outgoing response, this can be used to add your own keys.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let contentType = response.headers["Content-Type"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Or for outgoing response:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let response = response ...
|
||||||
|
response.headers["Content-Type"] = "application/json"
|
||||||
|
response.headers["Authorization"] = ... my auth token
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Extending Headers
|
||||||
|
|
||||||
|
We generally seek to improve code bases by removing stringly typed code where possible. We can add variables to the headers using generic extensions.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
extension KeyAccessible where Key == HeaderKey, Value == String {
|
||||||
|
var customKey: String? {
|
||||||
|
get {
|
||||||
|
return self["Custom-Key"]
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self["Custom-Key"] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With this pattern implemented, our string `"Custom-Key"` is contained in one section of our code. We can now access like this:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let customKey = response.headers.customKey
|
||||||
|
|
||||||
|
// or
|
||||||
|
|
||||||
|
let request = ...
|
||||||
|
response.headers.customKey = "my custom value"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Body
|
||||||
|
|
||||||
|
This is the body associated with the response and represents the general data payload. You can view more about body in the associated [docs](./body.md)
|
||||||
|
|
||||||
|
For responses, the body is most commonly set at initialization. With two main types.
|
||||||
|
|
||||||
|
##### BodyRepresentable
|
||||||
|
|
||||||
|
Things that can be converted to bytes, ie:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let response = Response(status: .ok, body: "some string")
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above example, the `String` will be automatically converted to a body. Your own types can do this as well.
|
||||||
|
|
||||||
|
##### Bytes Directly
|
||||||
|
|
||||||
|
If we already have our bytes array, we can pass it into the body like so:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let response = Response(status: .ok, body: .data(myArrayOfBytes))
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Chunked
|
||||||
|
|
||||||
|
To send an `HTTP.Response` in chunks, we can pass a closure that we'll use to send our response body in parts.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let response = Response(status: .ok) { chunker in
|
||||||
|
for name in ["joe", "pam", "cheryl"] {
|
||||||
|
sleep(1)
|
||||||
|
try chunker.send(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
try chunker.close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> Make sure to call `close()` before the chunker leaves scope.
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
We can access content the same we do in a [request](./request.md). This most commonly applies to outgoing requests.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let pokemonResponse = try drop.client.get("http://pokeapi.co/api/v2/pokemon/")
|
||||||
|
let names = pokemonResponse.data["results", "name"]?.array
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
To access JSON directly on a given response, use the following:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let json = request.response["hello"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Paths
|
||||||
|
|
||||||
|
For more on KeyPaths, visit [here](./request.md#key-paths)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue