Swift
Vapor
Vapor3.0

Vapor 3.0 でのルーティングでリダイレクト or ビューのレンダリングをする (1)

例えば、

  • ログインしていればビューをレンダリング
  • ログインしていなければログインページにリダイレクト

をしたい場合を考える。

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 型になるので型安全性を保ったまま状態に応じて処理を変えることができる。