コンテンツにスキップ

サービス

VaporのApplicationRequestは、あなたのアプリケーションやサードパーティパッケージによって拡張できるように構築されています。これらの型に追加される新しい機能は、しばしばサービスと呼ばれます。

読み取り専用

最もシンプルなタイプのサービスは読み取り専用です。これらのサービスは、applicationまたはrequestに追加される計算プロパティやメソッドで構成されます。

import Vapor

struct MyAPI {
    let client: Client

    func foos() async throws -> [String] { ... }
}

extension Request {
    var myAPI: MyAPI {
        .init(client: self.client)
    }
}

読み取り専用サービスは、この例のclientのような既存のサービスに依存できます。拡張機能が追加されると、カスタムサービスはrequestの他のプロパティと同様に使用できます。

req.myAPI.foos()

書き込み可能

状態や設定が必要なサービスは、データの保存にApplicationRequestのストレージを活用できます。次のMyConfiguration構造体をアプリケーションに追加したいとしましょう。

struct MyConfiguration {
    var apiKey: String
}

ストレージを使用するには、StorageKeyを宣言する必要があります。

struct MyConfigurationKey: StorageKey {
    typealias Value = MyConfiguration
}

これは、保存される型を指定するValue型エイリアスを持つ空の構造体です。空の型をキーとして使用することで、ストレージ値にアクセスできるコードを制御できます。その型がinternalまたはprivateの場合、あなたのコードのみがストレージ内の関連する値を変更できます。

最後に、MyConfiguration構造体を取得・設定するためのApplicationへの拡張を追加します。

extension Application {
    var myConfiguration: MyConfiguration? {
        get {
            self.storage[MyConfigurationKey.self]
        }
        set {
            self.storage[MyConfigurationKey.self] = newValue
        }
    }
}

拡張機能が追加されると、myConfigurationApplicationの通常のプロパティのように使用できます。

app.myConfiguration = .init(apiKey: ...)
print(app.myConfiguration?.apiKey)

ライフサイクル

VaporのApplicationでは、ライフサイクルハンドラーを登録できます。これにより、起動やシャットダウンなどのイベントにフックできます。

// 起動時にhelloを出力します。
struct Hello: LifecycleHandler {
    // アプリケーション起動前に呼ばれます。
    func willBoot(_ app: Application) throws {
        app.logger.info("Hello!")
    }

    // アプリケーション起動後に呼ばれます。
    func didBoot(_ app: Application) throws {
        app.logger.info("Server is running")
    }

    // アプリケーションシャットダウン前に呼ばれます。
    func shutdown(_ app: Application) {
        app.logger.info("Goodbye!")
    }
}

// ライフサイクルハンドラーを追加します。
app.lifecycle.use(Hello())

ロック

VaporのApplicationには、ロックを使用してコードを同期するための便利な機能が含まれています。LockKeyを宣言することで、コードへのアクセスを同期するための一意の共有ロックを取得できます。

struct TestKey: LockKey { }

let test = app.locks.lock(for: TestKey.self)
test.withLock {
    // 何か処理を行う。
}

同じLockKeylock(for:)を呼び出すたびに、同じロックが返されます。このメソッドはスレッドセーフです。

アプリケーション全体のロックには、app.syncを使用できます。

app.sync.withLock {
    // 何か処理を行う。
}

リクエスト

ルートハンドラーで使用されることを意図したサービスは、Requestに追加する必要があります。リクエストサービスは、リクエストのロガーとイベントループを使用する必要があります。レスポンスがVaporに返されるときにアサーションが発生しないよう、リクエストが同じイベントループに留まることが重要です。

サービスが作業を行うためにリクエストのイベントループを離れる必要がある場合は、終了前にイベントループに戻るようにする必要があります。これはEventLoopFuturehop(to:)を使用して行うことができます。

設定などのアプリケーションサービスへのアクセスが必要なリクエストサービスは、req.applicationを使用できます。ルートハンドラーからアプリケーションにアクセスする際は、スレッドセーフティを考慮することに注意してください。一般的に、リクエストでは読み取り操作のみを実行すべきです。書き込み操作はロックで保護する必要があります。