例えば、
- ログインしていればビューをレンダリング
- ログインしていなければログインページにリダイレクト
をしたい場合を考える。
Vapor 3.0 ではルーティングにバインドされたハンドラーメソッドの戻り値として Future
でラップされた ResponseEncodable
準拠型を返す必要がある。
したがって、以下は戻り値の型が一致しないためコンパイルエラーになる。
routes.swift
...
public func routes(_ router: Router) throws {
let articleController = ArticleController()
router.get("articles", "new", use: articleController.new)
...
let userController = UserController()
router.get("users", "login", use: userController.new)
....
}
ArticleController.swift
final class ArticleController {
...
func new(_ req: Request) throws -> Future<View> {
guard isLoggedIn(req) else {
return Future.map(on: req) { req.redirect("/users/login") } // => これは Future<Response> なので型が合わない
}
return try req.view().render("articles/new")
}
...
}
ResponseEncodable に準拠する enum を用意する
final class ArticleController {
...
enum ViewOrRedirect: ResponseEncodable {
func encode(for req: Request) throws -> Future<Response> {
switch self {
case .redirect(let path): return Future.map(on: req) { req.redirect(to: path) }
case .view(let view): return try view.encode(for: req)
}
}
case redirect(String)
case view(View)
}
...
func new(_ req: Request) throws -> Future<ViewOrRedirect> {
guard isLoggedIn(req) else {
return Future.map(on: req) { .redirect("/users/login") }
}
return try req.view().render("articles/new").map(to: ViewOrRedirect.self) { view in
return .view(view)
}
}
...
}
こうすることで、リダイレクトの場合もビューをレンダリングする場合も、返り値の型が ResponseEncodable
に準拠した ViewOrRedirect
型になるので型安全性を保ったまま状態に応じて処理を変えることができる。