diff --git a/3.0/docs/deploy/getting-started.md b/3.0/docs/deploy/getting-started.md deleted file mode 100644 index a2e79bd7..00000000 --- a/3.0/docs/deploy/getting-started.md +++ /dev/null @@ -1,3 +0,0 @@ -# Getting Started with Deployments - -Coming soon. \ No newline at end of file diff --git a/3.0/docs/getting-started/cloud.md b/3.0/docs/getting-started/cloud.md index 166730c7..b4bfcaaf 100644 --- a/3.0/docs/getting-started/cloud.md +++ b/3.0/docs/getting-started/cloud.md @@ -21,5 +21,4 @@ For a detailed guide, visit [Vapor Cloud → Quick Start](https://docs.vapor. ## Other Options -Vapor can be deployed anywhere that supports Ubuntu (basically everywhere). To learn more about -deploying your code, checkout [Deploy → Getting Started](../deploy/getting-started.md) +Vapor can be deployed anywhere that supports Ubuntu (basically everywhere). Guides on deploying to other systems are coming soon (contributions welcome)! diff --git a/3.0/docs/getting-started/content.md b/3.0/docs/getting-started/content.md index 2c376b2d..b6f5b9c1 100644 --- a/3.0/docs/getting-started/content.md +++ b/3.0/docs/getting-started/content.md @@ -1,6 +1,6 @@ # Content -In Vapor 3, all content types (JSON, protobuf, FormURLEncoded, Multipart, etc) are treated the same. All you need to parse and serialize content is a `Codable` class or struct. +In Vapor 3, all content types (JSON, protobuf, URLEncodedForm, [Multipart](../multipart/getting-started.md), etc) are treated the same. All you need to parse and serialize content is a `Codable` class or struct. For this introduction, we will use JSON as an example. But keep in mind the API is the same for any supported content type. @@ -18,12 +18,9 @@ Content-Type: application/json } ``` -### Decode Request - First, create a struct or class that represents the data you expect. ```swift -import Foundation import Vapor struct LoginRequest: Content { @@ -47,20 +44,6 @@ router.post("login") { req -> Future in We use `.map(to:)` here since `req.content.decode(_:)` returns a [future](async.md). -### Other Request Types - -Since the request in the previous example declared JSON as its content type, Vapor knows to use a JSON decoder automatically. This same method would work just as well for the following request. - -```http -POST /login HTTP/1.1 -Content-Type: application/x-www-form-urlencoded - -email=user@vapor.codes&don't+look! -``` - -!!! tip - You can configure which encoders/decoders Vapor uses. Read on to learn more. - ## Response Let's take a look at how you would create the following HTTP response. @@ -75,12 +58,9 @@ Content-Type: application/json } ``` -### Encode Response - Just like decoding, first create a struct or class that represents the data that you are expecting. ```swift -import Foundation import Vapor struct User: Content { @@ -100,39 +80,10 @@ router.get("user") { req -> User in } ``` -### Other Response Types +Great job! Now you know how to encode and decode data in Vapor. -Content will automatically encode as JSON by default. You can always override which content type is used -using the `as:` parameter. +!!! tip + See [Vapor → Content](../vapor/content.md) for more in-depth information. -```swift -try res.content.encode(user, as: .formURLEncoded) -``` +The next section in this guide is [Async](async.md). -You can also change the default media type for any class or struct. - -```swift -struct User: Content { - /// See Content.defaultMediaType - static let defaultMediaType: MediaType = .formURLEncoded - - ... -} -``` - -## Configuring Content - -Use `ContentConfig` to register custom encoder/decoders for your application. These custom coders will be used anywhere you do `content.encode`/`content.decode`. - -```swift -/// Create default content config -var contentConfig = ContentConfig.default() - -/// Create custom JSON encoder -var jsonEncoder = JSONEncoder() -jsonEncoder.dateEncodingStrategy = .millisecondsSince1970 - -/// Register JSON encoder and content config -contentConfig.use(encoder: jsonEncoder, for: .json) -services.register(contentConfig) -``` diff --git a/3.0/docs/multipart/getting-started.md b/3.0/docs/multipart/getting-started.md new file mode 100644 index 00000000..86f2654e --- /dev/null +++ b/3.0/docs/multipart/getting-started.md @@ -0,0 +1,42 @@ +# Getting Started with Multipart + +Multipart ([vapor/multipart](https://github.com/vapor/multipart)) is a small package that helps you parse and serialize `multipart` encoded data. Multipart is a widely-supported encoding on the web. It's most often used for serializing web forms, especially ones that contain rich media like images. + +The Multipart package makes it easy to use this encoding by integrating directly with `Codable`. + +## Vapor + +This package is included with Vapor and exported by default. You will have access to all `Multipart` APIs when you import `Vapor`. + +```swift +import Vapor +``` + +## Standalone + +The Multipart package is lightweight, pure-Swift, and has very few dependencies. This means it can be used to work with multipart-encoded data for any Swift project—even one not using Vapor. + +To include it in your package, add the following to your `Package.swift` file. + +```swift +// swift-tools-version:4.0 +import PackageDescription + +let package = Package( + name: "Project", + dependencies: [ + ... + .package(url: "https://github.com/vapor/multipart.git", from: "3.0.0"), + ], + targets: [ + .target(name: "Project", dependencies: ["Multipart", ... ]) + ] +) +``` + +Use `import Multipart` to access the APIs. + +!!! warning + Some of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Multipart package in general. + Visit the [API Docs](https://api.vapor.codes/multipart/latest/Multipart/index.html) for Multipart-specific API info. + diff --git a/3.0/docs/multipart/overview.md b/3.0/docs/multipart/overview.md new file mode 100644 index 00000000..ce071bdd --- /dev/null +++ b/3.0/docs/multipart/overview.md @@ -0,0 +1,106 @@ +# Using Multipart + +Multipart is a widely-supported encoding on the web. It's most often used for serializing web forms, especially ones that contain rich media like images. It allows for arbitrary data to be encoded in each part thanks to a unique delimiter _boundary_ that is defined separately. This boundary is guaranteed by the client to not appear anywhere in the data. + +Multipart is a powerful encoding, however it is rarely used in its base format. Most commonly, `multipart/form-data` is used. This encoding adds a `"name"` property to each part of the multipart data. This is required for serializing web forms. For the rest of this guide, assume we are talking about `multipart/form-data` unless otherwise specified. + +!!! tip + Multipart integrates with [`Content`](https://api.vapor.codes/vapor/latest/Vapor/Protocols/Content.html) like all other encoding methods in Vapor. See [Vapor → Content](../vapor/content.md) for more information about the [`Content`](https://api.vapor.codes/vapor/latest/Vapor/Protocols/Content.html) protocol. + +Let's take a look at how to decode a `multipart/form-data`-encoded request. + +## Decode + +Most often, you will be decoding `multipart/form-data`-encoded requests from a web form. Let's take a look at what one of these requests might look like. After that, we will take a look at what the HTML form for that request would look like. + +### Request + +Here is an example `multipart/form-data`-encoded request for creating a new user. + +```http +POST /users HTTP/1.1 +Content-Type: multipart/form-data; boundary=123 + +--123 +Content-Disposition: form-data; name="name" + +Vapor +--123 +Content-Disposition: form-data; name="age" + +3 +--123 +Content-Disposition: form-data; name="image"; filename="droplet.png" + + +--123-- +``` + +You can see the multipart data uses a _boundary_ (in this case it is `"123"`) to separate the data. This will usually be a longer string. The client sending a multipart-encoded request must ensure that the boundary it supplies does not appear anywhere in the content it is sending you. That's what allows this encoding to be used to send things like files. + +### Form + +There are many ways to create a multipart-encoded request, but the most common is an HTML web form. Here is what the HTML form for this request might have looked like. + +```html +
+ + + +
+``` + +Take note of the `enctype` attribute on the `
` as well as the `file` type input. This is what allows us to send files via the web form. + +### Content + +Now let's take a look at how we would handle this request in Vapor. The first step (as always with [`Content`](https://api.vapor.codes/vapor/latest/Vapor/Protocols/Content.html)) is to create a `Codable` struct that represents the data structure. + +```swift +import Vapor + +struct User: Content { + var name: String + var age: Int + var image: Data +} +``` + +!!! tip + You can use [`File`](https://api.vapor.codes/core/latest/Core/Structs/File.html) instead of `Data` if you would also like to access the filename. + +Now that we have our `User` struct, let's decode that request! We can use the [`ContentContainer`](https://api.vapor.codes/vapor/latest/Vapor/Structs/ContentContainer.html) to do this easily. + +```swift +router.post("users") { req -> Future in + return try req.content.decode(User.self).map(to: HTTPStatus.self) { user in + print(user.name) // "Vapor" + print(user.age) // 3 + print(user.image) // Raw image data + return .ok + } +} +``` + +Now when you post the form to `/users`, you should see the information printed in the console. Nice work! + +## Encode + +APIs encode multipart data much less often than they decode it. However, encoding is just as easy with Vapor. Using our same `User` struct from the previous example, here is how we can encode a multipart-encoded response. + +```swift +router.get("multipart") { req -> User in + let res = req.makeResponse() + let user = User(name: "Vapor", age: 3, image: Data(...)) + res.content.encode(user, as: .formData) + return user +} +``` + +!!! tip + If you set a default `MediaType` on your `Content` types, then you can return them directly in the route closure. + +## Parsing & Serializing + +The Multipart package also offers APIs for parsing and serializing `multipart/form-data` data without using `Codable`. Check out the [API Docs](https://api.vapor.codes/multipart/latest/Multipart/index.html) for more information on using those APIs. + diff --git a/3.0/docs/vapor/content.md b/3.0/docs/vapor/content.md new file mode 100644 index 00000000..2feb3a20 --- /dev/null +++ b/3.0/docs/vapor/content.md @@ -0,0 +1,138 @@ +# Using Content + +In Vapor 3, all content types (JSON, protobuf, URLEncodedForm, [Multipart](../multipart/getting-started.md), etc) are treated the same. All you need to parse and serialize content is a `Codable` class or struct. + +For this introduction, we will use JSON as an example. But keep in mind the API is the same for any supported content type. + +## Request + +Let's take a look at how you would parse the following HTTP request. + +```http +POST /login HTTP/1.1 +Content-Type: application/json + +{ + "email": "user@vapor.codes", + "password": "don't look!" +} +``` + +### Decode Request + +First, create a struct or class that represents the data you expect. + +```swift +import Foundation +import Vapor + +struct LoginRequest: Content { + var email: String + var password: String +} +``` + +Then simply conform this struct or class to `Content`. +Now we are ready to decode that HTTP request. + +```swift +router.post("login") { req -> Future in + return req.content.decode(LoginRequest.self).map(to: HTTPStatus.self) { loginRequest in + print(loginRequest.email) // user@vapor.codes + print(loginRequest.password) // don't look! + return .ok + } +} +``` + +We use `.map(to:)` here since `req.content.decode(_:)` returns a [future](../async/getting-started.md). + +### Other Request Types + +Since the request in the previous example declared JSON as its content type, Vapor knows to use a JSON decoder automatically. This same method would work just as well for the following request. + +```http +POST /login HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +email=user@vapor.codes&don't+look! +``` + +!!! tip + You can configure which encoders/decoders Vapor uses. Read on to learn more. + +## Response + +Let's take a look at how you would create the following HTTP response. + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "name": "Vapor User", + "email": "user@vapor.codes" +} +``` + +### Encode Response + +Just like decoding, first create a struct or class that represents the data that you are expecting. + +```swift +import Foundation +import Vapor + +struct User: Content { + var name: String + var email: String +} +``` + +Then just conform this struct or class to `Content`. Now we are ready to encode that HTTP response. + +```swift +router.get("user") { req -> User in + return User( + name: "Vapor User", + email: "user@vapor.codes" + ) +} +``` + +### Other Response Types + +Content will automatically encode as JSON by default. You can always override which content type is used +using the `as:` parameter. + +```swift +try res.content.encode(user, as: .formURLEncoded) +``` + +You can also change the default media type for any class or struct. + +```swift +struct User: Content { + /// See Content.defaultMediaType + static let defaultMediaType: MediaType = .formURLEncoded + + ... +} +``` + +## Configuring Content + +Use `ContentConfig` to register custom encoder/decoders for your application. These custom coders will be used anywhere you do `content.encode`/`content.decode`. + +```swift +/// Create default content config +var contentConfig = ContentConfig.default() + +/// Create custom JSON encoder +var jsonEncoder = JSONEncoder() +jsonEncoder.dateEncodingStrategy = .millisecondsSince1970 + +/// Register JSON encoder and content config +contentConfig.use(encoder: jsonEncoder, for: .json) +services.register(contentConfig) +``` \ No newline at end of file diff --git a/3.0/docs/vapor/getting-started.md b/3.0/docs/vapor/getting-started.md new file mode 100644 index 00000000..a4624d3f --- /dev/null +++ b/3.0/docs/vapor/getting-started.md @@ -0,0 +1,28 @@ +# Getting Started with Vapor + +Check out the main [Getting Started](../getting-started/hello-world.md) guide which covers Vapor specifically. This page is here mostly for consistency with the rest of the packages. + +More in-depth information on the APIs included in Vapor, see the sub-sections to the left. + +## Package + +If you don't want to use one of Vapor's templates to get started, you can always include the framework manually. + +```swift +// swift-tools-version:4.0 +import PackageDescription + +let package = Package( + name: "Project", + dependencies: [ + ... + .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"), + ], + targets: [ + .target(name: "Project", dependencies: ["Vapor", ... ]) + ] +) +``` + +Use `import Vapor` to access the APIs. + diff --git a/3.0/mkdocs.yml b/3.0/mkdocs.yml index 193f2adf..8f5afa1b 100644 --- a/3.0/mkdocs.yml +++ b/3.0/mkdocs.yml @@ -37,8 +37,6 @@ pages: - 'Ciphers': 'crypto/ciphers.md' - 'Asymmetric': 'crypto/asymmetric.md' - 'Random': 'crypto/random.md' -- 'Deploy': - - 'Getting Started': 'deploy/getting-started.md' - 'Fluent': - 'Getting Started': 'fluent/getting-started.md' - 'Models': 'fluent/models.md' @@ -57,6 +55,9 @@ pages: - 'Logging': - 'Getting Started': 'logging/getting-started.md' - 'Overview': 'logging/overview.md' +- 'Multipart': + - 'Getting Started': 'multipart/getting-started.md' + - 'Overview': 'multipart/overview.md' - 'MySQL': - 'Getting Started': 'mysql/getting-started.md' - 'Fluent MySQL': 'mysql/fluent.md' @@ -91,6 +92,9 @@ pages: - 'Validation': - 'Getting Started': 'validation/getting-started.md' - 'Overview': 'validation/overview.md' +- 'Vapor': + - 'Getting Started': 'vapor/getting-started.md' + - 'Content': 'vapor/content.md' - 'WebSocket': - 'Getting Started': 'websocket/websocket.md' - 'Version (3.0-rc)':