mirror of
https://github.com/actix/actix-web.git
synced 2024-11-26 11:31:09 +00:00
add a way to configure error treatment for Query and Path extractors (#550)
* add a way to configure error treatment for Query extractor * allow error handler to be customized for Path extractor
This commit is contained in:
parent
960274ada8
commit
42d5d48e71
2 changed files with 114 additions and 12 deletions
|
@ -6,6 +6,10 @@
|
||||||
|
|
||||||
* HttpServer now treats streaming bodies the same for HTTP/1.x protocols. #549
|
* HttpServer now treats streaming bodies the same for HTTP/1.x protocols. #549
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add method to configure custom error handler to `Query` and `Path` extractors.
|
||||||
|
|
||||||
## [0.7.13] - 2018-10-14
|
## [0.7.13] - 2018-10-14
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
122
src/extractor.rs
122
src/extractor.rs
|
@ -111,18 +111,64 @@ impl<T, S> FromRequest<S> for Path<T>
|
||||||
where
|
where
|
||||||
T: DeserializeOwned,
|
T: DeserializeOwned,
|
||||||
{
|
{
|
||||||
type Config = ();
|
type Config = PathConfig<S>;
|
||||||
type Result = Result<Self, Error>;
|
type Result = Result<Self, Error>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
|
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
|
||||||
let req = req.clone();
|
let req = req.clone();
|
||||||
|
let req2 = req.clone();
|
||||||
|
let err = Rc::clone(&cfg.ehandler);
|
||||||
de::Deserialize::deserialize(PathDeserializer::new(&req))
|
de::Deserialize::deserialize(PathDeserializer::new(&req))
|
||||||
.map_err(ErrorNotFound)
|
.map_err(move |e| (*err)(e, &req2))
|
||||||
.map(|inner| Path { inner })
|
.map(|inner| Path { inner })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Path extractor configuration
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// use actix_web::{error, http, App, HttpResponse, Path, Result};
|
||||||
|
///
|
||||||
|
/// /// deserialize `Info` from request's body, max payload size is 4kb
|
||||||
|
/// fn index(info: Path<(u32, String)>) -> Result<String> {
|
||||||
|
/// Ok(format!("Welcome {}!", info.1))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().resource("/index.html/{id}/{name}", |r| {
|
||||||
|
/// r.method(http::Method::GET).with_config(index, |cfg| {
|
||||||
|
/// cfg.0.error_handler(|err, req| {
|
||||||
|
/// // <- create custom error response
|
||||||
|
/// error::InternalError::from_response(err, HttpResponse::Conflict().finish()).into()
|
||||||
|
/// });
|
||||||
|
/// })
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct PathConfig<S> {
|
||||||
|
ehandler: Rc<Fn(serde_urlencoded::de::Error, &HttpRequest<S>) -> Error>,
|
||||||
|
}
|
||||||
|
impl<S> PathConfig<S> {
|
||||||
|
/// Set custom error handler
|
||||||
|
pub fn error_handler<F>(&mut self, f: F) -> &mut Self
|
||||||
|
where
|
||||||
|
F: Fn(serde_urlencoded::de::Error, &HttpRequest<S>) -> Error + 'static,
|
||||||
|
{
|
||||||
|
self.ehandler = Rc::new(f);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Default for PathConfig<S> {
|
||||||
|
fn default() -> Self {
|
||||||
|
PathConfig {
|
||||||
|
ehandler: Rc::new(|e, _| ErrorNotFound(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Debug for Path<T> {
|
impl<T: fmt::Debug> fmt::Debug for Path<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
self.inner.fmt(f)
|
self.inner.fmt(f)
|
||||||
|
@ -200,17 +246,69 @@ impl<T, S> FromRequest<S> for Query<T>
|
||||||
where
|
where
|
||||||
T: de::DeserializeOwned,
|
T: de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
type Config = ();
|
type Config = QueryConfig<S>;
|
||||||
type Result = Result<Self, Error>;
|
type Result = Result<Self, Error>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
|
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
|
||||||
|
let req2 = req.clone();
|
||||||
|
let err = Rc::clone(&cfg.ehandler);
|
||||||
serde_urlencoded::from_str::<T>(req.query_string())
|
serde_urlencoded::from_str::<T>(req.query_string())
|
||||||
.map_err(|e| e.into())
|
.map_err(move |e| (*err)(e, &req2))
|
||||||
.map(Query)
|
.map(Query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Query extractor configuration
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
/// use actix_web::{error, http, App, HttpResponse, Query, Result};
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct Info {
|
||||||
|
/// username: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// /// deserialize `Info` from request's body, max payload size is 4kb
|
||||||
|
/// fn index(info: Query<Info>) -> Result<String> {
|
||||||
|
/// Ok(format!("Welcome {}!", info.username))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().resource("/index.html", |r| {
|
||||||
|
/// r.method(http::Method::GET).with_config(index, |cfg| {
|
||||||
|
/// cfg.0.error_handler(|err, req| {
|
||||||
|
/// // <- create custom error response
|
||||||
|
/// error::InternalError::from_response(err, HttpResponse::Conflict().finish()).into()
|
||||||
|
/// });
|
||||||
|
/// })
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct QueryConfig<S> {
|
||||||
|
ehandler: Rc<Fn(serde_urlencoded::de::Error, &HttpRequest<S>) -> Error>,
|
||||||
|
}
|
||||||
|
impl<S> QueryConfig<S> {
|
||||||
|
/// Set custom error handler
|
||||||
|
pub fn error_handler<F>(&mut self, f: F) -> &mut Self
|
||||||
|
where
|
||||||
|
F: Fn(serde_urlencoded::de::Error, &HttpRequest<S>) -> Error + 'static,
|
||||||
|
{
|
||||||
|
self.ehandler = Rc::new(f);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Default for QueryConfig<S> {
|
||||||
|
fn default() -> Self {
|
||||||
|
QueryConfig {
|
||||||
|
ehandler: Rc::new(|e, _| e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Debug for Query<T> {
|
impl<T: fmt::Debug> fmt::Debug for Query<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
self.0.fmt(f)
|
self.0.fmt(f)
|
||||||
|
@ -951,15 +1049,15 @@ mod tests {
|
||||||
let info = router.recognize(&req, &(), 0);
|
let info = router.recognize(&req, &(), 0);
|
||||||
let req = req.with_route_info(info);
|
let req = req.with_route_info(info);
|
||||||
|
|
||||||
let s = Path::<MyStruct>::from_request(&req, &()).unwrap();
|
let s = Path::<MyStruct>::from_request(&req, &PathConfig::default()).unwrap();
|
||||||
assert_eq!(s.key, "name");
|
assert_eq!(s.key, "name");
|
||||||
assert_eq!(s.value, "user1");
|
assert_eq!(s.value, "user1");
|
||||||
|
|
||||||
let s = Path::<(String, String)>::from_request(&req, &()).unwrap();
|
let s = Path::<(String, String)>::from_request(&req, &PathConfig::default()).unwrap();
|
||||||
assert_eq!(s.0, "name");
|
assert_eq!(s.0, "name");
|
||||||
assert_eq!(s.1, "user1");
|
assert_eq!(s.1, "user1");
|
||||||
|
|
||||||
let s = Query::<Id>::from_request(&req, &()).unwrap();
|
let s = Query::<Id>::from_request(&req, &QueryConfig::default()).unwrap();
|
||||||
assert_eq!(s.id, "test");
|
assert_eq!(s.id, "test");
|
||||||
|
|
||||||
let mut router = Router::<()>::default();
|
let mut router = Router::<()>::default();
|
||||||
|
@ -968,11 +1066,11 @@ mod tests {
|
||||||
let info = router.recognize(&req, &(), 0);
|
let info = router.recognize(&req, &(), 0);
|
||||||
let req = req.with_route_info(info);
|
let req = req.with_route_info(info);
|
||||||
|
|
||||||
let s = Path::<Test2>::from_request(&req, &()).unwrap();
|
let s = Path::<Test2>::from_request(&req, &PathConfig::default()).unwrap();
|
||||||
assert_eq!(s.as_ref().key, "name");
|
assert_eq!(s.as_ref().key, "name");
|
||||||
assert_eq!(s.value, 32);
|
assert_eq!(s.value, 32);
|
||||||
|
|
||||||
let s = Path::<(String, u8)>::from_request(&req, &()).unwrap();
|
let s = Path::<(String, u8)>::from_request(&req, &PathConfig::default()).unwrap();
|
||||||
assert_eq!(s.0, "name");
|
assert_eq!(s.0, "name");
|
||||||
assert_eq!(s.1, 32);
|
assert_eq!(s.1, 32);
|
||||||
|
|
||||||
|
@ -989,7 +1087,7 @@ mod tests {
|
||||||
let req = TestRequest::with_uri("/32/").finish();
|
let req = TestRequest::with_uri("/32/").finish();
|
||||||
let info = router.recognize(&req, &(), 0);
|
let info = router.recognize(&req, &(), 0);
|
||||||
let req = req.with_route_info(info);
|
let req = req.with_route_info(info);
|
||||||
assert_eq!(*Path::<i8>::from_request(&req, &()).unwrap(), 32);
|
assert_eq!(*Path::<i8>::from_request(&req, &&PathConfig::default()).unwrap(), 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue