1
0
mirror of https://github.com/actix/actix-website synced 2024-11-27 18:12:57 +01:00
actix-website/content/docs/extractors.md
2018-05-25 15:51:16 -04:00

8.0 KiB

title menu weight
Extractors docs_basics 170

Type-safe information extraction

Actix provides facility for type-safe request information extraction. By default, actix provides several extractor implementations.

Accessing Extractors

How you access an Extractor depends on whether you are using a handler function or a custom Handler type.

Within Handler Functions

An Extractor can be passed to a handler function as a function parameter or accessed within the function by calling the ExtractorType::<...>::extract(req) function.


// Option 1:  passed as a parameter to a handler function
fn index(params: Path<(String, String,)>, info: Json<MyInfo>) -> HttpResponse {
   ... 
}


// Option 2:  accessed by calling extract() on the Extractor

fn index(req: HttpRequest) -> HttpResponse {
	let params = Path::<(String, String)>::extract(req);
	let info = Json::<MyInfo>::extract(req); 

	...
}

Within Custom Handler Types

Like a handler function, a custom Handler type can access an Extractor by calling the ExtractorType::<...>::extract(req) function. An Extractor cannot be passed as a parameter to a custom Handler type because a custom Handler type must follow the handle function signature specified by the Handler trait it implements.


struct MyHandler(String);

impl<S> Handler<S> for MyHandler {
    type Result = HttpResponse;

    /// Handle request
    fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
		let params = Path::<(String, String)>::extract(req);
		let info = Json::<MyInfo>::extract(req); 

		...
			
        HttpResponse::Ok().into()
    }
}

Path

Path provides information that can be extracted from the Request's path. You can deserialize any variable segment from the path.

For instance, for resource that registered for /users/{userid}/{friend} path two segments could be deserialized, userid and friend. This segments could be extracted to a tuple, i.e. Path<(u32, String)> or structure that implementd Deserialize trait from serde crate.

use actix_web::{App, Path, Result, http};

/// extract path info from "/users/{userid}/{friend}" url
/// {userid} -  - deserializes to a u32
/// {friend} - deserializes to a String
fn index(info: Path<(u32, String)>) -> Result<String> {
    Ok(format!("Welcome {}! {}", info.1, info.0))
}

fn main() {
    let app = App::new().resource(
        "/users/{userid}/{friend}",                    // <- define path parameters
        |r| r.method(http::Method::GET).with(index));  // <- use `with` extractor
}

Remember! handler function that uses extractors has to be registered with Route::with() method.

It is also possible to extract path information to a specific type that implements Deserialize trait from serde. Here is equivalent example that uses serde instead of tuple type.

#[macro_use] extern crate serde_derive;
use actix_web::{App, Path, Result, http};

#[derive(Deserialize)]
struct Info {
    userid: u32,
    friend: String,
}

/// extract path info using serde
fn index(info: Path<Info>) -> Result<String> {
     Ok(format!("Welcome {}!", info.friend))
}

fn main() {
    let app = App::new().resource(
       "/users/{userid}/{friend}",                    // <- define path parameters
       |r| r.method(http::Method::GET).with(index));  // <- use `with` extractor
}

Query

Same can be done with the request's query. Query type provides extraction functionality. Underneath it uses serde_urlencoded crate.

#[macro_use] extern crate serde_derive;
use actix_web::{App, Query, http};

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

// this handler get called only if the request's query contains `username` field
fn index(info: Query<Info>) -> String {
    format!("Welcome {}!", info.username)
}

fn main() {
    let app = App::new().resource(
       "/index.html",
       |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}

Json

Json allows to deserialize request body to a struct. To extract typed information from request's body, the type T must implement the Deserialize trait from serde.

#[macro_use] extern crate serde_derive;
use actix_web::{App, Json, Result, http};

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

/// deserialize `Info` from request's body
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
}

Some extractors provide a way to configure extraction process. Json extractor JsonConfig type for configuration. When you register handler Route::with() returns configuration instance. In case of Json extractor it returns JsonConfig. You can configure max size of the json payload and custom error handler function.

Following example limits size of the payload to 4kb and uses custom error hander.

#[macro_use] extern crate serde_derive;
use actix_web::{App, Json, HttpResponse, Result, http, error};

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

/// deserialize `Info` from request's body, max payload size is 4kb
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)
              .limit(4096)   // <- change json extractor configuration
              .error_handler(|err, req| {  // <- create custom error response
                  error::InternalError::from_response(
                     err, HttpResponse::Conflict().finish()).into()
              });
       });
}

Form

At the moment only url-encoded forms are supported. Url encoded body could be extracted to a specific type. This type must implement the Deserialize trait from serde crate.

FormConfig allows to configure extraction process.

#[macro_use] extern crate serde_derive;
use actix_web::{App, Form, Result};

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

/// extract form data using serde
/// this handler get called only if content type is *x-www-form-urlencoded*
/// and content of the request could be deserialized to a `FormData` struct
fn index(form: Form<FormData>) -> Result<String> {
     Ok(format!("Welcome {}!", form.username))
}
# fn main() {}

Multiple extractors

Actix provides extractor implementation for tuples (up to 10 elements) which elements provide FromRequest impl.

For example we can use path extractor and query extractor at the same time.

#[macro_use] extern crate serde_derive;
use actix_web::{App, Query, Path, http};

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

fn index(data: (Path<(u32, String)>, Query<Info>)) -> String {
    let (path, query) = data;
    format!("Welcome {}!", query.username)
}

fn main() {
    let app = App::new().resource(
       "/users/{userid}/{friend}",                    // <- define path parameters
       |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}

Other

Actix also provides several other extractors:

  • State - If you need access to an application state. This is similar to a HttpRequest::state().
  • HttpRequest - HttpRequest itself is an extractor which returns self. In case if you need access to the request.
  • String - You can convert request's payload to a String. Example is available in doc strings.
  • bytes::Bytes - You can convert request's payload to a Bytes. Example is available in doc strings.