diff --git a/content/docs/handlers.md b/content/docs/handlers.md index f0bdc36..2923dbe 100644 --- a/content/docs/handlers.md +++ b/content/docs/handlers.md @@ -62,69 +62,12 @@ it must be implemented. Here is an example of a handler that stores the number of processed requests: -```rust -use actix_web::{App, HttpRequest, HttpResponse, dev::Handler}; - -struct MyHandler(Cell); - -impl Handler for MyHandler { - type Result = HttpResponse; - - /// Handle request - fn handle(&self, req: &HttpRequest) -> Self::Result { - let i = self.0.get(); - self.0.set(i + 1); - HttpResponse::Ok().into() - } -} - -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:8080") - .unwrap() - .run(); -} -``` +{{< include-example example="request-handlers" file="main.rs" section="main" >}} 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`. -```rust -use actix_web::{server, App, HttpRequest, HttpResponse, dev::Handler}; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; - -struct MyHandler(Arc); - -impl Handler for MyHandler { - type Result = HttpResponse; - - /// Handle request - fn handle(&self, req: &HttpRequest) -> Self::Result { - self.0.fetch_add(1, Ordering::Relaxed); - HttpResponse::Ok().into() - } -} - -fn main() { - let sys = actix::System::new("example"); - - let inc = Arc::new(AtomicUsize::new(0)); - - server::new( - move || { - let cloned = inc.clone(); - App::new() - .resource("/", move |r| r.h(MyHandler(cloned))) - }) - .bind("127.0.0.1:8088").unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8088"); - let _ = sys.run(); -} -``` +{{< 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 @@ -137,51 +80,7 @@ To return a custom type directly from a handler function, the type needs to impl Let's create a response for a custom type that serializes to an `application/json` response: -```rust -# extern crate actix; -# extern crate actix_web; -extern crate serde; -extern crate serde_json; -#[macro_use] extern crate serde_derive; -use actix_web::{server, App, HttpRequest, HttpResponse, Error, Responder, http}; - -#[derive(Serialize)] -struct MyObj { - name: &'static str, -} - -/// Responder -impl Responder for MyObj { - type Item = HttpResponse; - type Error = Error; - - fn respond_to(self, req: &HttpRequest) -> Result { - let body = serde_json::to_string(&self)?; - - // Create response and set content type - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(body)) - } -} - -fn index(req: &HttpRequest) -> impl Responder { - MyObj { name: "user" } -} - -fn main() { - let sys = actix::System::new("example"); - - server::new( - || App::new() - .resource("/", |r| r.method(http::Method::GET).f(index))) - .bind("127.0.0.1:8088").unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8088"); - let _ = sys.run(); -} -``` +{{< include-example example="responder-trait" file="main.rs" section="main" >}} ## Async handlers @@ -190,55 +89,12 @@ or more precisely, any type that implements the [*Responder*](../../actix-web/ac In this case, the handler must return a `Future` object that resolves to the *Responder* type, i.e: -```rust -use actix_web::*; -use bytes::Bytes; -use futures::stream::once; -use futures::future::{Future, result}; - -fn index(req: &HttpRequest) -> Box> { - - result(Ok(HttpResponse::Ok() - .content_type("text/html") - .body(format!("Hello!")))) - .responder() -} - -fn index2(req: &HttpRequest) -> Box> { - result(Ok("Welcome!")) - .responder() -} - -fn main() { - App::new() - .resource("/async", |r| r.route().a(index)) - .resource("/", |r| r.route().a(index2)) - .finish(); -} -``` +{{< include-example example="async-handlers" file="main.rs" section="main" >}} Or the response body can be generated asynchronously. In this case, body must implement the stream trait `Stream`, i.e: -```rust -use actix_web::*; -use bytes::Bytes; -use futures::stream::once; - -fn index(req: &HttpRequest) -> HttpResponse { - let body = once(Ok(Bytes::from_static(b"test"))); - - HttpResponse::Ok() - .content_type("application/json") - .body(Body::Streaming(Box::new(body))) -} - -fn main() { - App::new() - .resource("/async", |r| r.f(index)) - .finish(); -} -``` +{{< include-example example="async-handlers" file="stream.rs" section="main" >}} Both methods can be combined. (i.e Async response with streaming body) @@ -246,23 +102,7 @@ It is possible to return a `Result` where the `Result::Item` type can be `Future In this example, the `index` handler can return an error immediately or return a future that resolves to a `HttpResponse`. -```rust -use actix_web::*; -use bytes::Bytes; -use futures::stream::once; -use futures::future::{Future, result}; - -fn index(req: &HttpRequest) -> Result>, Error> { - if is_error() { - Err(error::ErrorBadRequest("bad request")) - } else { - Ok(Box::new( - result(Ok(HttpResponse::Ok() - .content_type("text/html") - .body(format!("Hello!")))))) - } -} -``` +{{< include-example example="async-handlers" file="async_stream.rs" section="main" >}} ## Different return types (Either) @@ -272,21 +112,4 @@ you can error check and return errors, return async responses, or any result tha For this case, the [*Either*](../../actix-web/actix_web/enum.Either.html) type can be used. `Either` allows combining two different responder types into a single type. -```rust -use futures::future::{Future, result}; -use actix_web::{Either, Error, HttpResponse}; - -type RegisterResult = Either>>; - -fn index(req: &HttpRequest) -> RegisterResult { - if is_a_variant() { // <- choose variant A - Either::A( - HttpResponse::BadRequest().body("Bad data")) - } else { - Either::B( // <- variant B - result(Ok(HttpResponse::Ok() - .content_type("text/html") - .body(format!("Hello!")))).responder()) - } -} -``` +{{< include-example example="either" file="main.rs" section="main" >}} diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 444f971..328991a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -9,4 +9,10 @@ members = [ "request-routing", "server", "url-dispatch", + "responder-trait", + "either" +] +exclude = [ + "request-handlers", + "async-handlers", ] diff --git a/examples/async-handlers/Cargo.toml b/examples/async-handlers/Cargo.toml new file mode 100644 index 0000000..fef635a --- /dev/null +++ b/examples/async-handlers/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "async-handlers" +version = "0.1.0" +edition = "2018" + +[dependencies] +actix-web = "0.7" +futures = "0.1" +bytes = "0.4" diff --git a/examples/async-handlers/src/async_stream.rs b/examples/async-handlers/src/async_stream.rs new file mode 100644 index 0000000..2641e21 --- /dev/null +++ b/examples/async-handlers/src/async_stream.rs @@ -0,0 +1,24 @@ +fn is_error() -> bool { + true +} + +//
+use actix_web::{error, App, Error, HttpRequest, HttpResponse}; +use futures::future::{result, Future}; + +fn index( + _req: &HttpRequest, +) -> Result>, Error> { + if is_error() { + Err(error::ErrorBadRequest("bad request")) + } else { + Ok(Box::new(result(Ok(HttpResponse::Ok() + .content_type("text/html") + .body(format!("Hello!")))))) + } +} +//
+ +pub fn main() { + App::new().resource("/", |r| r.route().f(index)).finish(); +} diff --git a/examples/async-handlers/src/main.rs b/examples/async-handlers/src/main.rs new file mode 100644 index 0000000..b8c5f8f --- /dev/null +++ b/examples/async-handlers/src/main.rs @@ -0,0 +1,25 @@ +mod async_stream; +mod stream; +//
+use actix_web::{App, AsyncResponder, Error, HttpRequest, HttpResponse}; +use futures::future::{result, Future}; + +fn index(_req: &HttpRequest) -> Box> { + result(Ok(HttpResponse::Ok() + .content_type("text/html") + .body(format!("Hello!")))) + .responder() +} + +fn index2(_req: &HttpRequest) -> Box> { + result(Ok("Welcome!")).responder() +} + +fn main() { + App::new() + .resource("/async", |r| r.route().a(index)) + .resource("/", |r| r.route().a(index2)) + // .resource("/", |r| r.route().f(async_stream::index)) + .finish(); +} +//
diff --git a/examples/async-handlers/src/stream.rs b/examples/async-handlers/src/stream.rs new file mode 100644 index 0000000..da4c8a0 --- /dev/null +++ b/examples/async-handlers/src/stream.rs @@ -0,0 +1,17 @@ +//
+use actix_web::{App, Body, HttpRequest, HttpResponse}; +use bytes::Bytes; +use futures::stream::once; + +fn index(_req: &HttpRequest) -> HttpResponse { + let body = once(Ok(Bytes::from_static(b"test"))); + + HttpResponse::Ok() + .content_type("application/json") + .body(Body::Streaming(Box::new(body))) +} + +pub fn main() { + App::new().resource("/async", |r| r.f(index)).finish(); +} +//
diff --git a/examples/either/Cargo.toml b/examples/either/Cargo.toml new file mode 100644 index 0000000..dfaf7bc --- /dev/null +++ b/examples/either/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "either" +version = "0.1.0" +edition = "2018" + +[dependencies] +actix-web = "1.0" +futures = "0.1" diff --git a/examples/either/src/main.rs b/examples/either/src/main.rs new file mode 100644 index 0000000..453239d --- /dev/null +++ b/examples/either/src/main.rs @@ -0,0 +1,29 @@ +//
+use actix_web::{web, App, Either, Error, HttpRequest, HttpResponse}; +use futures::future::{ok, Future}; + +type RegisterResult = + Either>>; + +fn index(_req: HttpRequest) -> RegisterResult { + if is_a_variant() { + // <- choose variant A + Either::A(HttpResponse::BadRequest().body("Bad data")) + } else { + Either::B( + // <- variant B + Box::new(ok(HttpResponse::Ok() + .content_type("text/html") + .body(format!("Hello!")))), + ) + } +} + +fn main() { + App::new().route("/", web::get().to(index)); +} +//
+ +fn is_a_variant() -> bool { + true +} diff --git a/examples/request-handlers/Cargo.toml b/examples/request-handlers/Cargo.toml new file mode 100644 index 0000000..1fb4d7d --- /dev/null +++ b/examples/request-handlers/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "request-handlers" +version = "0.1.0" +edition = "2018" + +[dependencies] +actix-web = "0.7" +actix = "0.7" diff --git a/examples/request-handlers/src/handlers_arc.rs b/examples/request-handlers/src/handlers_arc.rs new file mode 100644 index 0000000..9858355 --- /dev/null +++ b/examples/request-handlers/src/handlers_arc.rs @@ -0,0 +1,34 @@ +// +use actix_web::{dev::Handler, server, App, HttpRequest, HttpResponse}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; + +struct MyHandler(Arc); + +impl Handler for MyHandler { + type Result = HttpResponse; + + /// Handle request + fn handle(&self, _req: &HttpRequest) -> Self::Result { + self.0.fetch_add(1, Ordering::Relaxed); + HttpResponse::Ok().into() + } +} + +pub fn main() { + let sys = actix::System::new("example"); + + let inc = Arc::new(AtomicUsize::new(0)); + + server::new(move || { + let cloned = inc.clone(); + App::new().resource("/", move |r| r.h(MyHandler(cloned))) + }) + .bind("127.0.0.1:8088") + .unwrap() + .start(); + + println!("Started http server: 127.0.0.1:8088"); + let _ = sys.run(); +} +// diff --git a/examples/request-handlers/src/main.rs b/examples/request-handlers/src/main.rs new file mode 100644 index 0000000..84d7aca --- /dev/null +++ b/examples/request-handlers/src/main.rs @@ -0,0 +1,25 @@ +mod handlers_arc; +//
+use actix_web::{dev::Handler, server, App, HttpRequest, HttpResponse}; +use std::cell::Cell; + +struct MyHandler(Cell); + +impl Handler for MyHandler { + type Result = HttpResponse; + + /// Handle request + fn handle(&self, _req: &HttpRequest) -> Self::Result { + let i = self.0.get(); + self.0.set(i + 1); + HttpResponse::Ok().into() + } +} + +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(); +} +//
diff --git a/examples/responder-trait/Cargo.toml b/examples/responder-trait/Cargo.toml new file mode 100644 index 0000000..1b921cc --- /dev/null +++ b/examples/responder-trait/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "responder-trait" +version = "0.1.0" +edition = "2018" + +[dependencies] +actix-web = "1.0" +serde = "1.0" +serde_json = "1.0" diff --git a/examples/responder-trait/src/main.rs b/examples/responder-trait/src/main.rs new file mode 100644 index 0000000..e635151 --- /dev/null +++ b/examples/responder-trait/src/main.rs @@ -0,0 +1,37 @@ +//
+use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder}; +use serde::Serialize; +use serde_json; + +#[derive(Serialize)] +struct MyObj { + name: &'static str, +} + +// Responder +impl Responder for MyObj { + type Error = Error; + type Future = Result; + + fn respond_to(self, _req: &HttpRequest) -> Self::Future { + let body = serde_json::to_string(&self)?; + + // Create response and set content type + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) + } +} + +fn index(_req: HttpRequest) -> impl Responder { + MyObj { name: "user" } +} + +fn main() { + HttpServer::new(|| App::new().route("/", web::get().to(index))) + .bind("127.0.0.1:8088") + .unwrap() + .run() + .unwrap(); +} +//