在 Rust 库中处理 API 键的最惯用方法?
Most idiomatic way to handle API keys in a Rust library?
我正在为包含两个 API 键的 API 编写 Rust 绑定。有许多方法可以实现这一点。我特别不想让用户负担
这样的请求
myapi::requestThing(firstApiKey, SecondApiKey,...)
我想让用户只输入一次 API 键并让它记住它。问题是我正在尝试从功能上做到这一点,将所有内容都塞进一个结构中似乎也不是最好的方法。
您绝对不希望拥有某种神奇存储的全局配置。这将防止 API 在同一进程中被多个用户同时使用。
我会为 API 端点构建一个构建器。这可以为 API URL 提供默认值,还可以从环境变量中获取 API 键。您还可以以编程方式覆盖 URL 或键。
use std::collections::HashMap;
struct ApiEndpoint {
url: String,
api_key_1: String,
api_key_2: String,
}
impl ApiEndpoint {
fn add_money_to_account(&self, cents: u64) {
println!("Adding {} cents. Making a request to {} ({}, {})", cents, self.url, self.api_key_1, self.api_key_2);
}
}
struct ApiBuilder {
url: Option<String>,
api_key_1: Option<String>,
api_key_2: Option<String>,
}
impl ApiBuilder {
fn new() -> ApiBuilder {
ApiBuilder {
url: None,
api_key_1: None,
api_key_2: None,
}
}
fn url(mut self, url: &str) -> ApiBuilder {
self.url = Some(url.into());
self
}
fn api_key_1(mut self, api_key_1: &str) -> ApiBuilder {
self.api_key_1 = Some(api_key_1.into());
self
}
fn api_key_2(mut self, api_key_2: &str) -> ApiBuilder {
self.api_key_2 = Some(api_key_2.into());
self
}
fn build(self) -> ApiEndpoint {
let mut env_vars: HashMap<_, _> = std::env::vars().collect();
ApiEndpoint {
url: self.url.unwrap_or_else(|| "http://example.com/default".into()),
api_key_1: self.api_key_1.or_else(|| env_vars.remove("MYLIB_ENV_VAR_1")).unwrap(),
api_key_2: self.api_key_2.or_else(|| env_vars.remove("MYLIB_ENV_VAR_2")).unwrap(),
}
}
}
fn main() {
let endpoint =
ApiBuilder::new()
.url("https://test.example.com")
.api_key_1("SEEKRET")
.api_key_2("PASSWORD")
.build();
endpoint.add_money_to_account(500);
}
cramming everything into a struct also doesn't seem like the best way
我不明白为什么不。
我正在为包含两个 API 键的 API 编写 Rust 绑定。有许多方法可以实现这一点。我特别不想让用户负担
这样的请求myapi::requestThing(firstApiKey, SecondApiKey,...)
我想让用户只输入一次 API 键并让它记住它。问题是我正在尝试从功能上做到这一点,将所有内容都塞进一个结构中似乎也不是最好的方法。
您绝对不希望拥有某种神奇存储的全局配置。这将防止 API 在同一进程中被多个用户同时使用。
我会为 API 端点构建一个构建器。这可以为 API URL 提供默认值,还可以从环境变量中获取 API 键。您还可以以编程方式覆盖 URL 或键。
use std::collections::HashMap;
struct ApiEndpoint {
url: String,
api_key_1: String,
api_key_2: String,
}
impl ApiEndpoint {
fn add_money_to_account(&self, cents: u64) {
println!("Adding {} cents. Making a request to {} ({}, {})", cents, self.url, self.api_key_1, self.api_key_2);
}
}
struct ApiBuilder {
url: Option<String>,
api_key_1: Option<String>,
api_key_2: Option<String>,
}
impl ApiBuilder {
fn new() -> ApiBuilder {
ApiBuilder {
url: None,
api_key_1: None,
api_key_2: None,
}
}
fn url(mut self, url: &str) -> ApiBuilder {
self.url = Some(url.into());
self
}
fn api_key_1(mut self, api_key_1: &str) -> ApiBuilder {
self.api_key_1 = Some(api_key_1.into());
self
}
fn api_key_2(mut self, api_key_2: &str) -> ApiBuilder {
self.api_key_2 = Some(api_key_2.into());
self
}
fn build(self) -> ApiEndpoint {
let mut env_vars: HashMap<_, _> = std::env::vars().collect();
ApiEndpoint {
url: self.url.unwrap_or_else(|| "http://example.com/default".into()),
api_key_1: self.api_key_1.or_else(|| env_vars.remove("MYLIB_ENV_VAR_1")).unwrap(),
api_key_2: self.api_key_2.or_else(|| env_vars.remove("MYLIB_ENV_VAR_2")).unwrap(),
}
}
}
fn main() {
let endpoint =
ApiBuilder::new()
.url("https://test.example.com")
.api_key_1("SEEKRET")
.api_key_2("PASSWORD")
.build();
endpoint.add_money_to_account(500);
}
cramming everything into a struct also doesn't seem like the best way
我不明白为什么不。