1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-25 00:12:59 +01:00
actix-extras/guide/src/qs_7.md

298 lines
9.4 KiB
Markdown
Raw Normal View History

# Request & Response
2017-12-02 20:41:20 +01:00
2017-12-05 05:38:38 +01:00
## Response
Builder-like patter is used to construct an instance of `HttpResponse`.
`HttpResponse` provides several method that returns `HttpResponseBuilder` instance,
which is implements various convinience methods that helps build response.
Check [documentation](../actix_web/dev/struct.HttpResponseBuilder.html)
2017-12-26 20:19:08 +01:00
for type description. Methods `.body`, `.finish`, `.json` finalizes response creation and
returns constructed *HttpResponse* instance. if this methods get called for the same
builder instance multiple times, builder will panic.
2017-12-05 05:38:38 +01:00
```rust
# extern crate actix_web;
use actix_web::*;
2017-12-08 01:40:29 +01:00
use actix_web::headers::ContentEncoding;
2017-12-05 05:38:38 +01:00
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.content_encoding(ContentEncoding::Br)
.content_type("plain/text")
.header("X-Hdr", "sample")
.body("data").unwrap()
}
# fn main() {}
```
2017-12-02 20:41:20 +01:00
## Content encoding
2017-12-05 05:38:38 +01:00
Actix automatically *compress*/*decompress* payload. Following codecs are supported:
2017-12-02 20:41:20 +01:00
* Brotli
* Gzip
* Deflate
* Identity
If request headers contains `Content-Encoding` header, request payload get decompressed
according to header value. Multiple codecs are not supported, i.e: `Content-Encoding: br, gzip`.
2017-12-04 05:09:46 +01:00
Response payload get compressed based on *content_encoding* parameter.
By default `ContentEncoding::Auto` is used. If `ContentEncoding::Auto` is selected
then compression depends on request's `Accept-Encoding` header.
`ContentEncoding::Identity` could be used to disable compression.
If other content encoding is selected the compression is enforced for this codec. For example,
to enable `brotli` response's body compression use `ContentEncoding::Br`:
```rust
2017-12-05 01:26:40 +01:00
# extern crate actix_web;
2017-12-04 05:09:46 +01:00
use actix_web::*;
2017-12-08 01:40:29 +01:00
use actix_web::headers::ContentEncoding;
2017-12-04 03:58:15 +01:00
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.content_encoding(ContentEncoding::Br)
2017-12-04 05:09:46 +01:00
.body("data").unwrap()
2017-12-04 03:58:15 +01:00
}
2017-12-04 05:09:46 +01:00
# fn main() {}
2017-12-04 03:58:15 +01:00
```
2017-12-21 00:12:43 +01:00
## JSON Request
2017-12-21 05:30:54 +01:00
There are two options of json body deserialization.
2017-12-21 00:12:43 +01:00
2017-12-21 05:30:54 +01:00
First option is to use *HttpResponse::json()* method. This method returns
[*JsonBody*](../actix_web/dev/struct.JsonBody.html) object which resolves into
deserialized value.
2017-12-21 00:12:43 +01:00
2017-12-21 05:30:54 +01:00
```rust
# extern crate actix;
# extern crate actix_web;
# extern crate futures;
# extern crate serde_json;
# #[macro_use] extern crate serde_derive;
# use actix_web::*;
# use futures::Future;
#[derive(Debug, Serialize, Deserialize)]
2017-12-21 00:12:43 +01:00
struct MyObj {
name: String,
number: i32,
}
2017-12-21 05:30:54 +01:00
fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req.json().from_err()
.and_then(|val: MyObj| {
println!("model: {:?}", val);
Ok(httpcodes::HTTPOk.build().json(val)?) // <- send response
})
.responder()
}
# fn main() {}
2017-12-21 00:12:43 +01:00
```
2017-12-21 05:30:54 +01:00
Or you can manually load payload into memory and ther deserialize it.
Here is simple example. We will deserialize *MyObj* struct. We need to load request
body first and then deserialize json into object.
2017-12-21 00:12:43 +01:00
2017-12-25 17:12:13 +01:00
```rust
# extern crate actix_web;
# extern crate futures;
# use actix_web::*;
# #[macro_use] extern crate serde_derive;
extern crate serde_json;
use futures::{Future, Stream};
#[derive(Serialize, Deserialize)]
struct MyObj {name: String, number: i32}
fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
2017-12-21 01:05:07 +01:00
// `concat2` will asynchronously read each chunk of the request body and
// return a single, concatenated, chunk
req.payload_mut().readany().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
2017-12-21 01:13:21 +01:00
.and_then(|body| { // <- body is loaded, now we can deserialize json
let obj = serde_json::from_slice::<MyObj>(&body)?;
Ok(httpcodes::HTTPOk.build().json(obj)?) // <- send response
2017-12-21 01:05:07 +01:00
})
2017-12-25 17:12:13 +01:00
.responder()
2017-12-21 00:12:43 +01:00
}
2017-12-25 17:12:13 +01:00
# fn main() {}
2017-12-21 00:12:43 +01:00
```
2017-12-25 17:12:13 +01:00
Complete example for both options is available in
2017-12-21 00:12:43 +01:00
[examples directory](https://github.com/actix/actix-web/tree/master/examples/json/).
2017-12-04 03:51:52 +01:00
## JSON Response
The `Json` type allows you to respond with well-formed JSON data: simply return a value of
type Json<T> where T is the type of a structure to serialize into *JSON*. The
type `T` must implement the `Serialize` trait from *serde*.
```rust
2017-12-05 01:26:40 +01:00
# extern crate actix_web;
2017-12-04 03:51:52 +01:00
#[macro_use] extern crate serde_derive;
use actix_web::*;
#[derive(Serialize)]
struct MyObj {
name: String,
}
fn index(req: HttpRequest) -> Result<Json<MyObj>> {
Ok(Json(MyObj{name: req.match_info().query("name")?}))
}
fn main() {
Application::new()
.resource(r"/a/{name}", |r| r.method(Method::GET).f(index))
2017-12-04 03:51:52 +01:00
.finish();
}
```
2017-12-14 07:36:28 +01:00
## Chunked transfer encoding
Actix automatically decode *chunked* encoding. `HttpRequest::payload()` already contains
decoded bytes stream. If request payload compressed with one of supported
compression codecs (br, gzip, deflate) bytes stream get decompressed.
Chunked encoding on response could be enabled with `HttpResponseBuilder::chunked()` method.
But this takes effect only for `Body::Streaming(BodyStream)` or `Body::StreamingContext` bodies.
Also if response payload compression is enabled and streaming body is used, chunked encoding
get enabled automatically.
Enabling chunked encoding for *HTTP/2.0* responses is forbidden.
```rust
# extern crate actix_web;
use actix_web::*;
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.chunked()
2017-12-19 09:18:57 +01:00
.body(Body::Streaming(payload::Payload::empty().stream())).unwrap()
2017-12-14 07:36:28 +01:00
}
# fn main() {}
```
## Multipart body
2017-12-19 19:10:03 +01:00
Actix provides multipart stream support.
[*Multipart*](../actix_web/multipart/struct.Multipart.html) is implemented as
a stream of multipart items, each item could be
[*Field*](../actix_web/multipart/struct.Field.html) or nested *Multipart* stream.
`HttpResponse::multipart()` method returns *Multipart* stream for current request.
2017-12-19 18:55:49 +01:00
2017-12-19 19:10:03 +01:00
In simple form multipart stream handling could be implemented similar to this example
2017-12-19 18:55:49 +01:00
2017-12-19 19:10:03 +01:00
```rust,ignore
# extern crate actix_web;
use actix_web::*;
fn index(req: HttpRequest) -> Box<Future<...>> {
req.multipart() // <- get multipart stream for current request
.and_then(|item| { // <- iterate over multipart items
match item {
// Handle multipart Field
multipart::MultipartItem::Field(field) => {
println!("==== FIELD ==== {:?} {:?}", field.heders(), field.content_type());
Either::A(
// Field in turn is a stream of *Bytes* objects
field.map(|chunk| {
println!("-- CHUNK: \n{}",
std::str::from_utf8(&chunk).unwrap());})
.fold((), |_, _| result(Ok(()))))
},
multipart::MultipartItem::Nested(mp) => {
// Or item could be nested Multipart stream
Either::B(result(Ok(())))
}
}
})
}
```
Full example is available in
[examples directory](https://github.com/actix/actix-web/tree/master/examples/multipart/).
2017-12-14 07:36:28 +01:00
## Urlencoded body
2017-12-19 23:03:01 +01:00
Actix provides support for *application/x-www-form-urlencoded* encoded body.
`HttpResponse::urlencoded()` method returns
[*UrlEncoded*](../actix_web/dev/struct.UrlEncoded.html) future, it resolves
into `HashMap<String, String>` which contains decoded parameters.
*UrlEncoded* future can resolve into a error in several cases:
* content type is not `application/x-www-form-urlencoded`
* transfer encoding is `chunked`.
* content-length is greater than 256k
* payload terminates with error.
```rust
# extern crate actix_web;
# extern crate futures;
use actix_web::*;
use futures::future::{Future, ok};
fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
2017-12-21 06:06:04 +01:00
req.urlencoded() // <- get UrlEncoded future
.from_err()
.and_then(|params| { // <- url encoded parameters
println!("==== BODY ==== {:?}", params);
2018-01-01 02:26:32 +01:00
ok(httpcodes::HTTPOk.into())
2017-12-21 06:06:04 +01:00
})
.responder()
2017-12-19 23:03:01 +01:00
}
# fn main() {}
```
2017-12-14 07:36:28 +01:00
## Streaming request
2017-12-19 09:29:25 +01:00
Actix uses [*Payload*](../actix_web/payload/struct.Payload.html) object as request payload stream.
2017-12-19 09:18:57 +01:00
*HttpRequest* provides several methods, which can be used for payload access.
At the same time *Payload* implements *Stream* trait, so it could be used with various
stream combinators. Also *Payload* provides serveral convinience methods that return
future object that resolve to Bytes object.
2017-12-28 04:02:29 +01:00
* *readany()* method returns *Stream* of *Bytes* objects.
2017-12-19 09:18:57 +01:00
2017-12-28 04:02:29 +01:00
* *readexactly()* method returns *Future* that resolves when specified number of bytes
2017-12-19 09:18:57 +01:00
get received.
2017-12-28 04:02:29 +01:00
* *readline()* method returns *Future* that resolves when `\n` get received.
2017-12-19 09:18:57 +01:00
2017-12-28 04:02:29 +01:00
* *readuntil()* method returns *Future* that resolves when specified bytes string
2017-12-19 09:18:57 +01:00
matches in input bytes stream
2017-12-19 18:51:28 +01:00
In this example handle reads request payload chunk by chunk and prints every chunk.
2017-12-19 09:18:57 +01:00
```rust
# extern crate actix_web;
# extern crate futures;
# use futures::future::result;
use actix_web::*;
use futures::{Future, Stream};
fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
2017-12-21 06:06:04 +01:00
req.payload_mut()
.readany()
.from_err()
.fold((), |_, chunk| {
println!("Chunk: {:?}", chunk);
result::<_, error::PayloadError>(Ok(()))
})
.map(|_| HttpResponse::Ok().finish().unwrap())
.responder()
2017-12-19 09:18:57 +01:00
}
# fn main() {}
```