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:
parent
f07c78a5ca
commit
507842bf1c
@ -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()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -17,5 +17,6 @@ exclude = [
|
|||||||
"async-handlers",
|
"async-handlers",
|
||||||
"extractors",
|
"extractors",
|
||||||
"autoreload",
|
"autoreload",
|
||||||
"errors"
|
"errors",
|
||||||
|
"requests",
|
||||||
]
|
]
|
||||||
|
12
examples/requests/Cargo.toml
Normal file
12
examples/requests/Cargo.toml
Normal 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"
|
21
examples/requests/src/json_two.rs
Normal file
21
examples/requests/src/json_two.rs
Normal 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>
|
23
examples/requests/src/main.rs
Normal file
23
examples/requests/src/main.rs
Normal 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>
|
43
examples/requests/src/manual.rs
Normal file
43
examples/requests/src/manual.rs
Normal 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>
|
25
examples/requests/src/multipart.rs
Normal file
25
examples/requests/src/multipart.rs
Normal 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>
|
15
examples/requests/src/streaming.rs
Normal file
15
examples/requests/src/streaming.rs
Normal 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>
|
22
examples/requests/src/urlencoded.rs
Normal file
22
examples/requests/src/urlencoded.rs
Normal 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() {}
|
Loading…
x
Reference in New Issue
Block a user