Routing¶
Routing is het proces van het vinden van de juiste request handler voor een inkomend verzoek. De kern van Vapor's routering is een krachtige, trie-node router van RoutingKit.
Overview¶
Om te begrijpen hoe routing werkt in Vapor, moet je eerst een paar basisbegrippen over HTTP verzoeken begrijpen. Kijk eens naar het volgende voorbeeld verzoek.
GET /hello/vapor HTTP/1.1
host: vapor.codes
content-length: 0
Dit is een eenvoudig GET
HTTP verzoek naar de URL /hello/vapor
. Dit is het soort HTTP verzoek dat uw browser zou maken als u hem op de volgende URL zou richten.
http://vapor.codes/hello/vapor
HTTP Method¶
Het eerste deel van het verzoek is de HTTP methode. GET
is de meest voorkomende HTTP methode, maar er zijn er meerdere die je vaak zult gebruiken. Deze HTTP methodes worden vaak geassocieerd met CRUD semantiek.
Method | CRUD |
---|---|
GET |
Lezen |
POST |
Maken |
PUT |
Vervangen |
PATCH |
Updaten |
DELETE |
Verwijderen |
Request Path¶
Direct na de HTTP methode staat de URI van het verzoek. Deze bestaat uit een pad dat begint met /
en een optionele query string na ?
. De HTTP methode en het pad zijn wat Vapor gebruikt om verzoeken te routeren.
Na de URI volgt de HTTP versie, gevolgd door nul of meer headers en tenslotte een body. Omdat dit een GET
verzoek is, heeft het geen body.
Router Methodes¶
Laten we eens kijken hoe dit verzoek in Vapor zou kunnen worden behandeld.
app.get("hello", "vapor") { req in
return "Hello, vapor!"
}
Alle gebruikelijke HTTP methodes zijn beschikbaar als methodes op Application
. Ze accepteren een of meer string argumenten die het pad van het verzoek weergeven, gescheiden door /
.
Merk op dat je dit ook zou kunnen schrijven met on
gevolgd door de methode.
app.on(.GET, "hello", "vapor") { ... }
Met deze route geregistreerd, zal het voorbeeld HTTP verzoek van hierboven resulteren in het volgende HTTP antwoord.
HTTP/1.1 200 OK
content-length: 13
content-type: text/plain; charset=utf-8
Hello, vapor!
Route Parameters¶
Nu we met succes een verzoek hebben gerouteerd op basis van de HTTP methode en het pad, laten we eens proberen het pad dynamisch te maken. Merk op dat de naam "vapor" hard gecodeerd is in zowel het pad als het antwoord. Laten we dit dynamisch maken, zodat je /hello/<elke naam>
kunt bezoeken en een antwoord kunt krijgen.
app.get("hello", ":name") { req -> String in
let name = req.parameters.get("name")!
return "Hello, \(name)!"
}
Door het gebruik van een path component voorafgegaan door :
, geven we aan de router aan dat dit een dynamische component is. Elke string die hier wordt aangeleverd zal nu overeenkomen met deze route. We kunnen dan req.parameters
gebruiken om de waarde van de string op te vragen.
Als je het voorbeeld verzoek opnieuw uitvoert, krijg je nog steeds een antwoord dat hallo zegt tegen Vapor. U kunt nu echter elke naam na /hello/
invoegen en het in het antwoord zien staan. Laten we /hello/swift
eens proberen.
GET /hello/swift HTTP/1.1
content-length: 0
HTTP/1.1 200 OK
content-length: 13
content-type: text/plain; charset=utf-8
Hello, swift!
Nu dat je de basis begrijpt, bekijk dan elke sectie om meer te leren over parameters, groepen, en meer.
Routes¶
Een route specificeert een request handler voor een gegeven HTTP methode en URI pad. Het kan ook extra metadata opslaan.
Methods¶
Routes kunnen direct worden geregistreerd in uw Application
met behulp van verschillende HTTP methode helpers.
// responds to GET /foo/bar/baz
app.get("foo", "bar", "baz") { req in
...
}
Route handlers ondersteunen het retourneren van alles dat ResponseEncodable
is. Dit is inclusief Content
, een async
closure, en elke EventLoopFuture
waar de toekomstige waarde ResponseEncodable
is.
Je kunt het return type van een route specificeren met -> T
voor in
. Dit kan handig zijn in situaties waar de compiler het return type niet kan bepalen.
app.get("foo") { req -> String in
return "bar"
}
Dit zijn de ondersteunde route helper methodes:
get
post
patch
put
delete
Naast de HTTP methode helpers, is er een on
functie die HTTP methode accepteert als een input parameter.
// reageert op OPTIONS /foo/bar/baz
app.on(.OPTIONS, "foo", "bar", "baz") { req in
...
}
Path Component¶
Elke route registratie methode accepteert een variadische lijst van PathComponent
. Dit type is uit te drukken door string literal en heeft vier gevallen:
- Constante (
foo
) - Parameter (
:foo
) - Alles (
*
) - CatchAll (
**
)
Constant¶
Dit is een statische routecomponent. Alleen verzoeken met een exact overeenkomende string op deze positie worden toegestaan.
// reageert op GET /foo/bar/baz
app.get("foo", "bar", "baz") { req in
...
}
Parameter¶
Dit is een dynamische routecomponent. Elke string op deze positie is toegestaan. Een parameter path component wordt gespecificeerd met een :
prefix. De string achter de :
wordt gebruikt als parameter naam. Je kunt de naam gebruiken om later de waarde van de parameter uit het request te halen.
// reageert op GET /foo/bar/baz
// reageert op GET /foo/qux/baz
// ...
app.get("foo", ":bar", "baz") { req in
...
}
Alles¶
Dit lijkt veel op parameter, behalve dat de waarde wordt weggegooid. Deze path component wordt gespecificeerd als alleen *
.
// reageert op GET /foo/bar/baz
// reageert op GET /foo/qux/baz
// ...
app.get("foo", "*", "baz") { req in
...
}
CatchAll¶
Dit is een dynamische routecomponent die overeenkomt met een of meer componenten. Het wordt gespecificeerd met alleen **
. Elke string op deze positie of latere posities zal worden gematched in het verzoek.
// reageert op GET /foo/bar
// reageert op GET /foo/bar/baz
// ...
app.get("foo", "**") { req in
...
}
Parameters¶
Bij gebruik van een parameter path component (voorafgegaan door :
), zal de waarde van de URI op die positie worden opgeslagen in req.parameters
. U kunt de naam van de path component gebruiken om de waarde te benaderen.
// reageert op GET /hello/foo
// reageert op GET /hello/bar
// ...
app.get("hello", ":name") { req -> String in
let name = req.parameters.get("name")!
return "Hello, \(name)!"
}
Tip
We kunnen er zeker van zijn dat req.parameters.get
hier nooit nil
zal teruggeven, omdat ons route pad :name
bevat. Echter, als u route parameters benadert in middleware of in code die getriggerd wordt door meerdere routes, zult u de mogelijkheid van nil
willen behandelen.
Tip
Als je URL query params wilt ophalen, bijvoorbeeld /hello/?name=foo
moet je Vapor's Content APIs gebruiken om URL gecodeerde data in de URL's query string te verwerken. Zie Content
referentie voor meer details.
req.parameters.get
ondersteunt ook het automatisch casten van de parameter naar LosslessStringConvertible
types.
// reageert op GET /number/42
// reageert op GET /number/1337
// ...
app.get("number", ":x") { req -> String in
guard let int = req.parameters.get("x", as: Int.self) else {
throw Abort(.badRequest)
}
return "\(int) is a great number"
}
De waarden van de door CatchAll gematchte URI (**
) worden in req.parameters
opgeslagen als [String]
. Je kunt req.parameters.getCatchall
gebruiken om toegang te krijgen tot deze componenten.
// reageert op GET /hello/foo
// reageert op GET /hello/foo/bar
// ...
app.get("hello", "**") { req -> String in
let name = req.parameters.getCatchall().joined(separator: " ")
return "Hello, \(name)!"
}
Body Streaming¶
Wanneer je een route registreert met de on
methode, kun je specificeren hoe de request body behandeld moet worden. Standaard worden request bodies in het geheugen verzameld voordat je je handler aanroept. Dit is handig omdat het mogelijk maakt om de request inhoud synchroon te decoderen, ook al leest uw applicatie inkomende requests asynchroon.
Standaard zal Vapor het verzamelen van streaming body's beperken tot 16KB in grootte. U kunt dit configureren met app.routes
.
// Verhoogt de limiet voor het verzamelen van streaming body collectie tot 500kb
app.routes.defaultMaxBodySize = "500kb"
Als een streaming body die wordt verzameld de geconfigureerde limiet overschrijdt, zal een 413 Payload Too Large
foutmelding worden gegeven.
Om de request body collectie strategie te configureren voor een individuele route, gebruik de body
parameter.
// Verzamelt streaming bodies (tot 1mb groot) voordat deze route wordt aangeroepen.
app.on(.POST, "listings", body: .collect(maxSize: "1mb")) { req in
// Verzoek afhandelen.
}
Als een maxSize
wordt doorgegeven aan collect
, zal het de standaard van de applicatie voor die route overschrijven. Om de standaard van de applicatie te gebruiken, laat je het maxSize
argument weg.
Voor grote requests, zoals file uploads, kan het verzamelen van de request body in een buffer het systeemgeheugen belasten. Om te voorkomen dat de request body wordt verzameld, kunt u de stream
strategie gebruiken.
// De inhoud van het verzoek wordt niet in een buffer verzameld.
app.on(.POST, "upload", body: .stream) { req in
...
}
Wanneer de request body wordt gestreamd, zal req.body.data
nil
zijn. Je moet req.body.drain
gebruiken om elke chunk af te handelen als deze naar je route wordt gestuurd.
Hoofdletterongevoelige routering¶
Standaard gedrag voor routing is zowel hoofdlettergevoelig als hoofdletterbehoudend. Constante
padcomponenten kunnen afwisselend hoofdletterongevoelig en hoofdletterbehoudend worden behandeld voor de routering; om dit gedrag in te schakelen, configureer dit voor het opstarten van de applicatie:
app.routes.caseInsensitive = true
Er worden geen wijzigingen aangebracht aan het oorspronkelijke verzoek; de routebehandelaars ontvangen de onderdelen van het verzoekpad zonder wijziging.
Routes Bekijken¶
U kunt de routes van uw applicatie benaderen door de Routes
service te maken of door app.routes
te gebruiken.
print(app.routes.all) // [Route]
Vapor wordt ook geleverd met een routes
commando dat alle beschikbare routes afdrukt in een ASCII geformatteerde tabel.
$ swift run App routes
+--------+----------------+
| GET | / |
+--------+----------------+
| GET | /hello |
+--------+----------------+
| GET | /todos |
+--------+----------------+
| POST | /todos |
+--------+----------------+
| DELETE | /todos/:todoID |
+--------+----------------+
Metadata¶
Alle route registratie methodes retourneren de aangemaakte Route
. Hiermee kun je metadata toevoegen aan de userInfo
dictionary van de route. Er zijn enkele standaard methodes beschikbaar, zoals het toevoegen van een beschrijving.
app.get("hello", ":name") { req in
...
}.description("says hello")
Route Groepen¶
Route groepering maakt het mogelijk om een set van routes te maken met een pad voorvoegsel of specifieke middleware. Grouperen ondersteunt een builder en closure gebaseerde syntax.
Alle groepering methoden retourneren een RouteBuilder
wat betekent dat je oneindig kunt mixen, matchen, en nesten met andere route bouw methoden.
Pad Voorvoegsel¶
Pad voorvoegsel route groepen staan u toe om een of meer path componenten aan een groep van routes te prependeren.
let users = app.grouped("users")
// GET /users
users.get { req in
...
}
// POST /users
users.post { req in
...
}
// GET /users/:id
users.get(":id") { req in
let id = req.parameters.get("id")!
...
}
Elke pad component die je kunt doorgeven aan methodes als get
of post
kan worden doorgegeven aan grouped
. Er is ook een alternatieve, closure-gebaseerde syntax.
app.group("users") { users in
// GET /users
users.get { req in
...
}
// POST /users
users.post { req in
...
}
// GET /users/:id
users.get(":id") { req in
let id = req.parameters.get("id")!
...
}
}
Door het nesten van pad voorvoegsel route groepen kunt u beknopt CRUD APIs definiëren.
app.group("users") { users in
// GET /users
users.get { ... }
// POST /users
users.post { ... }
users.group(":id") { user in
// GET /users/:id
user.get { ... }
// PATCH /users/:id
user.patch { ... }
// PUT /users/:id
user.put { ... }
}
}
Middleware¶
Naast het prefixen van padcomponenten, kunt u ook middleware toevoegen aan routegroepen.
app.get("fast-thing") { req in
...
}
app.group(RateLimitMiddleware(requestsPerMinute: 5)) { rateLimited in
rateLimited.get("slow-thing") { req in
...
}
}
Dit is vooral nuttig voor het beschermen van subsets van uw routes met verschillende authenticatie middleware.
app.post("login") { ... }
let auth = app.grouped(AuthMiddleware())
auth.get("dashboard") { ... }
auth.get("logout") { ... }
Omleidingen¶
Omleidingen zijn nuttig in een aantal scenario's, zoals het doorsturen van oude locaties naar nieuwe voor SEO, het doorsturen van een niet-geauthenticeerde gebruiker naar de login pagina of het behouden van achterwaartse compatibiliteit met de nieuwe versie van uw API.
Om een verzoek om te leiden, gebruik:
req.redirect(to: "/some/new/path")
U kunt ook het type omleiding specificeren, bijvoorbeeld om een pagina permanent om te leiden (zodat uw SEO correct wordt bijgewerkt) gebruiken we:
req.redirect(to: "/some/new/path", redirectType: .permanent)
De verschillende Redirect
s zijn:
.permanent
- geeft een 301 Permanent omleiding..normal
- retourneert een 303 see other redirect. Dit is de standaard door Vapor en vertelt de client om de omleiding te volgen met een GET verzoek..temporary
- retourneert een 307 temporary redirect. Dit vertelt de client om de HTTP methode gebruikt in het verzoek te behouden.
Bekijk de volledige lijst om de juiste omleidingsstatuscode te kiezen.