1
0
mirror of https://github.com/actix/actix-website synced 2025-02-02 12:19:04 +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: Actix automatically *decompresses* payloads. The following codecs are supported:
* Brotli * Brotli
* Chunked
* Compress
* Gzip * Gzip
* Deflate * Deflate
* Identity * Identity
* Trailers
* EncodingExt
If request headers contain a `Content-Encoding` header, the request payload is decompressed If request headers contain a `Content-Encoding` header, the request payload is decompressed
according to the header value. Multiple codecs are not supported, 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. There are several options for json body deserialization.
The first option is to use *Json* extractor. First, you define a handler function 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 this handler. It is also possible to accept arbitrary valid json object by
using `serde_json::Value` as a type `T`. using `serde_json::Value` as a type `T`.
```rust {{< include-example example="requests" file="main.rs" section="json-request" >}}
#[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()
}
```
You may also manually load the payload into memory and then deserialize it. 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 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. body first and then deserialize the json into an object.
```rust {{< include-example example="requests" file="manual.rs" section="json-manual" >}}
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()
}
```
> A complete example for both options is available in > A complete example for both options is available in
> [examples directory](https://github.com/actix/examples/tree/master/json/). > [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: The following demonstrates multipart stream handling for a simple form:
```rust {{< include-example example="requests" file="multipart.rs" section="multipart" >}}
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(())))
}
}
})
}
```
> A full example is available in the > A full example is available in the
> [examples directory](https://github.com/actix/examples/tree/master/multipart/). > [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 * content-length is greater than 256k
* payload terminates with error. * payload terminates with error.
```rust {{< include-example example="requests" file="urlencoded.rs" section="urlencoded" >}}
#[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() {}
```
# Streaming request # Streaming request
@ -190,20 +88,4 @@ body payload.
In the following example, we read and print the request payload chunk by chunk: In the following example, we read and print the request payload chunk by chunk:
```rust {{< include-example example="requests" file="streaming.rs" section="streaming" >}}
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()
}
```

View File

@ -17,5 +17,6 @@ exclude = [
"async-handlers", "async-handlers",
"extractors", "extractors",
"autoreload", "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() {}