diff --git a/content/docs/extractors.md b/content/docs/extractors.md index f6668f6..df6c6bc 100644 --- a/content/docs/extractors.md +++ b/content/docs/extractors.md @@ -6,37 +6,26 @@ weight: 170 # Type-safe information extraction -Actix provides facility for type-safe request information extraction. By default, -actix provides several extractor implementations. +Actix-web provides a facility for type-safe request information access called *extractors* +(ie, `impl FromRequest`). By default, actix-web provides several extractor implementations. -# Accessing Extractors +## Extractors Within Handler Functions -How you access an Extractor depends on whether you are using a handler function -or a custom Handler type. +An extractor can be accessed in a few different ways. -## Within Handler Functions +Option 1 - passed as a parameter to a handler function: -An Extractor can be passed to a handler function as a function parameter -*or* accessed within the function by calling the ExtractorType::<...>::extract(req) -function. +{{< include-example example="extractors" file="main.rs" section="option-one" >}} -{{< include-example example="extractors" file="main.rs" section="main" >}} +Option 2 - accessed by calling `extract()` on the Extractor -## Within Custom Handler Types - -Like a handler function, a custom Handler type can *access* an Extractor by -calling the ExtractorType::<...>::extract(&req) function. An Extractor -*cannot* be passed as a parameter to a custom Handler type because a custom -Handler type must follow the ``handle`` function signature specified by the -Handler trait it implements. - -{{< include-example example="extractors" file="custom_handler.rs" section="custom-handler" >}} +{{< include-example example="extractors" file="main.rs" section="option-two" >}} # Path -[*Path*](../../actix-web/actix_web/struct.Path.html) provides information that can -be extracted from the Request's path. You can deserialize any variable -segment from the path. +[*Path*](https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html) provides +information that can be extracted from the Request's path. You can deserialize any +variable segment from the path. For instance, for resource that registered for the `/users/{userid}/{friend}` path two segments could be deserialized, `userid` and `friend`. These segments @@ -51,27 +40,31 @@ instead of a *tuple* type. {{< include-example example="extractors" file="path_two.rs" section="path-two" >}} +It is also possible to `get` or `query` the request for path parameters by name: + +{{< include-example example="extractors" file="path_three.rs" section="path-three" >}} + # Query -Same can be done with the request's query. -The [*Query*](../../actix-web/actix_web/struct.Query.html) -type provides extraction functionality. Underneath it uses *serde_urlencoded* crate. +The [*Query*](https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Query.html) +type provides extraction functionality for the request's query parameters. Underneath it +uses *serde_urlencoded* crate. {{< include-example example="extractors" file="query.rs" section="query" >}} # Json -[*Json*](../../actix-web/actix_web/struct.Json.html) allows to deserialize +[*Json*](https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Json.html) allows to deserialize a request body into a struct. To extract typed information from a request's body, the type `T` must implement the `Deserialize` trait from *serde*. {{< include-example example="extractors" file="json_one.rs" section="json-one" >}} Some extractors provide a way to configure the extraction process. Json extractor -[*JsonConfig*](../../actix-web/actix_web/dev/struct.JsonConfig.html) type for configuration. -When you register a handler using `Route::with()`, it returns a configuration instance. In case of -a *Json* extractor it returns a *JsonConfig*. You can configure the maximum size of the json -payload as well as a custom error handler function. +[*JsonConfig*](https://docs.rs/actix-web/1.0.2/actix_web/web/struct.JsonConfig.html) type +for configuration. When you register a handler using `Route::with()`, it returns a +configuration instance. In case of a *Json* extractor it returns a *JsonConfig*. You can +configure the maximum size of the json payload as well as a custom error handler function. The following example limits the size of the payload to 4kb and uses a custom error handler. @@ -83,14 +76,14 @@ At the moment only url-encoded forms are supported. The url-encoded body could be extracted to a specific type. This type must implement the `Deserialize` trait from the *serde* crate. -[*FormConfig*](../../actix-web/actix_web/dev/struct.FormConfig.html) allows +[*FormConfig*](https://docs.rs/actix-web/1.0.2/actix_web/web/struct.FormConfig.html) allows configuring the extraction process. {{< include-example example="extractors" file="form.rs" section="form" >}} # Multiple extractors -Actix provides extractor implementations for tuples (up to 10 elements) +Actix-web provides extractor implementations for tuples (up to 10 elements) whose elements implement `FromRequest`. For example we can use a path extractor and a query extractor at the same time. @@ -99,15 +92,42 @@ For example we can use a path extractor and a query extractor at the same time. # Other -Actix also provides several other extractors: +Actix-web also provides several other extractors: -* [*Data*](../../actix-web/actix_web/web/struct.Data.html) - If you need - access to an application state. This is similar to a `HttpRequest::app_data()`. +* [*Data*](https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Data.html) - If you need + access to an application state. * *HttpRequest* - *HttpRequest* itself is an extractor which returns self, in case you need access to the request. * *String* - You can convert a request's payload to a *String*. - [*Example*](../../actix-web/actix_web/trait.FromRequest.html#example-1) + [*Example*](https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html#example-2) is available in doc strings. * *bytes::Bytes* - You can convert a request's payload into *Bytes*. - [*Example*](../../actix-web/actix_web/trait.FromRequest.html#example) + [*Example*](https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html#example-4) is available in doc strings. +* *Payload* - You can access a request's payload. + [*Example*](https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Payload.html) + +# Async Data Access + +Application state is accessible from the handler with the `web::Data` extractor; +however, state is accessible as a read-only reference. If you need mutable access to state, +it must be implemented. + +> **Beware**, actix creates multiple copies of the application state and the handlers, +> unique for each thread. If you run your application in several threads, actix will +> create the same amount as number of threads of application state objects and handler +> objects. + +Here is an example of a handler that stores the number of processed requests: + +{{< include-example example="request-handlers" file="main.rs" section="data" >}} + +Although this handler will work, `self.0` will be different depending on the number of threads and +number of requests processed per thread. A proper implementation would use `Arc` and `AtomicUsize`. + +{{< include-example example="request-handlers" file="handlers_arc.rs" section="arc" >}} + +> Be careful with synchronization primitives like `Mutex` or `RwLock`. The `actix-web` framework +> handles requests asynchronously. By blocking thread execution, all concurrent +> request handling processes would block. If you need to share or update some state +> from multiple threads, consider using the [actix](https://actix.github.io/actix/actix/) actor system. diff --git a/examples/extractors/src/custom_handler.rs b/examples/extractors/src/custom_handler.rs deleted file mode 100644 index 93596d9..0000000 --- a/examples/extractors/src/custom_handler.rs +++ /dev/null @@ -1,18 +0,0 @@ -use actix_web::{web, HttpRequest, HttpResponse}; - -struct MyHandler {} -struct MyInfo {} - -// -impl Handler for MyHandler { - type Result = HttpResponse; - - /// Handle request - fn handle(&self, req: &HttpRequest) -> Self::Result { - let params = web::Path::<(String, String)>::extract(req); - let info = web::Json::::extract(req); - - HttpResponse::Ok().into() - } -} -// diff --git a/examples/extractors/src/form.rs b/examples/extractors/src/form.rs index 1af09d4..394a255 100644 --- a/examples/extractors/src/form.rs +++ b/examples/extractors/src/form.rs @@ -1,5 +1,5 @@ //
-use actix_web::{web, App, Result}; +use actix_web::{web, App, HttpServer, Result}; use serde::Deserialize; #[derive(Deserialize)] @@ -16,5 +16,9 @@ fn index(form: web::Form) -> Result { // pub fn main() { - App::new().route("", web::post().to(index)); + HttpServer::new(|| App::new().route("/", web::post().to(index))) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); } diff --git a/examples/extractors/src/json_one.rs b/examples/extractors/src/json_one.rs index d54459b..255959d 100644 --- a/examples/extractors/src/json_one.rs +++ b/examples/extractors/src/json_one.rs @@ -1,5 +1,5 @@ // -use actix_web::{web, App, Result}; +use actix_web::{web, App, HttpServer, Result}; use serde::Deserialize; #[derive(Deserialize)] @@ -11,8 +11,12 @@ struct Info { fn index(info: web::Json) -> Result { Ok(format!("Welcome {}!", info.username)) } +// pub fn main() { - App::new().route("/", web::get().to(index)); + HttpServer::new(|| App::new().route("/", web::post().to(index))) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); } -// diff --git a/examples/extractors/src/json_two.rs b/examples/extractors/src/json_two.rs index c6464ac..e5e8a22 100644 --- a/examples/extractors/src/json_two.rs +++ b/examples/extractors/src/json_two.rs @@ -1,5 +1,5 @@ // -use actix_web::{error, web, App, FromRequest, HttpResponse, Responder}; +use actix_web::{error, web, App, FromRequest, HttpResponse, HttpServer, Responder}; use serde::Deserialize; #[derive(Deserialize)] @@ -13,22 +13,28 @@ fn index(info: web::Json) -> impl Responder { } pub fn main() { - App::new().service( - web::resource("/") - .data( - // change json extractor configuration - web::Json::::configure(|cfg| { - cfg.limit(4096).error_handler(|err, _req| { - // <- create custom error response - error::InternalError::from_response( - err, - HttpResponse::Conflict().finish(), - ) - .into() - }) - }), - ) - .route(web::post().to(index)), - ); + HttpServer::new(|| { + App::new().service( + web::resource("/") + .data( + // change json extractor configuration + web::Json::::configure(|cfg| { + cfg.limit(4096).error_handler(|err, _req| { + // <- create custom error response + error::InternalError::from_response( + err, + HttpResponse::Conflict().finish(), + ) + .into() + }) + }), + ) + .route(web::post().to(index)), + ) + }) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); } // diff --git a/examples/extractors/src/main.rs b/examples/extractors/src/main.rs index 276b1b6..0925a79 100644 --- a/examples/extractors/src/main.rs +++ b/examples/extractors/src/main.rs @@ -8,6 +8,7 @@ pub mod json_one; pub mod json_two; pub mod multiple; pub mod path_one; +pub mod path_three; pub mod path_two; pub mod query; @@ -17,13 +18,13 @@ struct MyInfo { id: u32, } -//
-// Option 1: passed as a parameter to a handler function +// fn index(path: web::Path<(String, String)>, json: web::Json) -> impl Responder { format!("{} {} {} {}", path.0, path.1, json.id, json.username) } +// -// Option 2: accessed by calling extract() on the Extractor +// fn extract(req: HttpRequest) -> impl Responder { let params = web::Path::<(String, String)>::extract(&req).unwrap(); @@ -33,7 +34,7 @@ fn extract(req: HttpRequest) -> impl Responder { format!("{} {} {} {}", params.0, params.1, info.username, info.id) } -//
+// fn main() { HttpServer::new(|| { diff --git a/examples/extractors/src/multiple.rs b/examples/extractors/src/multiple.rs index 1d861d4..118dd5c 100644 --- a/examples/extractors/src/multiple.rs +++ b/examples/extractors/src/multiple.rs @@ -1,5 +1,5 @@ // -use actix_web::{web, App}; +use actix_web::{web, App, HttpServer}; use serde::Deserialize; #[derive(Deserialize)] @@ -7,14 +7,23 @@ struct Info { username: String, } -fn index((_path, query): (web::Path<(u32, String)>, web::Query)) -> String { - format!("Welcome {}!", query.username) +fn index((path, query): (web::Path<(u32, String)>, web::Query)) -> String { + format!( + "Welcome {}, friend {}, useri {}!", + query.username, path.1, path.0 + ) } pub fn main() { - App::new().route( - "/users/{userid}/{friend}", // <- define path parameters - web::get().to(index), - ); + HttpServer::new(|| { + App::new().route( + "/users/{userid}/{friend}", // <- define path parameters + web::get().to(index), + ) + }) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); } // diff --git a/examples/extractors/src/path_one.rs b/examples/extractors/src/path_one.rs index eb46b9b..0b83de8 100644 --- a/examples/extractors/src/path_one.rs +++ b/examples/extractors/src/path_one.rs @@ -1,17 +1,23 @@ // -use actix_web::{web, App, Result}; +use actix_web::{web, App, HttpServer, Result}; /// extract path info from "/users/{userid}/{friend}" url /// {userid} - - deserializes to a u32 /// {friend} - deserializes to a String fn index(info: web::Path<(u32, String)>) -> Result { - Ok(format!("Welcome {}! {}", info.1, info.0)) + Ok(format!("Welcome {}, userid {}!", info.1, info.0)) } pub fn main() { - App::new().route( - "/users/{userid}/{friend}", // <- define path parameters - web::get().to(index), - ); + HttpServer::new(|| { + App::new().route( + "/users/{userid}/{friend}", // <- define path parameters + web::get().to(index), + ) + }) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); } // diff --git a/examples/extractors/src/path_two.rs b/examples/extractors/src/path_two.rs index 7c5a4c3..aa836d6 100644 --- a/examples/extractors/src/path_two.rs +++ b/examples/extractors/src/path_two.rs @@ -1,5 +1,5 @@ // -use actix_web::{web, App, Result}; +use actix_web::{web, App, HttpServer, Result}; use serde::Deserialize; #[derive(Deserialize)] @@ -14,9 +14,15 @@ fn index(info: web::Path) -> Result { } pub fn main() { - App::new().route( - "/users/{userid}/{friend}", // <- define path parameters - web::get().to(index), - ); + HttpServer::new(|| { + App::new().route( + "/users/{userid}/{friend}", // <- define path parameters + web::get().to(index), + ) + }) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); } // diff --git a/examples/extractors/src/query.rs b/examples/extractors/src/query.rs index ff1883b..10af0ce 100644 --- a/examples/extractors/src/query.rs +++ b/examples/extractors/src/query.rs @@ -1,5 +1,5 @@ // -use actix_web::{web, App}; +use actix_web::{web, App, HttpServer}; use serde::Deserialize; #[derive(Deserialize)] @@ -11,8 +11,12 @@ struct Info { fn index(info: web::Query) -> String { format!("Welcome {}!", info.username) } +// pub fn main() { - App::new().route("/", web::get().to(index)); + HttpServer::new(|| App::new().route("/", web::get().to(index))) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); } -// diff --git a/examples/request-handlers/Cargo.toml b/examples/request-handlers/Cargo.toml index da4764a..325b659 100644 --- a/examples/request-handlers/Cargo.toml +++ b/examples/request-handlers/Cargo.toml @@ -1,8 +1,7 @@ [package] name = "request-handlers" -version = "0.7.0" +version = "1.0.0" edition = "2018" [dependencies] -actix-web = "0.7" -actix = "0.7" +actix-web = "1.0" diff --git a/examples/request-handlers/src/handlers_arc.rs b/examples/request-handlers/src/handlers_arc.rs index 9858355..f3002d8 100644 --- a/examples/request-handlers/src/handlers_arc.rs +++ b/examples/request-handlers/src/handlers_arc.rs @@ -1,34 +1,37 @@ // -use actix_web::{dev::Handler, server, App, HttpRequest, HttpResponse}; +use actix_web::{web, App, HttpServer, Responder}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -struct MyHandler(Arc); +#[derive(Clone)] +struct AppState { + count: Arc, +} -impl Handler for MyHandler { - type Result = HttpResponse; +fn show_count(data: web::Data) -> impl Responder { + format!("count: {}", data.count.load(Ordering::Relaxed)) +} - /// Handle request - fn handle(&self, _req: &HttpRequest) -> Self::Result { - self.0.fetch_add(1, Ordering::Relaxed); - HttpResponse::Ok().into() - } +fn add_one(data: web::Data) -> impl Responder { + data.count.fetch_add(1, Ordering::Relaxed); + + format!("count: {}", data.count.load(Ordering::Relaxed)) } pub fn main() { - let sys = actix::System::new("example"); + let data = AppState { + count: Arc::new(AtomicUsize::new(0)), + }; - let inc = Arc::new(AtomicUsize::new(0)); - - server::new(move || { - let cloned = inc.clone(); - App::new().resource("/", move |r| r.h(MyHandler(cloned))) + HttpServer::new(move || { + App::new() + .data(data.clone()) + .route("/", web::to(show_count)) + .route("/add", web::to(add_one)) }) .bind("127.0.0.1:8088") .unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8088"); - let _ = sys.run(); + .run() + .unwrap(); } // diff --git a/examples/request-handlers/src/main.rs b/examples/request-handlers/src/main.rs index 8756e7d..48d7842 100644 --- a/examples/request-handlers/src/main.rs +++ b/examples/request-handlers/src/main.rs @@ -1,25 +1,38 @@ -mod handlers_arc; -// -use actix_web::{dev::Handler, server, App, HttpRequest, HttpResponse}; +pub mod handlers_arc; +// +use actix_web::{web, App, HttpServer, Responder}; use std::cell::Cell; -struct MyHandler(Cell); +#[derive(Clone)] +struct AppState { + count: Cell, +} -impl Handler for MyHandler { - type Result = HttpResponse; +fn show_count(data: web::Data) -> impl Responder { + format!("count: {}", data.count.get()) +} - /// Handle request - fn handle(&self, _req: &HttpRequest) -> Self::Result { - let i = self.0.get(); - self.0.set(i + 1); - HttpResponse::Ok().into() - } +fn add_one(data: web::Data) -> impl Responder { + let count = data.count.get(); + data.count.set(count + 1); + + format!("count: {}", data.count.get()) } fn main() { - server::new(|| App::new().resource("/", |r| r.h(MyHandler(Cell::new(0))))) //use r.h() to bind handler, not the r.f() - .bind("127.0.0.1:8088") - .unwrap() - .run(); + let data = AppState { + count: Cell::new(0), + }; + + HttpServer::new(move || { + App::new() + .data(data.clone()) + .route("/", web::to(show_count)) + .route("/add", web::to(add_one)) + }) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); } -// +//