1
0
mirror of https://github.com/actix/actix-website synced 2025-03-11 18:52:58 +01:00

Handlers done-ish.

This commit is contained in:
Cameron Dershem 2019-06-20 02:04:22 -04:00
parent 4291b822fc
commit 59f010461a
8 changed files with 62 additions and 79 deletions

View File

@ -6,13 +6,17 @@ weight: 160
# Request Handlers # Request Handlers
A request handler can be any object that implements A request handler is a function that accepts zero or more parameters that can be extracted
[*Handler*](../../actix-web/actix_web/dev/trait.Handler.html) trait. from a request (ie,
[*impl FromRequest*](https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html))
and returns a type that can be converted into an HttpResponse (ie,
[*impl Responder*](https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html)).
Request handling happens in two stages. First the handler object is called, Request handling happens in two stages. First the handler object is called,
returning any object that implements the returning any object that implements the
[*Responder*](../../actix-web/actix_web/trait.Responder.html#foreign-impls) trait. [*Responder*](https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html) trait.
Then, `respond_to()` is called on the returned object, converting itself to a `AsyncResult` or `Error`. Then, `respond_to()` is called on the returned object, converting itself to a `HttpResponse`
or `Error`.
By default actix provides `Responder` implementations for some standard types, By default actix provides `Responder` implementations for some standard types,
such as `&'static str`, `String`, etc. such as `&'static str`, `String`, etc.
@ -29,7 +33,7 @@ fn index(req: &HttpRequest) -> &'static str {
``` ```
```rust ```rust
fn index(req: &HttpRequest) -> String { fn index(req: HttpRequest) -> String {
"Hello world!".to_owned() "Hello world!".to_owned()
} }
``` ```
@ -38,49 +42,24 @@ You can also change the signature to return `impl Responder` which works well if
complex types are involved. complex types are involved.
```rust ```rust
fn index(req: &HttpRequest) -> impl Responder { fn index(req: HttpRequest) -> impl Responder {
Bytes::from_static("Hello world!") Bytes::from_static("Hello world!")
} }
``` ```
```rust,ignore ```rust
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> { fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
... ...
} }
``` ```
*Handler* trait is generic over *S*, which defines the application state's type.
Application state is accessible from the handler with the `HttpRequest::state()` method;
however, state is accessible as a read-only reference. If you need mutable access to state,
it must be implemented.
> **Note**: Alternatively, the handler can use interior mutably to access its own
> state. **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="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`.
{{< 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.
## Response with custom type ## Response with custom type
To return a custom type directly from a handler function, the type needs to implement the `Responder` trait. To return a custom type directly from a handler function, the type needs to implement the `Responder` trait.
Let's create a response for a custom type that serializes to an `application/json` response: Let's create a response for a custom type that serializes to an `application/json` response:
{{< include-example example="responder-trait" file="main.rs" section="main" >}} {{< include-example example="responder-trait" file="main.rs" section="responder-trait" >}}
## Async handlers ## Async handlers
@ -89,12 +68,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: In this case, the handler must return a `Future` object that resolves to the *Responder* type, i.e:
{{< include-example example="async-handlers" file="main.rs" section="main" >}} {{< include-example example="async-handlers" file="main.rs" section="async-responder" >}}
Or the response body can be generated asynchronously. In this case, body Or the response body can be generated asynchronously. In this case, body
must implement the stream trait `Stream<Item=Bytes, Error=Error>`, i.e: must implement the stream trait `Stream<Item=Bytes, Error=Error>`, i.e:
{{< include-example example="async-handlers" file="stream.rs" section="main" >}} {{< include-example example="async-handlers" file="stream.rs" section="stream" >}}
Both methods can be combined. (i.e Async response with streaming body) Both methods can be combined. (i.e Async response with streaming body)
@ -102,7 +81,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 In this example, the `index` handler can return an error immediately or return a
future that resolves to a `HttpResponse`. future that resolves to a `HttpResponse`.
{{< include-example example="async-handlers" file="async_stream.rs" section="main" >}} {{< include-example example="async-handlers" file="async_stream.rs" section="async-stream" >}}
## Different return types (Either) ## Different return types (Either)
@ -112,4 +91,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. 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. `Either` allows combining two different responder types into a single type.
{{< include-example example="either" file="main.rs" section="main" >}} {{< include-example example="either" file="main.rs" section="either" >}}

View File

@ -1,9 +1,9 @@
[package] [package]
name = "async-handlers" name = "async-handlers"
version = "0.7.0" version = "1.0.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
actix-web = "0.7" actix-web = "1.0"
futures = "0.1" futures = "0.1"
bytes = "0.4" bytes = "0.4"

View File

@ -1,24 +1,28 @@
fn is_error() -> bool { fn is_error() -> bool {
true false
} }
// <main> // <async-stream>
use actix_web::{error, App, Error, HttpRequest, HttpResponse}; use actix_web::{error, web, App, Error, HttpRequest, HttpResponse, HttpServer};
use futures::future::{result, Future}; use futures::future::{result, Future};
fn index( fn index(
_req: &HttpRequest, _req: HttpRequest,
) -> Result<Box<Future<Item = HttpResponse, Error = Error>>, Error> { ) -> Result<Box<Future<Item = HttpResponse, Error = Error>>, Error> {
if is_error() { if is_error() {
Err(error::ErrorBadRequest("bad request")) Err(error::ErrorBadRequest("bad request"))
} else { } else {
Ok(Box::new(result(Ok(HttpResponse::Ok() Ok(Box::new(result(Ok(HttpResponse::Ok()
.content_type("text/html") .content_type("text/html")
.body(format!("Hello!")))))) .body("Hello!")))))
} }
} }
// </main> // </async-stream>
pub fn main() { pub fn main() {
App::new().resource("/", |r| r.route().f(index)).finish(); HttpServer::new(|| App::new().route("/", web::to_async(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }

View File

@ -1,25 +1,22 @@
mod async_stream; pub mod async_stream;
mod stream; pub mod stream;
// <main> // <async-responder>
use actix_web::{App, AsyncResponder, Error, HttpRequest, HttpResponse}; use actix_web::{web, App, Error, HttpRequest, HttpResponse};
use futures::future::{result, Future}; use futures::future::{ok, Future};
fn index(_req: &HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> { fn index(_req: HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
result(Ok(HttpResponse::Ok() Box::new(ok::<_, Error>(
.content_type("text/html") HttpResponse::Ok().content_type("text/html").body("Hello!"),
.body(format!("Hello!")))) ))
.responder()
} }
fn index2(_req: &HttpRequest) -> Box<Future<Item = &'static str, Error = Error>> { fn index2(_req: HttpRequest) -> Box<Future<Item = &'static str, Error = Error>> {
result(Ok("Welcome!")).responder() Box::new(ok::<_, Error>("Welcome!"))
} }
fn main() { fn main() {
App::new() App::new()
.resource("/async", |r| r.route().a(index)) .route("/async", web::to_async(index))
.resource("/", |r| r.route().a(index2)) .route("/", web::to_async(index2));
// .resource("/", |r| r.route().f(async_stream::index))
.finish();
} }
// </main> // </async-responder>

View File

@ -1,17 +1,21 @@
// <main> // <stream>
use actix_web::{App, Body, HttpRequest, HttpResponse}; use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
use bytes::Bytes; use bytes::Bytes;
use futures::stream::once; use futures::stream::once;
fn index(_req: &HttpRequest) -> HttpResponse { fn index(_req: HttpRequest) -> HttpResponse {
let body = once(Ok(Bytes::from_static(b"test"))); let body = once::<Bytes, Error>(Ok(Bytes::from_static(b"test")));
HttpResponse::Ok() HttpResponse::Ok()
.content_type("application/json") .content_type("application/json")
.body(Body::Streaming(Box::new(body))) .streaming(Box::new(body))
} }
pub fn main() { pub fn main() {
App::new().resource("/async", |r| r.f(index)).finish(); HttpServer::new(|| App::new().route("/async", web::to_async(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </main> // </stream>

View File

@ -1,4 +1,4 @@
// <main> // <either>
use actix_web::{web, App, Either, Error, HttpRequest, HttpResponse}; use actix_web::{web, App, Either, Error, HttpRequest, HttpResponse};
use futures::future::{ok, Future}; use futures::future::{ok, Future};
@ -22,7 +22,7 @@ fn index(_req: HttpRequest) -> RegisterResult {
fn main() { fn main() {
App::new().route("/", web::get().to(index)); App::new().route("/", web::get().to(index));
} }
// </main> // </either>
fn is_a_variant() -> bool { fn is_a_variant() -> bool {
true true

View File

@ -1,5 +1,5 @@
mod handlers_arc; mod handlers_arc;
// <main> // <handler>
use actix_web::{dev::Handler, server, App, HttpRequest, HttpResponse}; use actix_web::{dev::Handler, server, App, HttpRequest, HttpResponse};
use std::cell::Cell; use std::cell::Cell;
@ -22,4 +22,4 @@ fn main() {
.unwrap() .unwrap()
.run(); .run();
} }
// </main> // </handler>

View File

@ -1,7 +1,6 @@
// <main> // <responder-trait>
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder}; use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder};
use serde::Serialize; use serde::Serialize;
use serde_json;
#[derive(Serialize)] #[derive(Serialize)]
struct MyObj { struct MyObj {
@ -23,9 +22,10 @@ impl Responder for MyObj {
} }
} }
fn index(_req: HttpRequest) -> impl Responder { fn index() -> impl Responder {
MyObj { name: "user" } MyObj { name: "user" }
} }
// </responder-trait>
fn main() { fn main() {
HttpServer::new(|| App::new().route("/", web::get().to(index))) HttpServer::new(|| App::new().route("/", web::get().to(index)))
@ -34,4 +34,3 @@ fn main() {
.run() .run()
.unwrap(); .unwrap();
} }
// </main>