Controllers¶
Controllers help you organize related functionality into a single place. They can also be used to create RESTful resources.
Basic¶
A basic controller looks like the following:
import Vapor import HTTP final class HelloController { func sayHello(_ req: Request) throws -> ResponseRepresentable { guard let name = req.data["name"]?.string else { throw Abort(.badRequest) } return "Hello, \(name)" } }
Simple controllers don't need to conform to any protocols. You are free to design them however you see fit.
Registering¶
The only required structure is the signature of each method in the controller. In order to register this method into the router, it must have a signature like (Request) throws -> ResponseRepresentable
. Request
and ResponseRepresentable
are made available by importing the HTTP
module.
import Vapor let drop = try Droplet() let hc = HelloController() drop.get("hello", handler: hc.sayHello)
Since the signature of our sayHello
method matches the signature of the closure for the drop.get
method, we can pass it directly. If you want to test it locally simply open http://localhost:8080/hello?name=John.
Type Safe¶
You can also use controller methods with type-safe routing.
final class HelloController { ... func sayHelloAlternate(_ req: Request) throws -> ResponseRepresentable { let name: String = try req.parameters.next(String.self) return "Hello, \(name)" } }
We add a new method called sayHelloAlternate
to the HelloController
that fetches a String
from the request's parameters.
let hc = HelloController() drop.get("hello", String.parameter, handler: hc.sayHelloAlternate)
Since drop.get
accepts a signature (Request) throws -> ResponseRepresentable
, our method can now be used as the closure for this route. In this case to test it locally open http://localhost:8080/hello/John.
Note
Read more about type safe routing in the Routing Parameters section.
Resources¶
Controllers that conform to ResourceRepresentable
can be easily registered into a router as a RESTful resource. Let's look at an example of a UserController
.
final class UserController { func index(_ req: Request) throws -> ResponseRepresentable { return try User.all().makeJSON() } func show(_ req: Request) throws -> ResponseRepresentable { let user = try req.parameters.next(User.self) return user } }
Here is a typical user controller with an index
and show
route. Indexing returns a JSON list of all users and showing returns a JSON representation of a single user.
We could register the controller like so:
let users = UserController() drop.get("users", handler: users.index) drop.get("users", User.self, handler: users.show)
But ResourceRepresentable
makes this standard RESTful structure easy.
extension UserController: ResourceRepresentable { func makeResource() -> Resource<User> { return Resource( index: index, show: show ) } }
Conforming UserController
to ResourceRepresentable
requires that the signatures of
the index
and show
methods match what the Resource<User>
is expecting.
Now that UserController
conforms to ResourceRepresentable
, registering the routes is easy.
let users = UserController() drop.resource("users", users)
drop.resource
will take care of registering only the routes that have been supplied by the call to makeResource()
. In this case, only the index
and show
routes will be supplied.
Actions¶
Below is a table describing all of the actions available.
Action | Method | Path | Note |
---|---|---|---|
index | GET | /users | Returns all users, optionally filtered by the request data. |
store | POST | /users | Creates a new user from the request data. |
show | GET | /users/:id | Returns the user with the ID supplied in the path. |
replace | PUT | /users/:id | Updates the specified user, setting any fields not present in the request data to nil . |
update | PATCH | /users/:id | Updates the specified user, only modifying fields present in the request data. |
destroy | DELETE | /users/:id | Deletes the specified user. |
clear | DELETE | /users | Deletes all users, optionally filtered by the request data. |
create | GET | /users/create | Displays a form for creating a new user. |
edit | GET | /users/:id/edit | Displays a form for editing the specified user. |
aboutItem | OPTIONS | /users/:id | Meta action. Displays information about which actions are supported. |
aboutMultiple | OPTIONS | /users | Meta action. Displays information about which actions are supported. |
Note
The aboutItem
and aboutMultiple
meta actions are implemented automatically if not overridden.
Tip
The difference between replace
and update
is subtle but important:
If a field does not exist in the request data (for example, the user's age is missing),
update
should simply not update that field where as replace
should set it to nil
.
If required data is missing from a replace
request, an error should be thrown.
Folder¶
Controllers can go anywhere in your application, but they are most often stored in the App/Controllers/
directory.
Tip
If you are building a large application, you may want to create your controllers in a separate module. This will allow you to perform unit tests on your controllers.