API warp 中的密钥验证 (Rust)
API key validation in warp (Rust)
我正在尝试开始使用 warp 并测试 api 密钥验证。以下代码有效,但效果不佳。
验证函数从 header 中提取密钥。成功验证后,密钥不再使用,但“handle_request”函数需要为其输入参数。
能否就如何避免不需要的输入参数和使用 warp 进行 api 密钥验证的更简洁方法提出建议?
提前致谢!
use std::convert::Infallible;
use std::error::Error;
use serde::{Deserialize, Serialize};
use warp::http::{Response, StatusCode};
use warp::{reject, Filter, Rejection, Reply};
//use futures::future;
// use headers::{Header, HeaderMapExt};
// use http::header::HeaderValue;
// use http::HeaderMap;
extern crate pretty_env_logger;
#[macro_use] extern crate log;
#[derive(Deserialize, Serialize)]
struct Params {
key1: String,
key2: u32,
}
#[derive(Debug)]
struct Unauthorized;
impl reject::Reject for Unauthorized {}
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// get /exampel?key1=value&key2=42
let route1 = warp::get().and(key_validation())
.and(warp::query::<Params>())
.and_then(handle_request);
let routes = route1.recover(handle_rejection);
warp::serve(routes)
.run(([127, 0, 0, 1], 3030))
.await;
}
async fn handle_request(api_key:String, params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}
fn key_validation() -> impl Filter<Extract = (String,), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(n)
} else {
Err(reject::custom(Unauthorized))
}
})
}
// JSON replies
/// An API error serializable to JSON.
#[derive(Serialize)]
struct ErrorMessage {
code: u16,
message: String,
}
// This function receives a `Rejection` and tries to return a custom
// value, otherwise simply passes the rejection along.
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
let code;
let message;
if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "NOT_FOUND";
} else if let Some(Unauthorized) = err.find() {
code = StatusCode::UNAUTHORIZED;
message = "Invalide API key";
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
// We can handle a specific error, here METHOD_NOT_ALLOWED,
// and render it however we want
code = StatusCode::METHOD_NOT_ALLOWED;
message = "METHOD_NOT_ALLOWED";
} else {
// We should have expected this... Just log and say its a 500
error!("unhandled rejection: {:?}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "UNHANDLED_REJECTION";
}
let json = warp::reply::json(&ErrorMessage {
code: code.as_u16(),
message: message.into(),
});
Ok(warp::reply::with_status(json, code))
}
更新:
当我试图避免使用“key_validation”函数提取某些内容时,出现此错误:
error[E0271]: type mismatch resolving `<warp::filter::and_then::AndThen<impl warp::Filter+Copy, [closure@src/main.rs:44:50: 50:6]> as warp::filter::FilterBase>::Extract == ()`
--> src/main.rs:43:24
|
43 | fn key_validation() -> impl Filter<Extract = (), Error = Rejection> + Copy {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found `()`
|
= note: expected tuple `(_,)`
found unit type `()`
为了解决这个问题,我尝试了:
async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}
fn key_validation() -> impl Filter<Extract = ((),), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(())
} else {
Err(reject::custom(Unauthorized))
}
})
}
结果是:
error[E0593]: function is expected to take 2 arguments, but it takes 1 argument
--> src/main.rs:31:19
|
31 | .and_then(handle_request);
| ^^^^^^^^^^^^^^ expected function that takes 2 arguments
...
39 | async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
| ------------------------------------------------------------------------------------ takes 1 argument
|
= note: required because of the requirements on the impl of `warp::generic::Func<((), Params)>` for `fn(Params) -> impl Future {handle_request}`
这些是使用的依赖项:
[dependencies]
log = "0.4"
pretty_env_logger = "0.4"
tokio = { version = "1", features = ["full"] }
warp = "0.3"
serde = { version = "1.0", features = ["derive"] }
futures = { version = "0.3", default-features = false, features = ["alloc"] }
只要使您的方法不提取任何内容即可:
async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}
fn key_validation() -> impl Filter<Extract = (), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(())
} else {
Err(reject::custom(Unauthorized))
}
})
}
可能您需要丢弃 key_validation 结果值,使用 untuple_one
:
let route1 = warp::get()
.and(key_validation())
.untuple_one()
.and(warp::query::<Params>())
.and_then(handle_request);
我正在尝试开始使用 warp 并测试 api 密钥验证。以下代码有效,但效果不佳。
验证函数从 header 中提取密钥。成功验证后,密钥不再使用,但“handle_request”函数需要为其输入参数。
能否就如何避免不需要的输入参数和使用 warp 进行 api 密钥验证的更简洁方法提出建议?
提前致谢!
use std::convert::Infallible;
use std::error::Error;
use serde::{Deserialize, Serialize};
use warp::http::{Response, StatusCode};
use warp::{reject, Filter, Rejection, Reply};
//use futures::future;
// use headers::{Header, HeaderMapExt};
// use http::header::HeaderValue;
// use http::HeaderMap;
extern crate pretty_env_logger;
#[macro_use] extern crate log;
#[derive(Deserialize, Serialize)]
struct Params {
key1: String,
key2: u32,
}
#[derive(Debug)]
struct Unauthorized;
impl reject::Reject for Unauthorized {}
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// get /exampel?key1=value&key2=42
let route1 = warp::get().and(key_validation())
.and(warp::query::<Params>())
.and_then(handle_request);
let routes = route1.recover(handle_rejection);
warp::serve(routes)
.run(([127, 0, 0, 1], 3030))
.await;
}
async fn handle_request(api_key:String, params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}
fn key_validation() -> impl Filter<Extract = (String,), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(n)
} else {
Err(reject::custom(Unauthorized))
}
})
}
// JSON replies
/// An API error serializable to JSON.
#[derive(Serialize)]
struct ErrorMessage {
code: u16,
message: String,
}
// This function receives a `Rejection` and tries to return a custom
// value, otherwise simply passes the rejection along.
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
let code;
let message;
if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "NOT_FOUND";
} else if let Some(Unauthorized) = err.find() {
code = StatusCode::UNAUTHORIZED;
message = "Invalide API key";
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
// We can handle a specific error, here METHOD_NOT_ALLOWED,
// and render it however we want
code = StatusCode::METHOD_NOT_ALLOWED;
message = "METHOD_NOT_ALLOWED";
} else {
// We should have expected this... Just log and say its a 500
error!("unhandled rejection: {:?}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "UNHANDLED_REJECTION";
}
let json = warp::reply::json(&ErrorMessage {
code: code.as_u16(),
message: message.into(),
});
Ok(warp::reply::with_status(json, code))
}
更新: 当我试图避免使用“key_validation”函数提取某些内容时,出现此错误:
error[E0271]: type mismatch resolving `<warp::filter::and_then::AndThen<impl warp::Filter+Copy, [closure@src/main.rs:44:50: 50:6]> as warp::filter::FilterBase>::Extract == ()`
--> src/main.rs:43:24
|
43 | fn key_validation() -> impl Filter<Extract = (), Error = Rejection> + Copy {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found `()`
|
= note: expected tuple `(_,)`
found unit type `()`
为了解决这个问题,我尝试了:
async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}
fn key_validation() -> impl Filter<Extract = ((),), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(())
} else {
Err(reject::custom(Unauthorized))
}
})
}
结果是:
error[E0593]: function is expected to take 2 arguments, but it takes 1 argument
--> src/main.rs:31:19
|
31 | .and_then(handle_request);
| ^^^^^^^^^^^^^^ expected function that takes 2 arguments
...
39 | async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
| ------------------------------------------------------------------------------------ takes 1 argument
|
= note: required because of the requirements on the impl of `warp::generic::Func<((), Params)>` for `fn(Params) -> impl Future {handle_request}`
这些是使用的依赖项:
[dependencies]
log = "0.4"
pretty_env_logger = "0.4"
tokio = { version = "1", features = ["full"] }
warp = "0.3"
serde = { version = "1.0", features = ["derive"] }
futures = { version = "0.3", default-features = false, features = ["alloc"] }
只要使您的方法不提取任何内容即可:
async fn handle_request(params: Params) -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder().body(format!("key1 = {}, key2 = {}", params.key1, params.key2)))
}
fn key_validation() -> impl Filter<Extract = (), Error = Rejection> + Copy {
warp::header::<String>("x-api-key").and_then(|n: String| async move {
if n == "test" {
Ok(())
} else {
Err(reject::custom(Unauthorized))
}
})
}
可能您需要丢弃 key_validation 结果值,使用 untuple_one
:
let route1 = warp::get()
.and(key_validation())
.untuple_one()
.and(warp::query::<Params>())
.and_then(handle_request);