1
0
mirror of https://github.com/actix/actix-website synced 2025-01-22 16:15:56 +01:00

First pass at Requests Chapter.

This commit is contained in:
Cameron Dershem 2019-06-17 14:34:23 -04:00
parent f07c78a5ca
commit 507842bf1c
9 changed files with 173 additions and 129 deletions

View File

@ -9,9 +9,13 @@ weight: 200
Actix automatically *decompresses* payloads. The following codecs are supported:
* Brotli
* Chunked
* Compress
* Gzip
* Deflate
* Identity
* Trailers
* EncodingExt
If request headers contain a `Content-Encoding` header, the request payload is decompressed
according to the header value. Multiple codecs are not supported,
@ -22,80 +26,18 @@ i.e: `Content-Encoding: br, gzip`.
There are several options for json body deserialization.
The first option is to use *Json* extractor. First, you define a handler function
that accepts `Json<T>` as a parameter, then, you use the `.with()` method for registering
that accepts `Json<T>` as a parameter, then, you use the `.to()` method for registering
this handler. It is also possible to accept arbitrary valid json object by
using `serde_json::Value` as a type `T`.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Json, Result, http};
#[derive(Deserialize)]
struct Info {
username: String,
}
/// extract `Info` using serde
fn index(info: Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new().resource(
"/index.html",
|r| r.method(http::Method::POST).with(index)); // <- use `with` extractor
}
```
Another option is to use *HttpRequest::json()*. This method returns a
[*JsonBody*](../../actix-web/actix_web/dev/struct.JsonBody.html) object which resolves into
the deserialized value.
```rust
#[derive(Debug, Serialize, Deserialize)]
struct MyObj {
name: String,
number: i32,
}
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req.json().from_err()
.and_then(|val: MyObj| {
println!("model: {:?}", val);
Ok(HttpResponse::Ok().json(val)) // <- send response
})
.responder()
}
```
{{< include-example example="requests" file="main.rs" section="json-request" >}}
You may also manually load the payload into memory and then deserialize it.
In the following example, we will deserialize a *MyObj* struct. We need to load the request
body first and then deserialize the json into an object.
```rust
extern crate serde_json;
use futures::{Future, Stream};
#[derive(Serialize, Deserialize)]
struct MyObj {name: String, number: i32}
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
// `concat2` will asynchronously read each chunk of the request body and
// return a single, concatenated, chunk
req.concat2()
// `Future::from_err` acts like `?` in that it coerces the error type from
// the future into the final error type
.from_err()
// `Future::and_then` can be used to merge an asynchronous workflow with a
// synchronous workflow
.and_then(|body| {
let obj = serde_json::from_slice::<MyObj>(&body)?;
Ok(HttpResponse::Ok().json(obj))
})
.responder()
}
```
{{< include-example example="requests" file="manual.rs" section="json-manual" >}}
> A complete example for both options is available in
> [examples directory](https://github.com/actix/examples/tree/master/json/).
@ -117,31 +59,7 @@ for the current request.
The following demonstrates multipart stream handling for a simple form:
```rust
use actix_web::*;
fn index(req: &HttpRequest) -> Box<Future<...>> {
// get multipart and iterate over multipart items
req.multipart()
.and_then(|item| {
match item {
multipart::MultipartItem::Field(field) => {
println!("==== FIELD ==== {:?} {:?}",
field.headers(),
field.content_type());
Either::A(
field.map(|chunk| {
println!("-- CHUNK: \n{}",
std::str::from_utf8(&chunk).unwrap());})
.fold((), |_, _| result(Ok(()))))
},
multipart::MultipartItem::Nested(mp) => {
Either::B(result(Ok(())))
}
}
})
}
```
{{< include-example example="requests" file="multipart.rs" section="multipart" >}}
> A full example is available in the
> [examples directory](https://github.com/actix/examples/tree/master/multipart/).
@ -161,27 +79,7 @@ The *UrlEncoded* future can resolve into an error in several cases:
* content-length is greater than 256k
* payload terminates with error.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::*;
use futures::future::{Future, ok};
#[derive(Deserialize)]
struct FormData {
username: String,
}
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req.urlencoded::<FormData>() // <- get UrlEncoded future
.from_err()
.and_then(|data| { // <- deserialized instance
println!("USERNAME: {:?}", data.username);
ok(HttpResponse::Ok().into())
})
.responder()
}
# fn main() {}
```
{{< include-example example="requests" file="urlencoded.rs" section="urlencoded" >}}
# Streaming request
@ -190,20 +88,4 @@ body payload.
In the following example, we read and print the request payload chunk by chunk:
```rust
use actix_web::*;
use futures::{Future, Stream};
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req
.payload()
.from_err()
.fold((), |_, chunk| {
println!("Chunk: {:?}", chunk);
result::<_, error::PayloadError>(Ok(()))
})
.map(|_| HttpResponse::Ok().finish())
.responder()
}
```
{{< include-example example="requests" file="streaming.rs" section="streaming" >}}

View File

@ -17,5 +17,6 @@ exclude = [
"async-handlers",
"extractors",
"autoreload",
"errors"
"errors",
"requests",
]

View File

@ -0,0 +1,12 @@
[package]
name = "requests"
version = "0.1.0"
authors = ["Cameron Dershem <cameron@pinkhatbeard.com>"]
edition = "2018"
[dependencies]
serde = "1.0"
serde_json = "1.0"
actix-web = "1.0"
futures = "0.1"
bytes = "0.4"

View File

@ -0,0 +1,21 @@
// // <json-two>
// use actix_web::{error::Error, HttpRequest, HttpResponse};
// use futures::Future;
// use serde::{Deserialize, Serialize};
// #[derive(Debug, Serialize, Deserialize)]
// struct MyObj {
// name: String,
// number: i32,
// }
// fn index(req: HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
// req.json()
// .from_err()
// .and_then(|val: MyObj| {
// println!("model: {:?}", val);
// Ok(HttpResponse::Ok().json(val)) // <- send response
// })
// .responder()
// }
// // </json-two>

View File

@ -0,0 +1,23 @@
mod json_two;
mod manual;
mod multipart;
mod streaming;
mod urlencoded;
// <json-request>
use actix_web::{web, App, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
/// extract `Info` using serde
fn index(info: web::Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
App::new().route("/index.html", web::post().to(index));
}
// </json-request>

View File

@ -0,0 +1,43 @@
// <json-manual>
use actix_web::{error, web, Error, HttpResponse};
use bytes::BytesMut;
use futures::{Future, Stream};
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Serialize, Deserialize)]
struct MyObj {
name: String,
number: i32,
}
const MAX_SIZE: usize = 262_144; // max payload size is 256k
fn index_manual(
payload: web::Payload,
) -> impl Future<Item = HttpResponse, Error = Error> {
// payload is a stream of Bytes objects
payload
// `Future::from_err` acts like `?` in that it coerces the error type from
// the future into the final error type
.from_err()
// `fold` will asynchronously read each chunk of the request body and
// call supplied closure, then it resolves to result of closure
.fold(BytesMut::new(), move |mut body, chunk| {
// limit max size of in-memory payload
if (body.len() + chunk.len()) > MAX_SIZE {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
// `Future::and_then` can be used to merge an asynchronous workflow with a
// synchronous workflow
.and_then(|body| {
// body is loaded, now we can deserialize serde-json
let obj = serde_json::from_slice::<MyObj>(&body)?;
Ok(HttpResponse::Ok().json(obj)) // <- send response
})
}
// </json-manual>

View File

@ -0,0 +1,25 @@
// <multipart>
// use actix_web::{error, Error, HttpRequest, HttpResponse};
// use futures::Future;
// fn index(req: HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
// // get multipart and iterate over multipart items
// req.multipart().and_then(|item| match item {
// multipart::MultipartItem::Field(field) => {
// println!(
// "==== FIELD ==== {:?} {:?}",
// field.headers(),
// field.content_type()
// );
// Either::A(
// field
// .map(|chunk| {
// println!("-- CHUNK: \n{}", std::str::from_utf8(&chunk).unwrap());
// })
// .fold((), |_, _| result(Ok(()))),
// )
// }
// multipart::MultipartItem::Nested(mp) => Either::B(result(Ok(()))),
// })
// }
// </multipart>

View File

@ -0,0 +1,15 @@
// <streaming>
// use actix_web::{error, web, Error, HttpResponse};
// use futures::{future::result, Future, Stream};
// fn index(payload: web::Payload) -> Box<Future<Item = HttpResponse, Error = Error>> {
// payload
// .from_err()
// .fold((), |_, chunk| {
// println!("Chunk: {:?}", chunk);
// result::<_, error::PayloadError>(Ok(()))
// })
// .map(|_| HttpResponse::Ok().finish())
// .responder()
// }
// </streaming>

View File

@ -0,0 +1,22 @@
// <urlencoded>
// use actix_web::{Error, HttpRequest, HttpResponse};
// use futures::future::{ok, Future};
// use serde::Deserialize;
// #[derive(Deserialize)]
// struct FormData {
// username: String,
// }
// fn index(req: &HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
// req.urlencoded::<FormData>() // <- get UrlEncoded future
// .from_err()
// .and_then(|data| {
// // <- deserialized instance
// println!("USERNAME: {:?}", data.username);
// ok(HttpResponse::Ok().into())
// })
// .responder()
// }
// </urlencoded>
fn main() {}