Servizi¶
Application e Request di Vapor sono progettati per essere estesi dalla tua applicazione e da pacchetti di terze parti. Le nuove funzionalità aggiunte a questi tipi vengono spesso chiamate servizi.
Sola Lettura¶
Il tipo di servizio più semplice è quello in sola lettura. Questi servizi consistono di variabili calcolate o metodi aggiunti all'application o alla request.
import Vapor
struct MyAPI {
let client: Client
func foos() async throws -> [String] { ... }
}
extension Request {
var myAPI: MyAPI {
.init(client: self.client)
}
}
I servizi in sola lettura possono dipendere da qualsiasi servizio preesistente, come client in questo esempio. Una volta aggiunta l'estensione, il tuo servizio personalizzato può essere usato come qualsiasi altra proprietà sulla request.
req.myAPI.foos()
Modificabile¶
I servizi che necessitano di uno stato o di una configurazione possono utilizzare lo storage di Application e Request per memorizzare i dati. Supponiamo che tu voglia aggiungere la seguente struct MyConfiguration alla tua applicazione.
struct MyConfiguration {
var apiKey: String
}
Per usare lo storage, devi dichiarare una StorageKey.
struct MyConfigurationKey: StorageKey {
typealias Value = MyConfiguration
}
Questa è una struct vuota con un typealias Value che specifica quale tipo viene memorizzato. Usando un tipo vuoto come chiave, puoi controllare quale codice è in grado di accedere al tuo valore di storage. Se il tipo è internal o private, solo il tuo codice sarà in grado di modificare il valore associato nello storage.
Infine, aggiungi un'estensione ad Application per ottenere e impostare la struct MyConfiguration.
extension Application {
var myConfiguration: MyConfiguration? {
get {
self.storage[MyConfigurationKey.self]
}
set {
self.storage[MyConfigurationKey.self] = newValue
}
}
}
Una volta aggiunta l'estensione, puoi usare myConfiguration come una normale proprietà su Application.
app.myConfiguration = .init(apiKey: ...)
print(app.myConfiguration?.apiKey)
Lifecycle¶
Application di Vapor ti consente di registrare gestori di lifecycle. Questi ti permettono di agganciarti a eventi come l'avvio e il shutdown.
// Stampa hello durante l'avvio.
struct Hello: LifecycleHandler {
// Chiamato prima dell'avvio dell'applicazione.
func willBoot(_ app: Application) throws {
app.logger.info("Hello!")
}
// Chiamato dopo l'avvio dell'applicazione.
func didBoot(_ app: Application) throws {
app.logger.info("Server is running")
}
// Chiamato prima del shutdown dell'applicazione.
func shutdown(_ app: Application) {
app.logger.info("Goodbye!")
}
}
// Aggiunge il gestore di lifecycle.
app.lifecycle.use(Hello())
Lock¶
L'Application di Vapor include funzionalità per sincronizzare il codice usando i lock. Dichiarando una LockKey, puoi ottenere un lock unico e condiviso per sincronizzare l'accesso al tuo codice.
struct TestKey: LockKey { }
let test = app.locks.lock(for: TestKey.self)
test.withLock {
// Esegui qualcosa.
}
Ogni chiamata a lock(for:) con la stessa LockKey restituirà lo stesso lock. Questo metodo è thread-safe.
Per un lock a livello di applicazione, puoi usare app.sync.
app.sync.withLock {
// Esegui qualcosa.
}
Request¶
I servizi destinati a essere usati nei gestori di route dovrebbero essere aggiunti a Request. I servizi di request dovrebbero usare il Logger e l'EventLoop della request. È importante che una request rimanga sullo stesso EventLoop o verrà lanciata un'asserzione quando la risposta viene restituita a Vapor.
Se un servizio deve lasciare l'EventLoop della request per svolgere del lavoro, dovrebbe assicurarsi di tornare all'EventLoop prima di terminare. Questo può essere fatto usando hop(to:) su EventLoopFuture.
I servizi di request che necessitano di accesso ai servizi dell'applicazione, come le configurazioni, possono usare req.application. Fai attenzione a considerare la thread-safety quando accedi all'applicazione da un gestore di route. In generale, solo le operazioni di lettura dovrebbero essere eseguite dalle request. Le operazioni di scrittura devono essere protette da lock.