1
0
mirror of https://github.com/actix/actix-website synced 2024-12-18 09:53:12 +01:00
actix-website/content/docs/request.cn.md
2018-06-22 23:18:14 +08:00

6.1 KiB

title menu weight
Requests docs_advanced 200

Content Encoding

Actix automatically decompresses payloads. The following codecs are supported:

  • Brotli
  • Gzip
  • Deflate
  • Identity

If request headers contain a Content-Encoding header, the request payload is decompressed according to the header value. Multiple codecs are not supported, i.e: Content-Encoding: br, gzip.

JSON Request

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 this handler. It is also possible to accept arbitrary valid json object by using serde_json::Value as a type T.

#[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 object which resolves into the deserialized value.

#[derive(Debug, Serialize, Deserialize)]
struct MyObj {
    name: String,
    number: i32,
}

fn index(mut 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.

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.

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 examples directory.

Chunked transfer encoding

Actix automatically decodes chunked encoding. HttpRequest::payload() already contains the decoded byte stream. If the request payload is compressed with one of the supported compression codecs (br, gzip, deflate), then the byte stream is decompressed.

Multipart body

Actix provides multipart stream support. Multipart is implemented as a stream of multipart items. Each item can be a Field or a nested Multipart stream.HttpResponse::multipart() returns the Multipart stream for the current request.

The following demonstrates multipart stream handling for a simple form:

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 examples directory.

Urlencoded body

Actix provides support for application/x-www-form-urlencoded encoded bodies. HttpResponse::urlencoded() returns a UrlEncoded future, which resolves to the deserialized instance. The type of the instance must implement the Deserialize trait from serde.

The UrlEncoded future can resolve into an 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.
#[macro_use] extern crate serde_derive;
use actix_web::*;
use futures::future::{Future, ok};

#[derive(Deserialize)]
struct FormData {
    username: String,
}

fn index(mut 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

HttpRequest is a stream of Bytes objects. It can be used to read the request body payload.

In the following example, we read and print the request payload chunk by chunk:

use actix_web::*;
use futures::{Future, Stream};


fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
    req.from_err()
       .fold((), |_, chunk| {
            println!("Chunk: {:?}", chunk);
            result::<_, error::PayloadError>(Ok(()))
        })
       .map(|_| HttpResponse::Ok().finish())
       .responder()
}