mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-30 18:34:36 +01:00
make ErrorBadRequest type useful
This commit is contained in:
parent
9043e7286d
commit
a44f71d8c2
1
build.rs
1
build.rs
@ -16,6 +16,7 @@ fn main() {
|
|||||||
"guide/src/qs_2.md",
|
"guide/src/qs_2.md",
|
||||||
"guide/src/qs_3.md",
|
"guide/src/qs_3.md",
|
||||||
"guide/src/qs_4.md",
|
"guide/src/qs_4.md",
|
||||||
|
"guide/src/qs_4_5.md",
|
||||||
"guide/src/qs_5.md",
|
"guide/src/qs_5.md",
|
||||||
"guide/src/qs_6.md",
|
"guide/src/qs_6.md",
|
||||||
"guide/src/qs_7.md",
|
"guide/src/qs_7.md",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
- [Getting Started](./qs_2.md)
|
- [Getting Started](./qs_2.md)
|
||||||
- [Application](./qs_3.md)
|
- [Application](./qs_3.md)
|
||||||
- [Handler](./qs_4.md)
|
- [Handler](./qs_4.md)
|
||||||
|
- [Errors](./qs_4_5.md)
|
||||||
- [State](./qs_6.md)
|
- [State](./qs_6.md)
|
||||||
- [Resources and Routes](./qs_5.md)
|
- [Resources and Routes](./qs_5.md)
|
||||||
- [Request & Response](./qs_7.md)
|
- [Request & Response](./qs_7.md)
|
||||||
|
@ -84,9 +84,7 @@ fn main() {
|
|||||||
.serve::<_, ()>("127.0.0.1:8088").unwrap();
|
.serve::<_, ()>("127.0.0.1:8088").unwrap();
|
||||||
|
|
||||||
println!("Started http server: 127.0.0.1:8088");
|
println!("Started http server: 127.0.0.1:8088");
|
||||||
// do not copy this line
|
# actix::Arbiter::system().send(actix::msgs::SystemExit(0));
|
||||||
actix::Arbiter::system().send(actix::msgs::SystemExit(0));
|
|
||||||
|
|
||||||
let _ = sys.run();
|
let _ = sys.run();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -1,21 +1,19 @@
|
|||||||
# Handler
|
# Handler
|
||||||
|
|
||||||
A request handler can by any object that implements
|
A request handler can by any object that implements
|
||||||
[`Handler` trait](../actix_web/struct.HttpResponse.html#implementations).
|
[`Handler` trait](../actix_web/dev/trait.Handler.html#implementors).
|
||||||
|
Request handling happen in two stages. First handler object get called.
|
||||||
|
Handle can return any object that implements
|
||||||
|
[`FromRequest` trait](../actix_web/trait.FromRequest.html#foreign-impls).
|
||||||
|
Then `from_request()` get called on returned object. And finally
|
||||||
|
result of the `from_request()` call get converted to `Reply` object.
|
||||||
|
|
||||||
By default actix provdes several `Handler` implementations:
|
By default actix provides several `FromRequest` implementations for some standard types,
|
||||||
|
|
||||||
* Simple function that accepts `HttpRequest` and returns any object that
|
|
||||||
implements `FromRequest` trait
|
|
||||||
* Function that accepts `HttpRequest` and returns `Result<Reply, Into<Error>>` object.
|
|
||||||
* Function that accepts `HttpRequest` and return actor that has `HttpContext<A>`as a context.
|
|
||||||
|
|
||||||
Actix provides response `FromRequest` implementation for some standard types,
|
|
||||||
like `&'static str`, `String`, etc.
|
like `&'static str`, `String`, etc.
|
||||||
For complete list of implementations check
|
For complete list of implementations check
|
||||||
[FromRequest documentation](../actix_web/trait.FromRequest.html#foreign-impls).
|
[FromRequest documentation](../actix_web/trait.FromRequest.html#foreign-impls).
|
||||||
|
|
||||||
Examples:
|
Examples of valid handlers:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn index(req: HttpRequest) -> &'static str {
|
fn index(req: HttpRequest) -> &'static str {
|
||||||
@ -41,9 +39,11 @@ fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Custom conversion
|
## Response with custom type
|
||||||
|
|
||||||
Let's create response for custom type that serializes to `application/json` response:
|
To return custom type directly from handler function `FromResponse` trait should be
|
||||||
|
implemented for this type. Let's create response for custom type that
|
||||||
|
serializes to `application/json` response:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate actix;
|
# extern crate actix;
|
||||||
@ -55,7 +55,7 @@ use actix_web::*;
|
|||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct MyObj {
|
struct MyObj {
|
||||||
name: String,
|
name: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// we have to convert Error into HttpResponse as well
|
/// we have to convert Error into HttpResponse as well
|
||||||
@ -73,18 +73,20 @@ impl FromRequest for MyObj {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn index(req: HttpRequest) -> MyObj {
|
||||||
|
MyObj{name: "user"}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let sys = actix::System::new("example");
|
let sys = actix::System::new("example");
|
||||||
|
|
||||||
HttpServer::new(
|
HttpServer::new(
|
||||||
Application::new("/")
|
Application::new("/")
|
||||||
.resource("/", |r| r.method(
|
.resource("/", |r| r.method(Method::GET).f(index)))
|
||||||
Method::GET).f(|req| {MyObj{name: "user".to_owned()}})))
|
|
||||||
.serve::<_, ()>("127.0.0.1:8088").unwrap();
|
.serve::<_, ()>("127.0.0.1:8088").unwrap();
|
||||||
|
|
||||||
println!("Started http server: 127.0.0.1:8088");
|
println!("Started http server: 127.0.0.1:8088");
|
||||||
actix::Arbiter::system().send(actix::msgs::SystemExit(0)); // <- remove this line, this code stops system during testing
|
# actix::Arbiter::system().send(actix::msgs::SystemExit(0));
|
||||||
|
|
||||||
let _ = sys.run();
|
let _ = sys.run();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
135
guide/src/qs_4_5.md
Normal file
135
guide/src/qs_4_5.md
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
# Errors
|
||||||
|
|
||||||
|
Actix uses [`Error` type](../actix_web/error/struct.Error.html)
|
||||||
|
and [`ResponseError` trait](../actix_web/error/trait.ResponseError.html)
|
||||||
|
for handling handler's errors.
|
||||||
|
Any error that implements `ResponseError` trait can be returned as error value.
|
||||||
|
*Handler* can return *Result* object, actix by default provides
|
||||||
|
`FromRequest` implemenation for compatible result object. Here is implementation
|
||||||
|
definition:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
impl<T: FromRequest, E: Into<Error>> FromRequest for Result<T, E>
|
||||||
|
```
|
||||||
|
|
||||||
|
And any error that implements `ResponseError` can be converted into `Error` object.
|
||||||
|
For example if *handler* function returns `io::Error`, it would be converted
|
||||||
|
into `HTTPInternalServerError` response. Implementation for `io::Error` is provided
|
||||||
|
by default.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# extern crate actix_web;
|
||||||
|
# use actix_web::*;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
fn index(req: HttpRequest) -> io::Result<fs::NamedFile> {
|
||||||
|
Ok(fs::NamedFile::open("static/index.html")?)
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# fn main() {
|
||||||
|
# Application::new("/")
|
||||||
|
# .resource(r"/a/index.html", |r| r.f(index))
|
||||||
|
# .finish();
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom error response
|
||||||
|
|
||||||
|
To add support for custom errors all we need to do just implement `ResponseError` trait.
|
||||||
|
`ResponseError` trait has default implementation for `error_response()` method, it
|
||||||
|
generates *500* response.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# extern crate actix_web;
|
||||||
|
#[macro_use] extern crate failure;
|
||||||
|
use actix_web::*;
|
||||||
|
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
#[fail(display="my error")]
|
||||||
|
struct MyError {
|
||||||
|
name: &'static str
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::ResponseError for MyError {}
|
||||||
|
|
||||||
|
fn index(req: HttpRequest) -> Result<&'static str, MyError> {
|
||||||
|
Err(MyError{name: "test"})
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# fn main() {
|
||||||
|
# Application::new("/")
|
||||||
|
# .resource(r"/a/index.html", |r| r.f(index))
|
||||||
|
# .finish();
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example *index* handler will always return *500* response. But it is easy
|
||||||
|
to return different responses.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# extern crate actix_web;
|
||||||
|
#[macro_use] extern crate failure;
|
||||||
|
use actix_web::*;
|
||||||
|
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
enum MyError {
|
||||||
|
#[fail(display="internal error")]
|
||||||
|
InternalError,
|
||||||
|
#[fail(display="bad request")]
|
||||||
|
BadClientData,
|
||||||
|
#[fail(display="timeout")]
|
||||||
|
Timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::ResponseError for MyError {
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
match *self {
|
||||||
|
MyError::InternalError => HttpResponse::new(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR, Body::Empty),
|
||||||
|
MyError::BadClientData => HttpResponse::new(
|
||||||
|
StatusCode::BAD_REQUEST, Body::Empty),
|
||||||
|
MyError::Timeout => HttpResponse::new(
|
||||||
|
StatusCode::GATEWAY_TIMEOUT, Body::Empty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(req: HttpRequest) -> Result<&'static str, MyError> {
|
||||||
|
Err(MyError::BadClientData)
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# fn main() {
|
||||||
|
# Application::new("/")
|
||||||
|
# .resource(r"/a/index.html", |r| r.f(index))
|
||||||
|
# .finish();
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error helpers
|
||||||
|
|
||||||
|
Actix provides set of error helper types. It is possible to use them to generate
|
||||||
|
specific error response. We can use helper types for first example with custom error.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# extern crate actix_web;
|
||||||
|
#[macro_use] extern crate failure;
|
||||||
|
use actix_web::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MyError {
|
||||||
|
name: &'static str
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(req: HttpRequest) -> Result<&'static str> {
|
||||||
|
let result: Result<&'static str, MyError> = Err(MyError{name: "test"});
|
||||||
|
|
||||||
|
Ok(result.map_err(error::ErrorBadRequest)?)
|
||||||
|
}
|
||||||
|
# fn main() {
|
||||||
|
# Application::new("/")
|
||||||
|
# .resource(r"/a/index.html", |r| r.f(index))
|
||||||
|
# .finish();
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example *BAD REQUEST* response get generated for `MYError` error.
|
44
src/error.rs
44
src/error.rs
@ -426,6 +426,50 @@ impl From<UrlParseError> for UrlGenerationError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Helper type that can wrap any error and generate *BAD REQUEST* response.
|
||||||
|
///
|
||||||
|
/// In following example any `io::Error` will be converted into "BAD REQUEST" response
|
||||||
|
/// as oposite to *INNTERNAL SERVER ERROR* which is defined by default.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// # use actix_web::*;
|
||||||
|
/// use actix_web::fs::NamedFile;
|
||||||
|
///
|
||||||
|
/// fn index(req: HttpRequest) -> Result<fs::NamedFile> {
|
||||||
|
/// let f = NamedFile::open("test.txt").map_err(error::ErrorBadRequest)?;
|
||||||
|
/// Ok(f)
|
||||||
|
/// }
|
||||||
|
/// # fn main() {}
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ErrorBadRequest<T>(pub T);
|
||||||
|
|
||||||
|
unsafe impl<T> Sync for ErrorBadRequest<T> {}
|
||||||
|
unsafe impl<T> Send for ErrorBadRequest<T> {}
|
||||||
|
|
||||||
|
impl<T> ErrorBadRequest<T> {
|
||||||
|
pub fn cause(&self) -> &T {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug + 'static> Fail for ErrorBadRequest<T> {}
|
||||||
|
impl<T: fmt::Debug + 'static> fmt::Display for ErrorBadRequest<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "BadRequest({:?})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ResponseError for ErrorBadRequest<T>
|
||||||
|
where T: Send + Sync + fmt::Debug + 'static,
|
||||||
|
{
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
HttpResponse::new(StatusCode::BAD_REQUEST, Body::Empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
@ -81,7 +81,7 @@ pub mod httpcodes;
|
|||||||
pub mod multipart;
|
pub mod multipart;
|
||||||
pub mod middlewares;
|
pub mod middlewares;
|
||||||
pub mod pred;
|
pub mod pred;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result, ResponseError};
|
||||||
pub use body::{Body, Binary};
|
pub use body::{Body, Binary};
|
||||||
pub use application::Application;
|
pub use application::Application;
|
||||||
pub use httprequest::HttpRequest;
|
pub use httprequest::HttpRequest;
|
||||||
|
30
src/param.rs
30
src/param.rs
@ -2,14 +2,9 @@ use std;
|
|||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use failure::Fail;
|
|
||||||
use http::{StatusCode};
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use body::Body;
|
use error::{ResponseError, UriSegmentError, ErrorBadRequest};
|
||||||
use httpresponse::HttpResponse;
|
|
||||||
use error::{ResponseError, UriSegmentError};
|
|
||||||
|
|
||||||
|
|
||||||
/// A trait to abstract the idea of creating a new instance of a type from a path parameter.
|
/// A trait to abstract the idea of creating a new instance of a type from a path parameter.
|
||||||
@ -132,32 +127,13 @@ impl FromParam for PathBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Fail, Debug)]
|
|
||||||
#[fail(display="Error")]
|
|
||||||
pub struct BadRequest<T>(T);
|
|
||||||
|
|
||||||
impl<T> BadRequest<T> {
|
|
||||||
pub fn cause(&self) -> &T {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ResponseError for BadRequest<T>
|
|
||||||
where T: Send + Sync + std::fmt::Debug +std::fmt::Display + 'static,
|
|
||||||
BadRequest<T>: Fail
|
|
||||||
{
|
|
||||||
fn error_response(&self) -> HttpResponse {
|
|
||||||
HttpResponse::new(StatusCode::BAD_REQUEST, Body::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! FROM_STR {
|
macro_rules! FROM_STR {
|
||||||
($type:ty) => {
|
($type:ty) => {
|
||||||
impl FromParam for $type {
|
impl FromParam for $type {
|
||||||
type Err = BadRequest<<$type as FromStr>::Err>;
|
type Err = ErrorBadRequest<<$type as FromStr>::Err>;
|
||||||
|
|
||||||
fn from_param(val: &str) -> Result<Self, Self::Err> {
|
fn from_param(val: &str) -> Result<Self, Self::Err> {
|
||||||
<$type as FromStr>::from_str(val).map_err(BadRequest)
|
<$type as FromStr>::from_str(val).map_err(ErrorBadRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,9 @@ mod tests {
|
|||||||
|
|
||||||
let pred = Header("transfer-encoding", "other");
|
let pred = Header("transfer-encoding", "other");
|
||||||
assert!(!pred.check(&mut req));
|
assert!(!pred.check(&mut req));
|
||||||
|
|
||||||
|
let pred = Header("content-tye", "other");
|
||||||
|
assert!(!pred.check(&mut req));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user