目前的應用程式中的參數像是database的連線字串都是直接寫死的,這樣其實是不利於測試與佈署的。實際上一個應用程式往往需要隨著環境不同帶入不同的組態,舉例來說在測試環境與正式環境使用不同的database做區隔,此外假設因為一些考量要更換使用的第三方服務,因為這些寫死的參數就會讓更換變得困難。一般而言,我們會用環境變數的方式將參數傳入應用程式中,像是:
use std::env;
let val = env::var("MY_VARIABLE");
然而這其實也是一個壞味道,直接在組間中取得靜態資源的方式,會讓組件依賴於該環境,也降低了測試的可能性,在rust中我們可以使用config
這個套件來組織我們的組態設定。
cargo add config
建立configuration.rs
用來組織系統組態,用來集中所有讀取設定的部份。config套件曾經有過一次版本更新,新的api改用builder模式去建立整個組態:
//// configuration.rs
let settings = Config::builder()
//// configuration_directory表示設定檔存放的位置,required用於設定該筆設定檔是否為必要
.add_source(File::from(configuration_directory.join("base")).required(true))
//// environment表示當前的環境設定檔
.add_source(File::from(configuration_directory.join(environment.as_str())).required(true))
//// 讀入環境設定以建立組態
.add_source(
config::Environment::with_prefix("APP")
.try_parsing(true)
.separator("__"),
)
.build()?;
這邊使用add_source
方法來加入各種組態,config
套件支援環境變數、json、yaml、toml等等多種檔案格式,並且由加入的順序,新加入的設定會覆蓋舊的設定。以應用程式來說,我會將可以公開的設定放在yaml中,而需要保護的設定則是以環境變數的方式設定,後續直接放在運行環境的隱私變數中。
接下來要建立一個結構讓我們可以用強型別的方式使用系統組態,另一方面這也是對系統設定的一種抽象形式。此外連線字串是需要被保護的資料,不可以直接暴露在應用程式中,因此我們要使用secrecy
這個套件,它可以好好保護我們的機密資料:
cargo add secrecy
接下來在configuration.rs
中定義Settings struct
//// configuration.rs
#[derive(Deserialize, Clone, Debug)]
pub struct Settings {
pub database: DatabaseSettings,
}
#[derive(Deserialize, Clone, Debug)]
pub struct DatabaseSettings {
pub connection_string: Secret<String>
}
最後在config builder組織好設定後把它轉型成我們設定的型別
//// configuration.rs
settings.try_deserialize::<Settings>()
到此組織系統組態的部份就完成了,接下來我們看一下如何使用。
接下來調整我們的路由
//// application.rs
pub fn build(config: &Settings) -> Router {
let database = Database::connect(settings.database.connection_string.expose_secret())
.unwrap();
let app = Router::new()
.route("/", get(health_check))
.route("/subscriptions", post(subscribe))
/// 在路由中加入swagger
.merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi()))
.with_state(AppState {
database
});;
app
}
我們從main.rs
中取得組態設定,並作為參數傳遞到build
方法中,由於connection_string
被保護了,需要額外調用expose_secret
方法才能使用。透過Secret
保護的資料除非主動調用,否則無法被知道,這個設計可以防止重要資料不小心被暴露在應用程式的log之中。
透過組織的方式來統整系統中的設定,可以讓我們的系統更為嚴謹,也透過這個方式讓組件不直接依賴於環境變數與設定文件,整個建立config的部份可以參考這裡。