有没有办法在 Warp 中作为过滤器的一部分进行验证?
Is there a way to do validation as part of a filter in Warp?
我定义了路由和端点函数。我还注入了一些依赖项。
pub fn route1() -> BoxedFilter<(String, ParamType)> {
warp::get()
.and(warp::path::param())
.and(warp::filters::query::query())
.and(warp::path::end())
.boxed()
}
pub async fn handler1(
query: String,
param: ParamType,
dependency: DependencyType,
) -> Result<impl warp::Reply, warp::Rejection> {
}
let api = api::routes::route1()
.and(warp::any().map(move || dependency))
.and_then(api::hanlders::hander1);
这似乎一切正常。
但是,我希望能够在几个端点前放置一些东西来检查查询参数中的有效键。在 handler1
里面我可以添加:
if !param.key_valid {
return Ok(warp::reply::with_status(
warp::reply::json(&""),
StatusCode::BAD_REQUEST,
));
}
我不想将它单独添加到每个处理程序。
看来我应该可以通过 filter
来完成,但我想不通。我试过使用 .map()
但随后返回多个项目将它转移到一个元组,我必须更改我的下游函数签名。理想情况下,我想找到一种方法来添加验证或其他过滤器,这些过滤器可以在没有任何下游值知道的情况下拒绝请求。
warp 的 rejection example:
有效地证明了这一点
Rejections represent cases where a filter should not continue processing the request, but a different filter could process it.
Extract a denominator from a "div-by" header, or reject with DivideByZero.
你需要
- 使用
Filter::and_then
获取现有过滤器(在本例中为 query()
)并执行验证。如果验证失败,return 自定义拒绝。
- 使用
Filter::recover
适当地处理自定义拒绝和任何其他可能的错误。
适用于您的情况:
use serde::Deserialize;
use std::{convert::Infallible, net::IpAddr};
use warp::{filters::BoxedFilter, http::StatusCode, reject::Reject, Filter, Rejection, Reply};
fn route1() -> BoxedFilter<(String, ParamType)> {
warp::get()
.and(warp::path::param())
.and(validated_query())
.and(warp::path::end())
.boxed()
}
#[derive(Debug)]
struct Invalid;
impl Reject for Invalid {}
fn validated_query() -> impl Filter<Extract = (ParamType,), Error = Rejection> + Copy {
warp::filters::query::query().and_then(|param: ParamType| async move {
if param.valid {
Ok(param)
} else {
Err(warp::reject::custom(Invalid))
}
})
}
async fn report_invalid(r: Rejection) -> Result<impl Reply, Infallible> {
let reply = warp::reply::reply();
if let Some(Invalid) = r.find() {
Ok(warp::reply::with_status(reply, StatusCode::BAD_REQUEST))
} else {
// Do better error handling here
Ok(warp::reply::with_status(
reply,
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
async fn handler1(
_query: String,
_param: ParamType,
_dependency: DependencyType,
) -> Result<impl warp::Reply, warp::Rejection> {
Ok(warp::reply::reply())
}
struct DependencyType;
#[derive(Deserialize)]
struct ParamType {
valid: bool,
}
#[tokio::main]
async fn main() {
let api = route1()
.and(warp::any().map(move || DependencyType))
.and_then(handler1)
.recover(report_invalid);
let ip: IpAddr = "127.0.0.1".parse().unwrap();
let port = 8888;
warp::serve(api).run((ip, port)).await;
}
并且删除了无关行的 curl 输出:
% curl -v '127.0.0.1:8888/dummy/?valid=false'
< HTTP/1.1 400 Bad Request
% curl -v '127.0.0.1:8888/dummy/?valid=true'
< HTTP/1.1 200 OK
Cargo.toml
[dependencies]
warp = "0.2.2"
serde = { version = "1.0.104", features = ["derive"] }
tokio = { version = "0.2.13", features = ["full"] }
我定义了路由和端点函数。我还注入了一些依赖项。
pub fn route1() -> BoxedFilter<(String, ParamType)> {
warp::get()
.and(warp::path::param())
.and(warp::filters::query::query())
.and(warp::path::end())
.boxed()
}
pub async fn handler1(
query: String,
param: ParamType,
dependency: DependencyType,
) -> Result<impl warp::Reply, warp::Rejection> {
}
let api = api::routes::route1()
.and(warp::any().map(move || dependency))
.and_then(api::hanlders::hander1);
这似乎一切正常。
但是,我希望能够在几个端点前放置一些东西来检查查询参数中的有效键。在 handler1
里面我可以添加:
if !param.key_valid {
return Ok(warp::reply::with_status(
warp::reply::json(&""),
StatusCode::BAD_REQUEST,
));
}
我不想将它单独添加到每个处理程序。
看来我应该可以通过 filter
来完成,但我想不通。我试过使用 .map()
但随后返回多个项目将它转移到一个元组,我必须更改我的下游函数签名。理想情况下,我想找到一种方法来添加验证或其他过滤器,这些过滤器可以在没有任何下游值知道的情况下拒绝请求。
warp 的 rejection example:
有效地证明了这一点Rejections represent cases where a filter should not continue processing the request, but a different filter could process it.
Extract a denominator from a "div-by" header, or reject with DivideByZero.
你需要
- 使用
Filter::and_then
获取现有过滤器(在本例中为query()
)并执行验证。如果验证失败,return 自定义拒绝。 - 使用
Filter::recover
适当地处理自定义拒绝和任何其他可能的错误。
适用于您的情况:
use serde::Deserialize;
use std::{convert::Infallible, net::IpAddr};
use warp::{filters::BoxedFilter, http::StatusCode, reject::Reject, Filter, Rejection, Reply};
fn route1() -> BoxedFilter<(String, ParamType)> {
warp::get()
.and(warp::path::param())
.and(validated_query())
.and(warp::path::end())
.boxed()
}
#[derive(Debug)]
struct Invalid;
impl Reject for Invalid {}
fn validated_query() -> impl Filter<Extract = (ParamType,), Error = Rejection> + Copy {
warp::filters::query::query().and_then(|param: ParamType| async move {
if param.valid {
Ok(param)
} else {
Err(warp::reject::custom(Invalid))
}
})
}
async fn report_invalid(r: Rejection) -> Result<impl Reply, Infallible> {
let reply = warp::reply::reply();
if let Some(Invalid) = r.find() {
Ok(warp::reply::with_status(reply, StatusCode::BAD_REQUEST))
} else {
// Do better error handling here
Ok(warp::reply::with_status(
reply,
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
async fn handler1(
_query: String,
_param: ParamType,
_dependency: DependencyType,
) -> Result<impl warp::Reply, warp::Rejection> {
Ok(warp::reply::reply())
}
struct DependencyType;
#[derive(Deserialize)]
struct ParamType {
valid: bool,
}
#[tokio::main]
async fn main() {
let api = route1()
.and(warp::any().map(move || DependencyType))
.and_then(handler1)
.recover(report_invalid);
let ip: IpAddr = "127.0.0.1".parse().unwrap();
let port = 8888;
warp::serve(api).run((ip, port)).await;
}
并且删除了无关行的 curl 输出:
% curl -v '127.0.0.1:8888/dummy/?valid=false'
< HTTP/1.1 400 Bad Request
% curl -v '127.0.0.1:8888/dummy/?valid=true'
< HTTP/1.1 200 OK
Cargo.toml
[dependencies]
warp = "0.2.2"
serde = { version = "1.0.104", features = ["derive"] }
tokio = { version = "0.2.13", features = ["full"] }