mirror of https://github.com/vapor/docs.git
update routing
This commit is contained in:
parent
fe27e04af4
commit
67ee447ae1
|
|
@ -29,6 +29,12 @@ menu:
|
||||||
guide:
|
guide:
|
||||||
name: Guide
|
name: Guide
|
||||||
items:
|
items:
|
||||||
|
guide-droplet:
|
||||||
|
text: Droplet
|
||||||
|
relativeUrl: guide/droplet.html
|
||||||
|
guide-folder-structure:
|
||||||
|
text: Folder Structure
|
||||||
|
relativeUrl: guide/folder-structure.html
|
||||||
guide-routing:
|
guide-routing:
|
||||||
text: Routing
|
text: Routing
|
||||||
relativeUrl: guide/routing.html
|
relativeUrl: guide/routing.html
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export PATH="$SWIFTENV_ROOT/bin:$PATH"
|
||||||
eval "$(swiftenv init -)"
|
eval "$(swiftenv init -)"
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: macOS uses `~/.bash_profile` and Ubuntu uses `~/.bashrc`
|
> Note: macOS uses `~/.bash_profile` and Ubuntu uses `~/.bashrc`
|
||||||
|
|
||||||
### Verify
|
### Verify
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ Run the following script to install the [Toolbox](https://github.com/qutheory/to
|
||||||
curl -sL toolbox.qutheory.io | bash
|
curl -sL toolbox.qutheory.io | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: You must have the correct version of Swift 3 installed.
|
> Note: You must have the correct version of Swift 3 installed.
|
||||||
|
|
||||||
## Verify
|
## Verify
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
---
|
||||||
|
currentMenu: guide-droplet
|
||||||
|
---
|
||||||
|
|
||||||
|
# Droplet
|
||||||
|
|
||||||
|
The `Droplet` is a service container that gives you access to many of Vapor's facilities. It is responsible for registering routes, starting the server, appending middleware, and more.
|
||||||
|
|
||||||
|
## Initialization
|
||||||
|
|
||||||
|
As you have probably already seen, the only thing required to create an instance of `Droplet` is to import Vapor.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import Vapor
|
||||||
|
|
||||||
|
let drop = Droplet()
|
||||||
|
|
||||||
|
// your magic here
|
||||||
|
|
||||||
|
drop.start()
|
||||||
|
```
|
||||||
|
|
||||||
|
Creation of the `Droplet` normally happens in the `main.swift` file.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
The `environment` property contains the current environment your application is running in. Usually development, testing, or production.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
if drop.config.environment == .production {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The environment affects [Config](config.md) and [Logging](log.md). The environment is `development` by default. To change it, pass the `--env=` flag as an argument.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vapor run serve --env=production
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are in Xcode, you can pass arguments through the scheme editor.
|
||||||
|
|
||||||
|
> Note: Debug logs can reduce the number of requests your application can handle per second. Enabling the production environment can improve performance.
|
||||||
|
|
||||||
|
## Working Directory
|
||||||
|
|
||||||
|
The `workDir` property contains a path to the current working directory of the application relative to where it was started. By default, this property assumes you started the Droplet from its root directory.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
drop.workDir // "/var/www/my-project/"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can override the working directory through the `Droplet`'s initializer, or by passing the `--workdir` argument.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vapor run serve --workdir="/var/www/my-project"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Initialization
|
||||||
|
|
||||||
|
The `Droplet` has several customizable properties.
|
||||||
|
|
||||||
|
Most plugins for Vapor come with a [Provider](providers.md), these take care of configuration details for you.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Droplet(
|
||||||
|
arguments: [String]?,
|
||||||
|
workDir workDirProvided: String?,
|
||||||
|
config configProvided: Config?,
|
||||||
|
localization localizationProvided: Localization?,
|
||||||
|
server: ServerProtocol.Type?,
|
||||||
|
sessions: Sessions?,
|
||||||
|
hash: Hash?,
|
||||||
|
console: ConsoleProtocol?,
|
||||||
|
log: Log?,
|
||||||
|
client: ClientProtocol.Type?,
|
||||||
|
database: Database?,
|
||||||
|
preparations: [Preparation.Type],
|
||||||
|
providers: [Provider.Type],
|
||||||
|
initializedProviders: [Provider]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
---
|
||||||
|
currentMenu: guide-folder-structure
|
||||||
|
---
|
||||||
|
|
||||||
|
# Folder Structure
|
||||||
|
|
||||||
|
The first step to creating an awesome application is knowing where things are. If you created your project using the [Toolbox](../getting-started/toolbox.md) or from a template, you will already have the folder structure created.
|
||||||
|
|
||||||
|
If you are making a Vapor application from scratch, this will show you exactly how to set it up.
|
||||||
|
|
||||||
|
## Minimum Folder Structure
|
||||||
|
|
||||||
|
We recommend putting all of your Swift code inside of the `App/` folder. This will allow you to create subfolders in `App/` to organize your models and resources.
|
||||||
|
|
||||||
|
This works best with the Swift package manager's restrictions on how packages should be structured.
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── App
|
||||||
|
│ └── main.swift
|
||||||
|
├── Public
|
||||||
|
└── Package.swift
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Public` folder is where all publicly accessible files should go. This folder will be automatically checked every time a URL is requested that is not found in your routes.
|
||||||
|
|
||||||
|
> Note: The `FileMiddleware` is responsible for accessing files from the `Public` folder.
|
||||||
|
|
||||||
|
## Models
|
||||||
|
|
||||||
|
The `Models` folder is a recommendation of where you can put your database and other models, following the MVC pattern.
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── App
|
||||||
|
. └── Models
|
||||||
|
. └── User.swift
|
||||||
|
```
|
||||||
|
|
||||||
|
## Controllers
|
||||||
|
|
||||||
|
The `Controllers` folder is a recommendation of where you can put your route controllers, following the MVC pattern.
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── App
|
||||||
|
. └── Controllers
|
||||||
|
. └── UserController.swift
|
||||||
|
```
|
||||||
|
|
||||||
|
## Views
|
||||||
|
|
||||||
|
The `Views` folder in `Resources` is where Vapor will look when you render views.
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── App
|
||||||
|
└── Resources
|
||||||
|
└── Views
|
||||||
|
└── user.html
|
||||||
|
```
|
||||||
|
|
||||||
|
The following code would load the `user.html` file.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
drop.view("user.html")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
Vapor has a sophisticated configuration system that involves a hierarchy of configuration importance.
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── App
|
||||||
|
└── Config
|
||||||
|
└── app.json // default app.json
|
||||||
|
└── development
|
||||||
|
└── app.json // overrides app.json when in development environment
|
||||||
|
└── production
|
||||||
|
└── app.json // overrides app.json when in production environment
|
||||||
|
└── secrets
|
||||||
|
└── app.json // overrides app.json in all environments, ignored by git
|
||||||
|
```
|
||||||
|
|
||||||
|
`.json` files are structured in the `Config` folder as shown above. The configuration will be applied dependant on where the `.json` file exists in the hierarchy. Learn more in [Config](config.md).
|
||||||
|
|
||||||
|
Learn about changing environments (the `--env=` flag) in the [Droplet](droplet.md) section.
|
||||||
161
guide/routing.md
161
guide/routing.md
|
|
@ -4,8 +4,163 @@ currentMenu: guide-routing
|
||||||
|
|
||||||
# Routing
|
# Routing
|
||||||
|
|
||||||
This is how routing works.
|
Routes in Vapor can be defined in any file that has access to your instance of `Droplet`. This is usually in the `main.swift` file.
|
||||||
|
|
||||||
|
## Basic
|
||||||
|
|
||||||
|
The most basic route includes a method, path, and closure.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let drop = Droplet()
|
drop.get("welcome") { request in
|
||||||
```
|
return "Hello"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The standard HTTP methods are available including `get`, `post`, `put`, `patch`, `delete`, and `options`.
|
||||||
|
|
||||||
|
You can also use `any` to match all methods.
|
||||||
|
|
||||||
|
## Request
|
||||||
|
|
||||||
|
The first parameter passed into your route closure is an instance of [Request](request.md). This contains the method, URI, body, and more.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let method = request.method
|
||||||
|
```
|
||||||
|
|
||||||
|
When you add a parameter type, like `String.self`, the closure will be required to contain another input variable. This variable will be the same type. In this case, `String`.
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
To respond with JSON, simply wrap your data structure with `JSON(node: )`
|
||||||
|
|
||||||
|
```swift
|
||||||
|
drop.get("version") { request in
|
||||||
|
return try JSON(node: ["version": "1.0"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response Representable
|
||||||
|
|
||||||
|
All routing closures can return a [ResponseRepresentable](response.md) data structure. By default, Strings and JSON conform to this protocol, but you can add your own.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
public protocol ResponseRepresentable {
|
||||||
|
func makeResponse() throws -> Response
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
Parameters are described by passing the type of data you would like to receive.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
drop.get("hello", String.self) { request, name in
|
||||||
|
return "Hello \(name)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## String Initializable
|
||||||
|
|
||||||
|
Any type that conforms to `StringInitializable` can be used as a parameter. By default, `String` and `Int` conform to this protocol, but you can add your own.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
struct User: StringInitializable {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("users", User.self) { request, user in
|
||||||
|
return "Hello \(user.name)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Using Swift extensions, you can extend your existing types to support this behavior.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
extension User: StringInitializable {
|
||||||
|
init?(from string: String) throws {
|
||||||
|
guard let int = Int(string) else {
|
||||||
|
return nil //Will Abort.InvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let user = User.find(int) else {
|
||||||
|
throw UserError.NotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
self = user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can throw your own errors or return `nil` to throw the default error.
|
||||||
|
|
||||||
|
## Groups
|
||||||
|
|
||||||
|
Prefix a group of routes with a common string, host, or middleware using `group` and `grouped`.
|
||||||
|
|
||||||
|
### Group
|
||||||
|
|
||||||
|
Group (without the "ed" at the end) takes a closure that is passed a `GroupBuilder`.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
drop.group("v1") { v1 in
|
||||||
|
v1.get("users") { request in
|
||||||
|
// get the users
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grouped
|
||||||
|
|
||||||
|
Grouped returns a `GroupBuilder` that you can pass around.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let v1 = drop.grouped("v1")
|
||||||
|
v1.get("users") { request in
|
||||||
|
// get the users
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Middleware
|
||||||
|
|
||||||
|
You can add middleware to a group of routes. This is especially useful for authentication.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
drop.group(AuthMiddleware()) { authorized in
|
||||||
|
authorized.get("token") { request in
|
||||||
|
// has been authorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Host
|
||||||
|
|
||||||
|
You can limit the host for a group of routes.
|
||||||
|
|
||||||
|
```
|
||||||
|
drop.group(host: "qutheory.io") { qt
|
||||||
|
qt.get { request in
|
||||||
|
// only responds to requests to qutheory.io
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Chaining
|
||||||
|
|
||||||
|
Groups can be chained together.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
drop.grouped(host: "qutheory.io").grouped(AuthMiddleware()).group("v1") { authedSecureV1 in
|
||||||
|
// add routes here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom Routing
|
||||||
|
|
||||||
|
Vapor still supports traditional routing for custom use-cases or long URLs.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
drop.get("users/:user_id") { request in
|
||||||
|
request.parameters["user_id"] // String?
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<a href="https://github.com/qutheory/documentation/blob/master/CONTRIBUTING.md" class="edit">✏️ Edit</a>
|
<a href="https://github.com/qutheory/documentation/blob/master/CONTRIBUTING.md" class="edit">✎ Edit on GitHub</a>
|
||||||
{{ content | raw }}
|
{{ content | raw }}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ a:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 48px;
|
font-size: 42px;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,7 +112,7 @@ nav {
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 200px;
|
width: 216px;
|
||||||
|
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
padding-top: 96px;
|
padding-top: 96px;
|
||||||
|
|
@ -127,6 +127,7 @@ nav a {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
display: block;
|
display: block;
|
||||||
color: #777;
|
color: #777;
|
||||||
|
font-size: 12px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,9 +142,9 @@ nav .active a {
|
||||||
|
|
||||||
nav h3 {
|
nav h3 {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: #999;
|
color: #bbb;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 6px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,7 +155,7 @@ nav ul, nav ul li {
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul {
|
nav ul {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.scroll {
|
div.scroll {
|
||||||
|
|
@ -169,7 +170,7 @@ main {
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
padding: 22px;
|
padding: 22px;
|
||||||
padding-top: 110px;
|
padding-top: 110px;
|
||||||
padding-left: 222px;
|
padding-left: 240px;
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -191,15 +192,39 @@ main a.edit {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main h1 {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
main h2 {
|
main h2 {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
border-bottom: 1px dotted rgba(0, 0, 0, 0.10);
|
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
border-bottom: 1px dotted rgba(0, 0, 0, 0.10);
|
||||||
|
}
|
||||||
|
|
||||||
|
main h3 {
|
||||||
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
main p {
|
main p {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 12px;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
main blockquote {
|
||||||
|
background: rgba(247, 202, 201, 0.2);
|
||||||
|
margin-top: 18px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
margin-left: 0;
|
||||||
|
padding: 6px;
|
||||||
|
padding-left: 12px;
|
||||||
|
border-left: 5px solid #f6cfcf;
|
||||||
|
}
|
||||||
|
|
||||||
|
main blockquote p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre, code {
|
pre, code {
|
||||||
|
|
@ -209,10 +234,11 @@ pre, code {
|
||||||
|
|
||||||
p code {
|
p code {
|
||||||
background: #fbfbfb;
|
background: #fbfbfb;
|
||||||
border-radius: 10px;
|
border-radius: 5px;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: #92a0b9;
|
color: #92a0b9;
|
||||||
|
box-shadow: 0 1px 0px 0px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue