diff --git a/.travis.yml b/.travis.yml index 908044127..a69ce1881 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,27 +33,12 @@ before_install: # Add clippy before_script: - - | - if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then - ( ( cargo install clippy && export CLIPPY=true ) || export CLIPPY=false ); - fi - export PATH=$PATH:~/.cargo/bin script: - | - if [[ "$TRAVIS_RUST_VERSION" == "stable" ]]; then - cargo clean - USE_SKEPTIC=1 cargo test --features=alpn - else - cargo clean - cargo test -- --nocapture - # --features=alpn - fi - - - | - if [[ "$TRAVIS_RUST_VERSION" == "nightly" && $CLIPPY ]]; then - cargo clippy - fi + cargo clean + cargo test --features="alpn,tls" -- --nocapture # Upload docs after_success: @@ -61,8 +46,6 @@ after_success: if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_RUST_VERSION" == "beta" ]]; then cargo doc --features "alpn, tls, session" --no-deps && echo "" > target/doc/index.html && - curl -sL https://github.com/rust-lang-nursery/mdBook/releases/download/v0.1.2/mdbook-v0.1.2-x86_64-unknown-linux-gnu.tar.gz | tar xvz -C $HOME/.cargo/bin && - cd guide && mdbook build -d ../target/doc/guide && cd .. && git clone https://github.com/davisp/ghp-import.git && ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://"$GH_TOKEN"@github.com/"$TRAVIS_REPO_SLUG.git" target/doc && echo "Uploaded documentation" diff --git a/README.md b/README.md index 9eb11248c..6e07e57c1 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,17 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. * Multipart streams * Static assets * SSL support with OpenSSL or `native-tls` -* Middlewares ([Logger](https://actix.rs/actix-web/guide/qs_10.html#logging), - [Session](https://actix.rs/actix-web/guide/qs_10.html#user-sessions), +* Middlewares ([Logger](https://actix.rs/book/actix-web/sec-9-middlewares.html#logging), + [Session](https://actix.rs/book/actix-web/sec-9-middlewares.html#user-sessions), [Redis sessions](https://github.com/actix/actix-redis), - [DefaultHeaders](https://actix.rs/actix-web/guide/qs_10.html#default-headers), + [DefaultHeaders](https://actix.rs/book/actix-web/sec-9-middlewares.html#default-headers), [CORS](https://actix.rs/actix-web/actix_web/middleware/cors/index.html), [CSRF](https://actix.rs/actix-web/actix_web/middleware/csrf/index.html)) * Built on top of [Actix actor framework](https://github.com/actix/actix) ## Documentation & community resources -* [User Guide](https://actix.rs/actix-web/guide/) +* [User Guide](https://actix.rs/book/actix-web/) * [API Documentation (Development)](https://actix.rs/actix-web/actix_web/) * [API Documentation (Releases)](https://docs.rs/actix-web/) * [Chat on gitter](https://gitter.im/actix/actix) diff --git a/guide/book.toml b/guide/book.toml deleted file mode 100644 index 5549978d7..000000000 --- a/guide/book.toml +++ /dev/null @@ -1,7 +0,0 @@ -[book] -title = "Actix web" -description = "Actix web framework guide" -author = "Actix Project and Contributors" - -[output.html] -google-analytics = "UA-110322332-1" diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md deleted file mode 100644 index d76840f9c..000000000 --- a/guide/src/SUMMARY.md +++ /dev/null @@ -1,16 +0,0 @@ -# Summary - -[Quickstart](./qs_1.md) -- [Getting Started](./qs_2.md) -- [Application](./qs_3.md) -- [Server](./qs_3_5.md) -- [Handler](./qs_4.md) -- [Errors](./qs_4_5.md) -- [URL Dispatch](./qs_5.md) -- [Request & Response](./qs_7.md) -- [Testing](./qs_8.md) -- [Middlewares](./qs_10.md) -- [Static file handling](./qs_12.md) -- [WebSockets](./qs_9.md) -- [HTTP/2](./qs_13.md) -- [Database integration](./qs_14.md) diff --git a/guide/src/qs_1.md b/guide/src/qs_1.md deleted file mode 100644 index b5c3ca0ff..000000000 --- a/guide/src/qs_1.md +++ /dev/null @@ -1,32 +0,0 @@ -# Quick start - -## Install Rust - -Before we begin, we need to install Rust using [rustup](https://www.rustup.rs/): - -```bash -curl https://sh.rustup.rs -sSf | sh -``` - -If you already have rustup installed, run this command to ensure you have the latest version of Rust: - -```bash -rustup update -``` - -Actix web framework requires rust version 1.21 and up. - -## Running Examples - -The fastest way to start experimenting with actix web is to clone the -[repository](https://github.com/actix/actix-web) and run the included examples. - -The following set of commands runs the `basics` example: - -```bash -git clone https://github.com/actix/example -cd examples/basics -cargo run -``` - -Check [examples/](https://github.com/actix/examples/tree/master/) directory for more examples. diff --git a/guide/src/qs_10.md b/guide/src/qs_10.md deleted file mode 100644 index 3cbd0938c..000000000 --- a/guide/src/qs_10.md +++ /dev/null @@ -1,251 +0,0 @@ -# Middleware - -Actix's middleware system allows us to add additional behavior to request/response processing. -Middleware can hook into an incoming request process, enabling us to modify requests -as well as halt request processing to return a response early. - -Middleware can also hook into response processing. - -Typically, middleware is involved in the following actions: - -* Pre-process the Request -* Post-process a Response -* Modify application state -* Access external services (redis, logging, sessions) - -Middleware is registered for each application and executed in same order as -registration. In general, a *middleware* is a type that implements the -[*Middleware trait*](../actix_web/middlewares/trait.Middleware.html). Each method -in this trait has a default implementation. Each method can return a result immediately -or a *future* object. - -The following demonstrates using middleware to add request and response headers: - -```rust -# extern crate http; -# extern crate actix_web; -use http::{header, HttpTryFrom}; -use actix_web::{App, HttpRequest, HttpResponse, Result}; -use actix_web::middleware::{Middleware, Started, Response}; - -struct Headers; // <- Our middleware - -/// Middleware implementation, middlewares are generic over application state, -/// so you can access state with `HttpRequest::state()` method. -impl Middleware for Headers { - - /// Method is called when request is ready. It may return - /// future, which should resolve before next middleware get called. - fn start(&self, req: &mut HttpRequest) -> Result { - req.headers_mut().insert( - header::CONTENT_TYPE, header::HeaderValue::from_static("text/plain")); - Ok(Started::Done) - } - - /// Method is called when handler returns response, - /// but before sending http message to peer. - fn response(&self, req: &mut HttpRequest, mut resp: HttpResponse) -> Result { - resp.headers_mut().insert( - header::HeaderName::try_from("X-VERSION").unwrap(), - header::HeaderValue::from_static("0.2")); - Ok(Response::Done(resp)) - } -} - -fn main() { - App::new() - .middleware(Headers) // <- Register middleware, this method can be called multiple times - .resource("/", |r| r.f(|_| HttpResponse::Ok())); -} -``` - -> Actix provides several useful middlewares, such as *logging*, *user sessions*, etc. - -## Logging - -Logging is implemented as a middleware. -It is common to register a logging middleware as the first middleware for the application. -Logging middleware must be registered for each application. - -The `Logger` middleware uses the standard log crate to log information. You should enable logger -for *actix_web* package to see access log ([env_logger](https://docs.rs/env_logger/*/env_logger/) -or similar). - -### Usage - -Create `Logger` middleware with the specified `format`. -Default `Logger` can be created with `default` method, it uses the default format: - -```ignore - %a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T -``` - -```rust -# extern crate actix_web; -extern crate env_logger; -use actix_web::App; -use actix_web::middleware::Logger; - -fn main() { - std::env::set_var("RUST_LOG", "actix_web=info"); - env_logger::init(); - - App::new() - .middleware(Logger::default()) - .middleware(Logger::new("%a %{User-Agent}i")) - .finish(); -} -``` - -The following is an example of the default logging format: - -``` -INFO:actix_web::middleware::logger: 127.0.0.1:59934 [02/Dec/2017:00:21:43 -0800] "GET / HTTP/1.1" 302 0 "-" "curl/7.54.0" 0.000397 -INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800] "GET /index.html HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0" 0.000646 -``` - -### Format - - `%%` The percent sign - - `%a` Remote IP-address (IP-address of proxy if using reverse proxy) - - `%t` Time when the request was started to process - - `%P` The process ID of the child that serviced the request - - `%r` First line of request - - `%s` Response status code - - `%b` Size of response in bytes, including HTTP headers - - `%T` Time taken to serve the request, in seconds with floating fraction in .06f format - - `%D` Time taken to serve the request, in milliseconds - - `%{FOO}i` request.headers['FOO'] - - `%{FOO}o` response.headers['FOO'] - - `%{FOO}e` os.environ['FOO'] - -## Default headers - -To set default response headers, the `DefaultHeaders` middleware can be used. The -*DefaultHeaders* middleware does not set the header if response headers already contain -a specified header. - -```rust -# extern crate actix_web; -use actix_web::{http, middleware, App, HttpResponse}; - -fn main() { - let app = App::new() - .middleware( - middleware::DefaultHeaders::new() - .header("X-Version", "0.2")) - .resource("/test", |r| { - r.method(http::Method::GET).f(|req| HttpResponse::Ok()); - r.method(http::Method::HEAD).f(|req| HttpResponse::MethodNotAllowed()); - }) - .finish(); -} -``` - -## User sessions - -Actix provides a general solution for session management. The -[**SessionStorage**](../actix_web/middleware/struct.SessionStorage.html) middleware can be -used with different backend types to store session data in different backends. - -> By default, only cookie session backend is implemented. Other backend implementations -> can be added. - -[**CookieSessionBackend**](../actix_web/middleware/struct.CookieSessionBackend.html) -uses cookies as session storage. `CookieSessionBackend` creates sessions which -are limited to storing fewer than 4000 bytes of data, as the payload must fit into a -single cookie. An internal server error is generated if a session contains more than 4000 bytes. - -A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor. - -A *signed* cookie may be viewed but not modified by the client. A *private* cookie may neither be viewed nor modified by the client. - -The constructors take a key as an argument. This is the private key for cookie session - when this value is changed, all session data is lost. - - - -In general, you create a -`SessionStorage` middleware and initialize it with specific backend implementation, -such as a `CookieSessionBackend`. To access session data, -[*HttpRequest::session()*](../actix_web/middleware/trait.RequestSession.html#tymethod.session) - must be used. This method returns a -[*Session*](../actix_web/middleware/struct.Session.html) object, which allows us to get or set -session data. - -```rust -# extern crate actix; -# extern crate actix_web; -use actix_web::{server, App, HttpRequest, Result}; -use actix_web::middleware::{RequestSession, SessionStorage, CookieSessionBackend}; - -fn index(mut req: HttpRequest) -> Result<&'static str> { - // access session data - if let Some(count) = req.session().get::("counter")? { - println!("SESSION value: {}", count); - req.session().set("counter", count+1)?; - } else { - req.session().set("counter", 1)?; - } - - Ok("Welcome!") -} - -fn main() { -# let sys = actix::System::new("basic-example"); - server::new( - || App::new() - .middleware(SessionStorage::new( // <- create session middleware - CookieSessionBackend::signed(&[0; 32]) // <- create signed cookie session backend - .secure(false) - ))) - .bind("127.0.0.1:59880").unwrap() - .start(); -# actix::Arbiter::system().do_send(actix::msgs::SystemExit(0)); -# let _ = sys.run(); -} -``` - -## Error handlers - -`ErrorHandlers` middleware allows us to provide custom handlers for responses. - -You can use the `ErrorHandlers::handler()` method to register a custom error handler -for a specific status code. You can modify an existing response or create a completly new -one. The error handler can return a response immediately or return a future that resolves -into a response. - -```rust -# extern crate actix_web; -use actix_web::{ - App, HttpRequest, HttpResponse, Result, - http, middleware::Response, middleware::ErrorHandlers}; - -fn render_500(_: &mut HttpRequest, resp: HttpResponse) -> Result { - let mut builder = resp.into_builder(); - builder.header(http::header::CONTENT_TYPE, "application/json"); - Ok(Response::Done(builder.into())) -} - -fn main() { - let app = App::new() - .middleware( - ErrorHandlers::new() - .handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500)) - .resource("/test", |r| { - r.method(http::Method::GET).f(|_| HttpResponse::Ok()); - r.method(http::Method::HEAD).f(|_| HttpResponse::MethodNotAllowed()); - }) - .finish(); -} -``` diff --git a/guide/src/qs_12.md b/guide/src/qs_12.md deleted file mode 100644 index 1b2a98c49..000000000 --- a/guide/src/qs_12.md +++ /dev/null @@ -1,54 +0,0 @@ -# Static file handling - -## Individual file - -It is possible to serve static files with a custom path pattern and `NamedFile`. To -match a path tail, we can use a `[.*]` regex. - -```rust -# extern crate actix_web; -use std::path::PathBuf; -use actix_web::{App, HttpRequest, Result, http::Method, fs::NamedFile}; - -fn index(req: HttpRequest) -> Result { - let path: PathBuf = req.match_info().query("tail")?; - Ok(NamedFile::open(path)?) -} - -fn main() { - App::new() - .resource(r"/a/{tail:.*}", |r| r.method(Method::GET).f(index)) - .finish(); -} -``` - -## Directory - -To serve files from specific directories and sub-directories, `StaticFiles` can be used. -`StaticFiles` must be registered with an `App::handler()` method, otherwise -it will be unable to serve sub-paths. - -```rust -# extern crate actix_web; -use actix_web::*; - -fn main() { - App::new() - .handler( - "/static", - fs::StaticFiles::new(".") - .show_files_listing()) - .finish(); -} -``` - -The parameter is the base directory. By default files listing for sub-directories -is disabled. Attempt to load directory listing will return *404 Not Found* response. -To enable files listing, use -[*StaticFiles::show_files_listing()*](../actix_web/s/struct.StaticFiles.html#method.show_files_listing) -method. - -Instead of showing files listing for directory, it is possible to redirect -to a specific index file. Use the -[*StaticFiles::index_file()*](../actix_web/s/struct.StaticFiles.html#method.index_file) -method to configure this redirect. diff --git a/guide/src/qs_13.md b/guide/src/qs_13.md deleted file mode 100644 index 963d5598a..000000000 --- a/guide/src/qs_13.md +++ /dev/null @@ -1,46 +0,0 @@ -# HTTP/2.0 - -Actix web automatically upgrades connections to *HTTP/2.0* if possible. - -## Negotiation - -*HTTP/2.0* protocol over tls without prior knowledge requires -[tls alpn](https://tools.ietf.org/html/rfc7301). - -> Currently, only `rust-openssl` has support. - -`alpn` negotiation requires enabling the feature. When enabled, `HttpServer` provides the -[serve_tls](../actix_web/struct.HttpServer.html#method.serve_tls) method. - -```toml -[dependencies] -actix-web = { version = "0.3.3", features=["alpn"] } -openssl = { version="0.10", features = ["v110"] } -``` - -```rust,ignore -use std::fs::File; -use actix_web::*; -use openssl::ssl::{SslMethod, SslAcceptor, SslFiletype}; - -fn main() { - // load ssl keys - let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); - builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap(); - builder.set_certificate_chain_file("cert.pem").unwrap(); - - HttpServer::new( - || App::new() - .resource("/index.html", |r| r.f(index))) - .bind("127.0.0.1:8080").unwrap(); - .serve_ssl(builder).unwrap(); -} -``` - -Upgrades to *HTTP/2.0* schema described in -[rfc section 3.2](https://http2.github.io/http2-spec/#rfc.section.3.2) is not supported. -Starting *HTTP/2* with prior knowledge is supported for both clear text connection -and tls connection. [rfc section 3.4](https://http2.github.io/http2-spec/#rfc.section.3.4) - -> Check out [examples/tls](https://github.com/actix/actix-web/tree/master/examples/tls) -> for a concrete example. diff --git a/guide/src/qs_14.md b/guide/src/qs_14.md deleted file mode 100644 index 0d1998e4d..000000000 --- a/guide/src/qs_14.md +++ /dev/null @@ -1,128 +0,0 @@ -# Database integration - -## Diesel - -At the moment, Diesel 1.0 does not support asynchronous operations, -but it possible to use the `actix` synchronous actor system as a database interface api. - -Technically, sync actors are worker style actors. Multiple sync actors -can be run in parallel and process messages from same queue. Sync actors work in mpsc mode. - -Let's create a simple database api that can insert a new user row into a SQLite table. -We must define a sync actor and a connection that this actor will use. The same approach -can be used for other databases. - -```rust,ignore -use actix::prelude::*; - -struct DbExecutor(SqliteConnection); - -impl Actor for DbExecutor { - type Context = SyncContext; -} -``` - -This is the definition of our actor. Now, we must define the *create user* message and response. - -```rust,ignore -struct CreateUser { - name: String, -} - -impl Message for CreateUser { - type Result = Result; -} -``` - -We can send a `CreateUser` message to the `DbExecutor` actor, and as a result, we will receive a -`User` model instance. Next, we must define the handler implementation for this message. - -```rust,ignore -impl Handler for DbExecutor { - type Result = Result; - - fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Self::Result - { - use self::schema::users::dsl::*; - - // Create insertion model - let uuid = format!("{}", uuid::Uuid::new_v4()); - let new_user = models::NewUser { - id: &uuid, - name: &msg.name, - }; - - // normal diesel operations - diesel::insert_into(users) - .values(&new_user) - .execute(&self.0) - .expect("Error inserting person"); - - let mut items = users - .filter(id.eq(&uuid)) - .load::(&self.0) - .expect("Error loading person"); - - Ok(items.pop().unwrap()) - } -} -``` - -That's it! Now, we can use the *DbExecutor* actor from any http handler or middleware. -All we need is to start *DbExecutor* actors and store the address in a state where http handler -can access it. - -```rust,ignore -/// This is state where we will store *DbExecutor* address. -struct State { - db: Addr, -} - -fn main() { - let sys = actix::System::new("diesel-example"); - - // Start 3 parallel db executors - let addr = SyncArbiter::start(3, || { - DbExecutor(SqliteConnection::establish("test.db").unwrap()) - }); - - // Start http server - HttpServer::new(move || { - App::with_state(State{db: addr.clone()}) - .resource("/{name}", |r| r.method(Method::GET).a(index))}) - .bind("127.0.0.1:8080").unwrap() - .start().unwrap(); - - println!("Started http server: 127.0.0.1:8080"); - let _ = sys.run(); -} -``` - -We will use the address in a request handler. The handle returns a future object; -thus, we receive the message response asynchronously. -`Route::a()` must be used for async handler registration. - - -```rust,ignore -/// Async handler -fn index(req: HttpRequest) -> Box> { - let name = &req.match_info()["name"]; - - // Send message to `DbExecutor` actor - req.state().db.send(CreateUser{name: name.to_owned()}) - .from_err() - .and_then(|res| { - match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()) - } - }) - .responder() -} -``` - -> A full example is available in the -> [examples directory](https://github.com/actix/actix-web/tree/master/examples/diesel/). - -> More information on sync actors can be found in the -> [actix documentation](https://docs.rs/actix/0.5.0/actix/sync/index.html). diff --git a/guide/src/qs_2.md b/guide/src/qs_2.md deleted file mode 100644 index a5f3d2770..000000000 --- a/guide/src/qs_2.md +++ /dev/null @@ -1,100 +0,0 @@ -# Getting Started - -Let’s write our first actix web application! - -## Hello, world! - -Start by creating a new binary-based Cargo project and changing into the new directory: - -```bash -cargo new hello-world --bin -cd hello-world -``` - -Now, add actix and actix web as dependencies of your project by ensuring your Cargo.toml -contains the following: - -```toml -[dependencies] -actix = "0.5" -actix-web = "0.5" -``` - -In order to implement a web server, we first need to create a request handler. - -A request handler is a function that accepts an `HttpRequest` instance as its only parameter -and returns a type that can be converted into `HttpResponse`: - -Filename: src/main.rs -```rust -# extern crate actix_web; -# use actix_web::*; - fn index(req: HttpRequest) -> &'static str { - "Hello world!" - } -# fn main() {} -``` - -Next, create an `Application` instance and register the -request handler with the application's `resource` on a particular *HTTP method* and *path*:: - -```rust -# extern crate actix_web; -# use actix_web::*; -# fn index(req: HttpRequest) -> &'static str { -# "Hello world!" -# } -# fn main() { - App::new() - .resource("/", |r| r.f(index)); -# } -``` - -After that, the application instance can be used with `HttpServer` to listen for incoming -connections. The server accepts a function that should return an `HttpHandler` instance. -For simplicity `server::new` could be used, this function is shortcut for `HttpServer::new`: - -```rust,ignore - server::new( - || App::new() - .resource("/", |r| r.f(index))) - .bind("127.0.0.1:8088")? - .run(); -``` - -That's it! Now, compile and run the program with `cargo run`. -Head over to ``http://localhost:8088/`` to see the results. - -The full source of src/main.rs is listed below: - -```rust -# use std::thread; -extern crate actix_web; -use actix_web::{server, App, HttpRequest, HttpResponse}; - -fn index(req: HttpRequest) -> &'static str { - "Hello world!" -} - -fn main() { -# // In the doctest suite we can't run blocking code - deliberately leak a thread -# // If copying this example in show-all mode, make sure you skip the thread spawn -# // call. -# thread::spawn(|| { - server::new( - || App::new() - .resource("/", |r| r.f(index))) - .bind("127.0.0.1:8088").expect("Can not bind to 127.0.0.1:8088") - .run(); -# }); -} -``` - -> **Note**: actix web is built upon [actix](https://github.com/actix/actix), -> an [actor model](https://en.wikipedia.org/wiki/Actor_model) framework in Rust. - -`actix::System` initializes actor system, `HttpServer` is an actor and must run within a -properly configured actix system. - -> For more information, check out the [actix documentation](https://actix.github.io/actix/actix/) -> and [actix guide](https://actix.github.io/actix/guide/). diff --git a/guide/src/qs_3.md b/guide/src/qs_3.md deleted file mode 100644 index d5c0b3258..000000000 --- a/guide/src/qs_3.md +++ /dev/null @@ -1,110 +0,0 @@ -# Application - -Actix web provides various primitives to build web servers and applications with Rust. -It provides routing, middlewares, pre-processing of requests, post-processing of responses, -websocket protocol handling, multipart streams, etc. - -All actix web servers are built around the `App` instance. -It is used for registering routes for resources and middlewares. -It also stores application state shared across all handlers within same application. - -Applications act as a namespace for all routes, i.e all routes for a specific application -have the same url path prefix. The application prefix always contains a leading "/" slash. -If a supplied prefix does not contain leading slash, it is automatically inserted. -The prefix should consist of value path segments. - -> For an application with prefix `/app`, -> any request with the paths `/app`, `/app/`, or `/app/test` would match; -> however, the path `/application` would not match. - -```rust,ignore -# extern crate actix_web; -# extern crate tokio_core; -# use actix_web::{*, http::Method}; -# fn index(req: HttpRequest) -> &'static str { -# "Hello world!" -# } -# fn main() { - let app = App::new() - .prefix("/app") - .resource("/index.html", |r| r.method(Method::GET).f(index)) - .finish() -# } -``` - -In this example, an application with the `/app` prefix and a `index.html` resource -are created. This resource is available through the `/app/index.html` url. - -> For more information, check the -> [URL Dispatch](./qs_5.html#using-a-application-prefix-to-compose-applications) section. - -Multiple applications can be served with one server: - -```rust -# extern crate actix_web; -# extern crate tokio_core; -# use tokio_core::net::TcpStream; -# use std::net::SocketAddr; -use actix_web::{server, App, HttpResponse}; - -fn main() { - server::new(|| vec![ - App::new() - .prefix("/app1") - .resource("/", |r| r.f(|r| HttpResponse::Ok())), - App::new() - .prefix("/app2") - .resource("/", |r| r.f(|r| HttpResponse::Ok())), - App::new() - .resource("/", |r| r.f(|r| HttpResponse::Ok())), - ]); -} -``` - -All `/app1` requests route to the first application, `/app2` to the second, and all other to the third. -**Applications get matched based on registration order**. If an application with a more generic -prefix is registered before a less generic one, it would effectively block the less generic -application matching. For example, if an `App` with the prefix `"/"` was registered -as the first application, it would match all incoming requests. - -## State - -Application state is shared with all routes and resources within the same application. -When using an http actor,state can be accessed with the `HttpRequest::state()` as read-only, -but interior mutability with `RefCell` can be used to achieve state mutability. -State is also available for route matching predicates and middlewares. - -Let's write a simple application that uses shared state. We are going to store request count -in the state: - -```rust -# extern crate actix; -# extern crate actix_web; -# -use std::cell::Cell; -use actix_web::{App, HttpRequest, http}; - -// This struct represents state -struct AppState { - counter: Cell, -} - -fn index(req: HttpRequest) -> String { - let count = req.state().counter.get() + 1; // <- get count - req.state().counter.set(count); // <- store new count in state - - format!("Request number: {}", count) // <- response with count -} - -fn main() { - App::with_state(AppState{counter: Cell::new(0)}) - .resource("/", |r| r.method(http::Method::GET).f(index)) - .finish(); -} -``` - -> **Note**: http server accepts an application factory rather than an application -> instance. Http server constructs an application instance for each thread, thus application state -> must be constructed multiple times. If you want to share state between different threads, a -> shared object should be used, e.g. `Arc`. Application state does not need to be `Send` and `Sync`, -> but the application factory must be `Send` + `Sync`. diff --git a/guide/src/qs_3_5.md b/guide/src/qs_3_5.md deleted file mode 100644 index 82e83ff1d..000000000 --- a/guide/src/qs_3_5.md +++ /dev/null @@ -1,210 +0,0 @@ -# Server - -The [**HttpServer**](../actix_web/server/struct.HttpServer.html) type is responsible for -serving http requests. - -`HttpServer` accepts an application factory as a parameter, and the -application factory must have `Send` + `Sync` boundaries. More about that in the -*multi-threading* section. - -To bind to a specific socket address, `bind()` must be used, and it may be called multiple times. -To start the http server, one of the start methods. - -- use `start()` for a simple server -- use `start_tls()` or `start_ssl()` for a ssl server - -`HttpServer` is an actix actor. It must be initialized within a properly configured actix system: - -```rust -# extern crate actix; -# extern crate actix_web; -use actix_web::{server::HttpServer, App, HttpResponse}; - -fn main() { - let sys = actix::System::new("guide"); - - HttpServer::new( - || App::new() - .resource("/", |r| r.f(|_| HttpResponse::Ok()))) - .bind("127.0.0.1:59080").unwrap() - .start(); - -# actix::Arbiter::system().do_send(actix::msgs::SystemExit(0)); - let _ = sys.run(); -} -``` - -> It is possible to start a server in a separate thread with the `spawn()` method. In that -> case the server spawns a new thread and creates a new actix system in it. To stop -> this server, send a `StopServer` message. - -`HttpServer` is implemented as an actix actor. It is possible to communicate with the server -via a messaging system. All start methods, e.g. `start()` and `start_ssl()`, return the -address of the started http server. It accepts several messages: - -- `PauseServer` - Pause accepting incoming connections -- `ResumeServer` - Resume accepting incoming connections -- `StopServer` - Stop incoming connection processing, stop all workers and exit - -```rust -# extern crate futures; -# extern crate actix; -# extern crate actix_web; -# use futures::Future; -use std::thread; -use std::sync::mpsc; -use actix_web::{server, App, HttpResponse}; - -fn main() { - let (tx, rx) = mpsc::channel(); - - thread::spawn(move || { - let sys = actix::System::new("http-server"); - let addr = server::new( - || App::new() - .resource("/", |r| r.f(|_| HttpResponse::Ok()))) - .bind("127.0.0.1:0").expect("Can not bind to 127.0.0.1:0") - .shutdown_timeout(60) // <- Set shutdown timeout to 60 seconds - .start(); - let _ = tx.send(addr); - let _ = sys.run(); - }); - - let addr = rx.recv().unwrap(); - let _ = addr.send( - server::StopServer{graceful:true}).wait(); // <- Send `StopServer` message to server. -} -``` - -## Multi-threading - -`HttpServer` automatically starts an number of http workers, by default -this number is equal to number of logical CPUs in the system. This number -can be overridden with the `HttpServer::threads()` method. - -```rust -# extern crate actix_web; -# extern crate tokio_core; -use actix_web::{App, HttpResponse, server::HttpServer}; - -fn main() { - HttpServer::new( - || App::new() - .resource("/", |r| r.f(|_| HttpResponse::Ok()))) - .threads(4); // <- Start 4 workers -} -``` - -The server creates a separate application instance for each created worker. Application state -is not shared between threads. To share state, `Arc` could be used. - -> Application state does not need to be `Send` and `Sync`, -> but factories must be `Send` + `Sync`. - -## SSL - -There are two features for ssl server: `tls` and `alpn`. The `tls` feature is for `native-tls` -integration and `alpn` is for `openssl`. - -```toml -[dependencies] -actix-web = { git = "https://github.com/actix/actix-web", features=["alpn"] } -``` - -```rust,ignore -use std::fs::File; -use actix_web::*; - -fn main() { - // load ssl keys - let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); - builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap(); - builder.set_certificate_chain_file("cert.pem").unwrap(); - - server::new( - || App::new() - .resource("/index.html", |r| r.f(index))) - .bind("127.0.0.1:8080").unwrap() - .serve_ssl(builder).unwrap(); -} -``` - -> **Note**: the *HTTP/2.0* protocol requires -> [tls alpn](https://tools.ietf.org/html/rfc7301). -> At the moment, only `openssl` has `alpn` support. -> For a full example, check out -> [examples/tls](https://github.com/actix/actix-web/tree/master/examples/tls). - -## Keep-Alive - -Actix can wait for requests on a keep-alive connection. - -> *keep alive* connection behavior is defined by server settings. - -- `75`, `Some(75)`, `KeepAlive::Timeout(75)` - enable 75 second *keep alive* timer. -- `None` or `KeepAlive::Disabled` - disable *keep alive*. -- `KeepAlive::Tcp(75)` - use `SO_KEEPALIVE` socket option. - -```rust -# extern crate actix_web; -# extern crate tokio_core; -use actix_web::{server, App, HttpResponse}; - -fn main() { - server::new(|| - App::new() - .resource("/", |r| r.f(|_| HttpResponse::Ok()))) - .keep_alive(75); // <- Set keep-alive to 75 seconds - - server::new(|| - App::new() - .resource("/", |r| r.f(|_| HttpResponse::Ok()))) - .keep_alive(server::KeepAlive::Tcp(75)); // <- Use `SO_KEEPALIVE` socket option. - - server::new(|| - App::new() - .resource("/", |r| r.f(|_| HttpResponse::Ok()))) - .keep_alive(None); // <- Disable keep-alive -} -``` - -If the first option is selected, then *keep alive* state is -calculated based on the response's *connection-type*. By default -`HttpResponse::connection_type` is not defined. In that case *keep alive* is -defined by the request's http version. - -> *keep alive* is **off** for *HTTP/1.0* and is **on** for *HTTP/1.1* and *HTTP/2.0*. - -*Connection type* can be change with `HttpResponseBuilder::connection_type()` method. - -```rust -# extern crate actix_web; -use actix_web::{HttpRequest, HttpResponse, http}; - -fn index(req: HttpRequest) -> HttpResponse { - HttpResponse::Ok() - .connection_type(http::ConnectionType::Close) // <- Close connection - .force_close() // <- Alternative method - .finish() -} -# fn main() {} -``` - -## Graceful shutdown - -`HttpServer` supports graceful shutdown. After receiving a stop signal, workers -have a specific amount of time to finish serving requests. Any workers still alive after the -timeout are force-dropped. By default the shutdown timeout is set to 30 seconds. -You can change this parameter with the `HttpServer::shutdown_timeout()` method. - -You can send a stop message to the server with the server address and specify if you want -graceful shutdown or not. The `start()` methods returns address of the server. - -`HttpServer` handles several OS signals. *CTRL-C* is available on all OSs, -other signals are available on unix systems. - -- *SIGINT* - Force shutdown workers -- *SIGTERM* - Graceful shutdown workers -- *SIGQUIT* - Force shutdown workers - -> It is possible to disable signal handling with `HttpServer::disable_signals()` method. diff --git a/guide/src/qs_4.md b/guide/src/qs_4.md deleted file mode 100644 index 1a1ff6178..000000000 --- a/guide/src/qs_4.md +++ /dev/null @@ -1,313 +0,0 @@ -# Handler - -A request handler can be any object that implements -[`Handler` trait](../actix_web/dev/trait.Handler.html). - -Request handling happens in two stages. First the handler object is called, -returning any object that implements the -[`Responder` trait](../actix_web/trait.Responder.html#foreign-impls). -Then, `respond_to()` is called on the returned object, converting itself to a `Reply` or `Error`. - -By default actix provides `Responder` implementations for some standard types, -such as `&'static str`, `String`, etc. - -> For a complete list of implementations, check -> [*Responder documentation*](../actix_web/trait.Responder.html#foreign-impls). - -Examples of valid handlers: - -```rust,ignore -fn index(req: HttpRequest) -> &'static str { - "Hello world!" -} -``` - -```rust,ignore -fn index(req: HttpRequest) -> String { - "Hello world!".to_owned() -} -``` - -```rust,ignore -fn index(req: HttpRequest) -> Bytes { - Bytes::from_static("Hello world!") -} -``` - -```rust,ignore -fn index(req: HttpRequest) -> Box> { - ... -} -``` - -*Handler* trait is generic over *S*, which defines the application state's type. -Application state is accessible from the handler with the `HttpRequest::state()` method; -however, state is accessible as a read-only reference. If you need mutable access to state, -it must be implemented. - -> **Note**: Alternatively, the handler can mutably access its own state because the `handle` method takes -> mutable reference to *self*. **Beware**, actix creates multiple copies -> of the application state and the handlers, unique for each thread. If you run your -> application in several threads, actix will create the same amount as number of threads -> of application state objects and handler objects. - -Here is an example of a handler that stores the number of processed requests: - -```rust -# extern crate actix_web; -use actix_web::{App, HttpRequest, HttpResponse, dev::Handler}; - -struct MyHandler(usize); - -impl Handler for MyHandler { - type Result = HttpResponse; - - /// Handle request - fn handle(&mut self, req: HttpRequest) -> Self::Result { - self.0 += 1; - HttpResponse::Ok().into() - } -} -# fn main() {} -``` - -Although this handler will work, `self.0` will be different depending on the number of threads and -number of requests processed per thread. A proper implementation would use `Arc` and `AtomicUsize`. - -```rust -# extern crate actix; -# extern crate actix_web; -use actix_web::{server, App, HttpRequest, HttpResponse, dev::Handler}; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; - -struct MyHandler(Arc); - -impl Handler for MyHandler { - type Result = HttpResponse; - - /// Handle request - fn handle(&mut self, req: HttpRequest) -> Self::Result { - self.0.fetch_add(1, Ordering::Relaxed); - HttpResponse::Ok().into() - } -} - -fn main() { - let sys = actix::System::new("example"); - - let inc = Arc::new(AtomicUsize::new(0)); - - server::new( - move || { - let cloned = inc.clone(); - App::new() - .resource("/", move |r| r.h(MyHandler(cloned))) - }) - .bind("127.0.0.1:8088").unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8088"); -# actix::Arbiter::system().do_send(actix::msgs::SystemExit(0)); - let _ = sys.run(); -} -``` - -> Be careful with synchronization primitives like `Mutex` or `RwLock`. Actix web framework -> handles requests asynchronously. By blocking thread execution, all concurrent -> request handling processes would block. If you need to share or update some state -> from multiple threads, consider using the [actix](https://actix.github.io/actix/actix/) actor system. - -## Response with custom type - -To return a custom type directly from a handler function, the type needs to implement the `Responder` trait. - -Let's create a response for a custom type that serializes to an `application/json` response: - -```rust -# extern crate actix; -# extern crate actix_web; -extern crate serde; -extern crate serde_json; -#[macro_use] extern crate serde_derive; -use actix_web::{server, App, HttpRequest, HttpResponse, Error, Responder, http}; - -#[derive(Serialize)] -struct MyObj { - name: &'static str, -} - -/// Responder -impl Responder for MyObj { - type Item = HttpResponse; - type Error = Error; - - fn respond_to(self, req: HttpRequest) -> Result { - let body = serde_json::to_string(&self)?; - - // Create response and set content type - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(body)) - } -} - -/// Because `MyObj` implements `Responder`, it is possible to return it directly -fn index(req: HttpRequest) -> MyObj { - MyObj{name: "user"} -} - -fn main() { - let sys = actix::System::new("example"); - - server::new( - || App::new() - .resource("/", |r| r.method(http::Method::GET).f(index))) - .bind("127.0.0.1:8088").unwrap() - .start(); - - println!("Started http server: 127.0.0.1:8088"); -# actix::Arbiter::system().do_send(actix::msgs::SystemExit(0)); - let _ = sys.run(); -} -``` - -## Async handlers - -There are two different types of async handlers. Response objects can be generated asynchronously -or more precisely, any type that implements the [*Responder*](../actix_web/trait.Responder.html) trait. - -In this case, the handler must return a `Future` object that resolves to the *Responder* type, i.e: - -```rust -# extern crate actix_web; -# extern crate futures; -# extern crate bytes; -# use actix_web::*; -# use bytes::Bytes; -# use futures::stream::once; -# use futures::future::{Future, result}; -fn index(req: HttpRequest) -> Box> { - - result(Ok(HttpResponse::Ok() - .content_type("text/html") - .body(format!("Hello!")))) - .responder() -} - -fn index2(req: HttpRequest) -> Box> { - result(Ok("Welcome!")) - .responder() -} - -fn main() { - App::new() - .resource("/async", |r| r.route().a(index)) - .resource("/", |r| r.route().a(index2)) - .finish(); -} -``` - -Or the response body can be generated asynchronously. In this case, body -must implement the stream trait `Stream`, i.e: - -```rust -# extern crate actix_web; -# extern crate futures; -# extern crate bytes; -# use actix_web::*; -# use bytes::Bytes; -# use futures::stream::once; -fn index(req: HttpRequest) -> HttpResponse { - let body = once(Ok(Bytes::from_static(b"test"))); - - HttpResponse::Ok() - .content_type("application/json") - .body(Body::Streaming(Box::new(body))) -} - -fn main() { - App::new() - .resource("/async", |r| r.f(index)) - .finish(); -} -``` - -Both methods can be combined. (i.e Async response with streaming body) - -It is possible to return a `Result` where the `Result::Item` type can be `Future`. -In this example, the `index` handler can return an error immediately or return a -future that resolves to a `HttpResponse`. - -```rust -# extern crate actix_web; -# extern crate futures; -# extern crate bytes; -# use actix_web::*; -# use bytes::Bytes; -# use futures::stream::once; -# use futures::future::{Future, result}; -fn index(req: HttpRequest) -> Result>, Error> { - if is_error() { - Err(error::ErrorBadRequest("bad request")) - } else { - Ok(Box::new( - result(Ok(HttpResponse::Ok() - .content_type("text/html") - .body(format!("Hello!")))))) - } -} -# -# fn is_error() -> bool { true } -# fn main() { -# App::new() -# .resource("/async", |r| r.route().f(index)) -# .finish(); -# } -``` - -## Different return types (Either) - -Sometimes, you need to return different types of responses. For example, -you can error check and return errors, return async responses, or any result that requires two different types. - -For this case, the [`Either`](../actix_web/enum.Either.html) type can be used. -`Either` allows combining two different responder types into a single type. - -```rust -# extern crate actix_web; -# extern crate futures; -# use actix_web::*; -# use futures::future::Future; -use futures::future::result; -use actix_web::{Either, Error, HttpResponse}; - -type RegisterResult = Either>>; - -fn index(req: HttpRequest) -> RegisterResult { - if is_a_variant() { // <- choose variant A - Either::A( - HttpResponse::BadRequest().body("Bad data")) - } else { - Either::B( // <- variant B - result(Ok(HttpResponse::Ok() - .content_type("text/html") - .body(format!("Hello!")))).responder()) - } -} -# fn is_a_variant() -> bool { true } -# fn main() { -# App::new() -# .resource("/register", |r| r.f(index)) -# .finish(); -# } -``` - -## Tokio core handle - -Any actix web handler runs within a properly configured -[actix system](https://actix.github.io/actix/actix/struct.System.html) -and [arbiter](https://actix.github.io/actix/actix/struct.Arbiter.html). -You can always get access to the tokio handle via the -[Arbiter::handle()](https://actix.github.io/actix/actix/struct.Arbiter.html#method.handle) -method. diff --git a/guide/src/qs_4_5.md b/guide/src/qs_4_5.md deleted file mode 100644 index 4bc82451d..000000000 --- a/guide/src/qs_4_5.md +++ /dev/null @@ -1,153 +0,0 @@ -# Errors - -Actix uses the [`Error` type](../actix_web/error/struct.Error.html) -and [`ResponseError` trait](../actix_web/error/trait.ResponseError.html) -for handling handler's errors. - -Any error that implements the `ResponseError` trait can be returned as an error value. -`Handler` can return an `Result` object. By default, actix provides a -`Responder` implementation for compatible result types. Here is the implementation -definition: - -```rust,ignore -impl> Responder for Result -``` - -Any error that implements `ResponseError` can be converted into an `Error` object. - -For example, if the *handler* function returns `io::Error`, it would be converted -into an `HttpInternalServerError` response. Implementation for `io::Error` is provided -by default. - -```rust -# extern crate actix_web; -# use actix_web::*; -use std::io; - -fn index(req: HttpRequest) -> io::Result { - Ok(fs::NamedFile::open("static/index.html")?) -} -# -# fn main() { -# App::new() -# .resource(r"/a/index.html", |r| r.f(index)) -# .finish(); -# } -``` - -## Custom error response - -To add support for custom errors, all we need to do is implement the `ResponseError` trait -for the custom error type. The `ResponseError` trait has a default implementation -for the `error_response()` method: it generates a *500* response. - -```rust -# extern crate actix_web; -#[macro_use] extern crate failure; -use actix_web::*; - -#[derive(Fail, Debug)] -#[fail(display="my error")] -struct MyError { - name: &'static str -} - -/// Use default implementation for `error_response()` method -impl error::ResponseError for MyError {} - -fn index(req: HttpRequest) -> Result<&'static str, MyError> { - Err(MyError{name: "test"}) -} -# -# fn main() { -# App::new() -# .resource(r"/a/index.html", |r| r.f(index)) -# .finish(); -# } -``` - -In this example the *index* handler always returns a *500* response. But it is easy -to return different responses for different types of errors. - -```rust -# extern crate actix_web; -#[macro_use] extern crate failure; -use actix_web::{App, HttpRequest, HttpResponse, http, error}; - -#[derive(Fail, Debug)] -enum MyError { - #[fail(display="internal error")] - InternalError, - #[fail(display="bad request")] - BadClientData, - #[fail(display="timeout")] - Timeout, -} - -impl error::ResponseError for MyError { - fn error_response(&self) -> HttpResponse { - match *self { - MyError::InternalError => HttpResponse::new( - http::StatusCode::INTERNAL_SERVER_ERROR), - MyError::BadClientData => HttpResponse::new( - http::StatusCode::BAD_REQUEST), - MyError::Timeout => HttpResponse::new( - http::StatusCode::GATEWAY_TIMEOUT), - } - } -} - -fn index(req: HttpRequest) -> Result<&'static str, MyError> { - Err(MyError::BadClientData) -} -# -# fn main() { -# App::new() -# .resource(r"/a/index.html", |r| r.f(index)) -# .finish(); -# } -``` - -## Error helpers - -Actix provides a set of error helper types. It is possible to use them for generating -specific error responses. We can use the helper types for the first example with a custom error. - -```rust -# extern crate actix_web; -#[macro_use] extern crate failure; -use actix_web::*; - -#[derive(Debug)] -struct MyError { - name: &'static str -} - -fn index(req: HttpRequest) -> Result<&'static str> { - let result: Result<&'static str, MyError> = Err(MyError{name: "test"}); - - Ok(result.map_err(|e| error::ErrorBadRequest(e))?) -} -# fn main() { -# App::new() -# .resource(r"/a/index.html", |r| r.f(index)) -# .finish(); -# } -``` - -In this example, a *BAD REQUEST* response is generated for the `MyError` error. - -## Error logging - -Actix logs all errors with the log level `WARN`. If log level set to `DEBUG` -and `RUST_BACKTRACE` is enabled, the backtrace gets logged. The Error type uses -the cause's error backtrace if available. If the underlying failure does not provide -a backtrace, a new backtrace is constructed pointing to that conversion point -(rather than the origin of the error). This construction only happens if there -is no underlying backtrace; if it does have a backtrace, no new backtrace is constructed. - -You can enable backtrace and debug logging with following command: - -``` ->> RUST_BACKTRACE=1 RUST_LOG=actix_web=debug cargo run -``` diff --git a/guide/src/qs_5.md b/guide/src/qs_5.md deleted file mode 100644 index 96f8b39be..000000000 --- a/guide/src/qs_5.md +++ /dev/null @@ -1,654 +0,0 @@ -# URL Dispatch - -URL dispatch provides a simple way for mapping URLs to `Handler` code using a simple pattern -matching language. If one of the patterns matches the path information associated with a request, -a particular handler object is invoked. - -> A handler is a specific object that implements the -> `Handler` trait, defined in your application, that receives the request and returns -> a response object. More information is available in the [handler section](../qs_4.html). - -## Resource configuration - -Resource configuration is the act of adding a new resources to an application. -A resource has a name, which acts as an identifier to be used for URL generation. -The name also allows developers to add routes to existing resources. -A resource also has a pattern, meant to match against the *PATH* portion of a *URL*. -It does not match against the *QUERY* portion (the portion following the scheme and -port, e.g., */foo/bar* in the *URL* *http://localhost:8080/foo/bar?q=value*). - -The [App::route](../actix_web/struct.App.html#method.route) method provides -simple way of registering routes. This method adds a single route to application -routing table. This method accepts a *path pattern*, -*http method* and a handler function. `route()` method could be called multiple times -for the same path, in that case, multiple routes register for the same resource path. - -```rust -# extern crate actix_web; -use actix_web::{App, HttpRequest, HttpResponse, http::Method}; - -fn index(req: HttpRequest) -> HttpResponse { - unimplemented!() -} - -fn main() { - App::new() - .route("/user/{name}", Method::GET, index) - .route("/user/{name}", Method::POST, index) - .finish(); -} -``` - -While *App::route()* provides simple way of registering routes, to access -complete resource configuration, different method has to be used. -The [App::resource](../actix_web/struct.App.html#method.resource) method -adds a single resource to application routing table. This method accepts a *path pattern* -and a resource configuration function. - -```rust -# extern crate actix_web; -use actix_web::{App, HttpRequest, HttpResponse, http::Method}; - -fn index(req: HttpRequest) -> HttpResponse { - unimplemented!() -} - -fn main() { - App::new() - .resource("/prefix", |r| r.f(index)) - .resource("/user/{name}", - |r| r.method(Method::GET).f(|req| HttpResponse::Ok())) - .finish(); -} -``` - -The *Configuration function* has the following type: - -```rust,ignore - FnOnce(&mut Resource<_>) -> () -``` - -The *Configuration function* can set a name and register specific routes. -If a resource does not contain any route or does not have any matching routes, it -returns *NOT FOUND* http response. - -## Configuring a Route - -Resource contains a set of routes. Each route in turn has a set of predicates and a handler. -New routes can be created with `Resource::route()` method which returns a reference -to new *Route* instance. By default the *route* does not contain any predicates, so matches -all requests and the default handler is `HttpNotFound`. - -The application routes incoming requests based on route criteria which are defined during -resource registration and route registration. Resource matches all routes it contains in -the order the routes were registered via `Resource::route()`. - -> A *Route* can contain any number of *predicates* but only one handler. - -```rust -# extern crate actix_web; -# use actix_web::*; - -fn main() { - App::new() - .resource("/path", |resource| - resource.route() - .filter(pred::Get()) - .filter(pred::Header("content-type", "text/plain")) - .f(|req| HttpResponse::Ok()) - ) - .finish(); -} -``` - -In this example, `HttpResponse::Ok()` is returned for *GET* requests. -If a request contains `Content-Type` header, the value of this header is *text/plain*, -and path equals to `/path`, Resource calls handle of the first matching route. - -If a resource can not match any route, a "NOT FOUND" response is returned. - -[*Resource::route()*](../actix_web/struct.Resource.html#method.route) returns a -[*Route*](../actix_web/struct.Route.html) object. Route can be configured with a -builder-like pattern. Following configuration methods are available: - -* [*Route::filter()*](../actix_web/struct.Route.html#method.filter) registers a new predicate. - Any number of predicates can be registered for each route. - -* [*Route::f()*](../actix_web/struct.Route.html#method.f) registers handler function - for this route. Only one handler can be registered. Usually handler registration - is the last config operation. Handler function can be a function or closure and has the type - `Fn(HttpRequest) -> R + 'static` - -* [*Route::h()*](../actix_web/struct.Route.html#method.h) registers a handler object - that implements the `Handler` trait. This is similar to `f()` method - only one handler can - be registered. Handler registration is the last config operation. - -* [*Route::a()*](../actix_web/struct.Route.html#method.a) registers an async handler - function for this route. Only one handler can be registered. Handler registration - is the last config operation. Handler function can be a function or closure and has the type - `Fn(HttpRequest) -> Future + 'static` - -## Route matching - -The main purpose of route configuration is to match (or not match) the request's `path` -against a URL path pattern. `path` represents the path portion of the URL that was requested. - -The way that *actix* does this is very simple. When a request enters the system, -for each resource configuration declaration present in the system, actix checks -the request's path against the pattern declared. This checking happens in the order that -the routes were declared via `App::resource()` method. If resource can not be found, -the *default resource* is used as the matched resource. - -When a route configuration is declared, it may contain route predicate arguments. All route -predicates associated with a route declaration must be `true` for the route configuration to -be used for a given request during a check. If any predicate in the set of route predicate -arguments provided to a route configuration returns `false` during a check, that route is -skipped and route matching continues through the ordered set of routes. - -If any route matches, the route matching process stops and the handler associated with -the route is invoked. If no route matches after all route patterns are exhausted, a *NOT FOUND* response get returned. - -## Resource pattern syntax - -The syntax of the pattern matching language used by actix in the pattern -argument is straightforward. - -The pattern used in route configuration may start with a slash character. If the pattern -does not start with a slash character, an implicit slash will be prepended -to it at matching time. For example, the following patterns are equivalent: - -``` -{foo}/bar/baz -``` - -and: - -``` -/{foo}/bar/baz -``` - -A *variable part* (replacement marker) is specified in the form *{identifier}*, -where this means "accept any characters up to the next slash character and use this -as the name in the `HttpRequest.match_info()` object". - -A replacement marker in a pattern matches the regular expression `[^{}/]+`. - -A match_info is the `Params` object representing the dynamic parts extracted from a -*URL* based on the routing pattern. It is available as *request.match_info*. For example, the -following pattern defines one literal segment (foo) and two replacement markers (baz, and bar): - -``` -foo/{baz}/{bar} -``` - -The above pattern will match these URLs, generating the following match information: - -``` -foo/1/2 -> Params {'baz':'1', 'bar':'2'} -foo/abc/def -> Params {'baz':'abc', 'bar':'def'} -``` - -It will not match the following patterns however: - -``` -foo/1/2/ -> No match (trailing slash) -bar/abc/def -> First segment literal mismatch -``` - -The match for a segment replacement marker in a segment will be done only up to -the first non-alphanumeric character in the segment in the pattern. So, for instance, -if this route pattern was used: - -``` -foo/{name}.html -``` - -The literal path */foo/biz.html* will match the above route pattern, and the match result -will be `Params{'name': 'biz'}`. However, the literal path */foo/biz* will not match, -because it does not contain a literal *.html* at the end of the segment represented -by *{name}.html* (it only contains biz, not biz.html). - -To capture both segments, two replacement markers can be used: - -``` -foo/{name}.{ext} -``` - -The literal path */foo/biz.html* will match the above route pattern, and the match -result will be *Params{'name': 'biz', 'ext': 'html'}*. This occurs because there is a -literal part of *.* (period) between the two replacement markers *{name}* and *{ext}*. - -Replacement markers can optionally specify a regular expression which will be used to decide -whether a path segment should match the marker. To specify that a replacement marker should -match only a specific set of characters as defined by a regular expression, you must use a -slightly extended form of replacement marker syntax. Within braces, the replacement marker -name must be followed by a colon, then directly thereafter, the regular expression. The default -regular expression associated with a replacement marker *[^/]+* matches one or more characters -which are not a slash. For example, under the hood, the replacement marker *{foo}* can more -verbosely be spelled as *{foo:[^/]+}*. You can change this to be an arbitrary regular expression -to match an arbitrary sequence of characters, such as *{foo:\d+}* to match only digits. - -Segments must contain at least one character in order to match a segment replacement marker. -For example, for the URL */abc/*: - -* */abc/{foo}* will not match. -* */{foo}/* will match. - -> **Note**: path will be URL-unquoted and decoded into valid unicode string before -> matching pattern and values representing matched path segments will be URL-unquoted too. - -So for instance, the following pattern: - -``` -foo/{bar} -``` - -When matching the following URL: - -``` -http://example.com/foo/La%20Pe%C3%B1a -``` - -The matchdict will look like so (the value is URL-decoded): - -``` -Params{'bar': 'La Pe\xf1a'} -``` - -Literal strings in the path segment should represent the decoded value of the -path provided to actix. You don't want to use a URL-encoded value in the pattern. -For example, rather than this: - -``` -/Foo%20Bar/{baz} -``` - -You'll want to use something like this: - -``` -/Foo Bar/{baz} -``` - -It is possible to get "tail match". For this purpose custom regex has to be used. - -``` -foo/{bar}/{tail:.*} -``` - -The above pattern will match these URLs, generating the following match information: - -``` -foo/1/2/ -> Params{'bar':'1', 'tail': '2/'} -foo/abc/def/a/b/c -> Params{'bar':u'abc', 'tail': 'def/a/b/c'} -``` - -## Match information - -All values representing matched path segments are available in -[`HttpRequest::match_info`](../actix_web/struct.HttpRequest.html#method.match_info). -Specific values can be retrieved with -[`Params::get()`](../actix_web/dev/struct.Params.html#method.get). - -Any matched parameter can be deserialized into a specific type if the type -implements the `FromParam` trait. For example most standard integer types -the trait, i.e.: - -```rust -# extern crate actix_web; -use actix_web::*; - -fn index(req: HttpRequest) -> Result { - let v1: u8 = req.match_info().query("v1")?; - let v2: u8 = req.match_info().query("v2")?; - Ok(format!("Values {} {}", v1, v2)) -} - -fn main() { - App::new() - .resource(r"/a/{v1}/{v2}/", |r| r.f(index)) - .finish(); -} -``` - -For this example for path '/a/1/2/', values v1 and v2 will resolve to "1" and "2". - -It is possible to create a `PathBuf` from a tail path parameter. The returned `PathBuf` is -percent-decoded. If a segment is equal to "..", the previous segment (if -any) is skipped. - -For security purposes, if a segment meets any of the following conditions, -an `Err` is returned indicating the condition met: - -* Decoded segment starts with any of: `.` (except `..`), `*` -* Decoded segment ends with any of: `:`, `>`, `<` -* Decoded segment contains any of: `/` -* On Windows, decoded segment contains any of: '\' -* Percent-encoding results in invalid UTF8. - -As a result of these conditions, a `PathBuf` parsed from request path parameter is -safe to interpolate within, or use as a suffix of, a path without additional checks. - -```rust -# extern crate actix_web; -use std::path::PathBuf; -use actix_web::{App, HttpRequest, Result, http::Method}; - -fn index(req: HttpRequest) -> Result { - let path: PathBuf = req.match_info().query("tail")?; - Ok(format!("Path {:?}", path)) -} - -fn main() { - App::new() - .resource(r"/a/{tail:.*}", |r| r.method(Method::GET).f(index)) - .finish(); -} -``` - -List of `FromParam` implementations can be found in -[api docs](../actix_web/dev/trait.FromParam.html#foreign-impls) - -## Path information extractor - -Actix provides functionality for type safe path information extraction. -[Path](../actix_web/struct.Path.html) extracts information, destination type -could be defined in several different forms. Simplest approach is to use -`tuple` type. Each element in tuple must correpond to one element from -path pattern. i.e. you can match path pattern `/{id}/{username}/` against -`Pyth<(u32, String)>` type, but `Path<(String, String, String)>` type will -always fail. - -```rust -# extern crate actix_web; -use actix_web::{App, Path, Result, http::Method}; - -// extract path info using serde -fn index(info: Path<(String, u32)>) -> Result { - Ok(format!("Welcome {}! id: {}", info.0, info.1)) -} - -fn main() { - let app = App::new() - .resource("/{username}/{id}/index.html", // <- define path parameters - |r| r.method(Method::GET).with(index)); -} -``` - - -It also possible to extract path pattern information to a struct. In this case, -this struct must implement *serde's *`Deserialize` trait. - -```rust -# extern crate actix_web; -#[macro_use] extern crate serde_derive; -use actix_web::{App, Path, Result, http::Method}; - -#[derive(Deserialize)] -struct Info { - username: String, -} - -// extract path info using serde -fn index(info: Path) -> Result { - Ok(format!("Welcome {}!", info.username)) -} - -fn main() { - let app = App::new() - .resource("/{username}/index.html", // <- define path parameters - |r| r.method(Method::GET).with(index)); -} -``` - -[Query](../actix_web/struct.Query.html) provides similar functionality for -request query parameters. - - -## Generating resource URLs - -Use the [HttpRequest.url_for()](../actix_web/struct.HttpRequest.html#method.url_for) -method to generate URLs based on resource patterns. For example, if you've configured a -resource with the name "foo" and the pattern "{a}/{b}/{c}", you might do this: - -```rust -# extern crate actix_web; -# use actix_web::{App, Result, HttpRequest, HttpResponse, http::Method, http::header}; -# -fn index(req: HttpRequest) -> Result { - let url = req.url_for("foo", &["1", "2", "3"])?; // <- generate url for "foo" resource - Ok(HttpResponse::Found() - .header(header::LOCATION, url.as_str()) - .finish()) -} - -fn main() { - let app = App::new() - .resource("/test/{a}/{b}/{c}", |r| { - r.name("foo"); // <- set resource name, then it could be used in `url_for` - r.method(Method::GET).f(|_| HttpResponse::Ok()); - }) - .route("/test/", Method::GET, index) - .finish(); -} -``` - -This would return something like the string *http://example.com/test/1/2/3* (at least if -the current protocol and hostname implied http://example.com). -`url_for()` method returns [*Url object*](https://docs.rs/url/1.6.0/url/struct.Url.html) so you -can modify this url (add query parameters, anchor, etc). -`url_for()` could be called only for *named* resources otherwise error get returned. - -## External resources - -Resources that are valid URLs, can be registered as external resources. They are useful -for URL generation purposes only and are never considered for matching at request time. - -```rust -# extern crate actix_web; -use actix_web::{App, HttpRequest, HttpResponse, Error}; - -fn index(mut req: HttpRequest) -> Result { - let url = req.url_for("youtube", &["oHg5SJYRHA0"])?; - assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0"); - Ok(HttpResponse::Ok().into()) -} - -fn main() { - let app = App::new() - .resource("/index.html", |r| r.f(index)) - .external_resource("youtube", "https://youtube.com/watch/{video_id}") - .finish(); -} -``` - -## Path normalization and redirecting to slash-appended routes - -By normalizing it means: - -* Add a trailing slash to the path. -* Double slashes are replaced by one. - -The handler returns as soon as it finds a path that resolves -correctly. The order if all enable is 1) merge, 3) both merge and append -and 3) append. If the path resolves with -at least one of those conditions, it will redirect to the new path. - -If *append* is *true*, append slash when needed. If a resource is -defined with trailing slash and the request doesn't have one, it will -be appended automatically. - -If *merge* is *true*, merge multiple consecutive slashes in the path into one. - -This handler designed to be used as a handler for application's *default resource*. - -```rust -# extern crate actix_web; -# #[macro_use] extern crate serde_derive; -# use actix_web::*; -use actix_web::http::NormalizePath; -# -# fn index(req: HttpRequest) -> HttpResponse { -# HttpResponse::Ok().into() -# } -fn main() { - let app = App::new() - .resource("/resource/", |r| r.f(index)) - .default_resource(|r| r.h(NormalizePath::default())) - .finish(); -} -``` - -In this example `/resource`, `//resource///` will be redirected to `/resource/`. - -In this example, the path normalization handler is registered for all methods, -but you should not rely on this mechanism to redirect *POST* requests. The redirect of the -slash-appending *Not Found* will turn a *POST* request into a GET, losing any -*POST* data in the original request. - -It is possible to register path normalization only for *GET* requests only: - -```rust -# extern crate actix_web; -# #[macro_use] extern crate serde_derive; -use actix_web::{App, HttpRequest, http::Method, http::NormalizePath}; -# -# fn index(req: HttpRequest) -> &'static str { -# "test" -# } -fn main() { - let app = App::new() - .resource("/resource/", |r| r.f(index)) - .default_resource(|r| r.method(Method::GET).h(NormalizePath::default())) - .finish(); -} -``` - -## Using an Application Prefix to Compose Applications - -The `App::prefix()` method allows to set a specific application prefix. -This prefix represents a resource prefix that will be prepended to all resource patterns added -by the resource configuration. This can be used to help mount a set of routes at a different -location than the included callable's author intended while still maintaining the same -resource names. - -For example: - -```rust -# extern crate actix_web; -# use actix_web::*; -# -fn show_users(req: HttpRequest) -> HttpResponse { - unimplemented!() -} - -fn main() { - App::new() - .prefix("/users") - .resource("/show", |r| r.f(show_users)) - .finish(); -} -``` - -In the above example, the *show_users* route will have an effective route pattern of -*/users/show* instead of */show* because the application's prefix argument will be prepended -to the pattern. The route will then only match if the URL path is */users/show*, -and when the `HttpRequest.url_for()` function is called with the route name show_users, -it will generate a URL with that same path. - -## Custom route predicates - -You can think of a predicate as a simple function that accepts a *request* object reference -and returns *true* or *false*. Formally, a predicate is any object that implements the -[`Predicate`](../actix_web/pred/trait.Predicate.html) trait. Actix provides -several predicates, you can check [functions section](../actix_web/pred/index.html#functions) -of api docs. - -Here is a simple predicate that check that a request contains a specific *header*: - -```rust -# extern crate actix_web; -# use actix_web::*; -use actix_web::{http, pred::Predicate, App, HttpRequest}; - -struct ContentTypeHeader; - -impl Predicate for ContentTypeHeader { - - fn check(&self, req: &mut HttpRequest) -> bool { - req.headers().contains_key(http::header::CONTENT_TYPE) - } -} - -fn main() { - App::new() - .resource("/index.html", |r| - r.route() - .filter(ContentTypeHeader) - .f(|_| HttpResponse::Ok())); -} -``` - -In this example, *index* handler will be called only if request contains *CONTENT-TYPE* header. - -Predicates have access to the application's state via `HttpRequest::state()`. -Also predicates can store extra information in -[request extensions](../actix_web/struct.HttpRequest.html#method.extensions). - -### Modifying predicate values - -You can invert the meaning of any predicate value by wrapping it in a `Not` predicate. -For example, if you want to return "METHOD NOT ALLOWED" response for all methods -except "GET": - -```rust -# extern crate actix_web; -# extern crate http; -# use actix_web::*; -use actix_web::{pred, App, HttpResponse}; - -fn main() { - App::new() - .resource("/index.html", |r| - r.route() - .filter(pred::Not(pred::Get())) - .f(|req| HttpResponse::MethodNotAllowed())) - .finish(); -} -``` - -The `Any` predicate accepts a list of predicates and matches if any of the supplied -predicates match. i.e: - -```rust,ignore - pred::Any(pred::Get()).or(pred::Post()) -``` - -The `All` predicate accepts a list of predicates and matches if all of the supplied -predicates match. i.e: - -```rust,ignore - pred::All(pred::Get()).and(pred::Header("content-type", "plain/text")) -``` - -## Changing the default Not Found response - -If the path pattern can not be found in the routing table or a resource can not find matching -route, the default resource is used. The default response is *NOT FOUND*. -It is possible to override the *NOT FOUND* response with `App::default_resource()`. -This method accepts a *configuration function* same as normal resource configuration -with `App::resource()` method. - -```rust -# extern crate actix_web; -use actix_web::{App, HttpResponse, http::Method, pred}; - -fn main() { - App::new() - .default_resource(|r| { - r.method(Method::GET).f(|req| HttpResponse::NotFound()); - r.route().filter(pred::Not(pred::Get())) - .f(|req| HttpResponse::MethodNotAllowed()); - }) -# .finish(); -} -``` diff --git a/guide/src/qs_7.md b/guide/src/qs_7.md deleted file mode 100644 index b07a25d6f..000000000 --- a/guide/src/qs_7.md +++ /dev/null @@ -1,357 +0,0 @@ -# Request & Response - -## Response - -A builder-like pattern is used to construct an instance of `HttpResponse`. -`HttpResponse` provides several methods that return a `HttpResponseBuilder` instance, -which implements various convenience methods for building responses. - -> Check the [documentation](../actix_web/dev/struct.HttpResponseBuilder.html) -> for type descriptions. - -The methods `.body`, `.finish`, and `.json` finalize response creation and -return a constructed *HttpResponse* instance. If this methods is called on the same -builder instance multiple times, the builder will panic. - -```rust -# extern crate actix_web; -use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding}; - -fn index(req: HttpRequest) -> HttpResponse { - HttpResponse::Ok() - .content_encoding(ContentEncoding::Br) - .content_type("plain/text") - .header("X-Hdr", "sample") - .body("data") -} -# fn main() {} -``` - -## Content encoding - -Actix automatically *compresses*/*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`. - -Response payload is compressed based on the *content_encoding* parameter. -By default, `ContentEncoding::Auto` is used. If `ContentEncoding::Auto` is selected, -then the compression depends on the request's `Accept-Encoding` header. - -> `ContentEncoding::Identity` can be used to disable compression. -> If another content encoding is selected, the compression is enforced for that codec. - -For example, to enable `brotli` use `ContentEncoding::Br`: - -```rust -# extern crate actix_web; -use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding}; - -fn index(req: HttpRequest) -> HttpResponse { - HttpResponse::Ok() - .content_encoding(ContentEncoding::Br) - .body("data") -} -# fn main() {} -``` - -In this case we explicitly disable content compression -by setting content encoding to a `Identity` value: - -```rust -# extern crate actix_web; -use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding}; - -fn index(req: HttpRequest) -> HttpResponse { - HttpResponse::Ok() - .content_encoding(ContentEncoding::Identity) // <- disable compression - .body("data") -} -# fn main() {} -``` - -Also it is possible to set default content encoding on application level, by -default `ContentEncoding::Auto` is used, which implies automatic content compression -negotiation. - -```rust -# extern crate actix_web; -use actix_web::{App, HttpRequest, HttpResponse, http::ContentEncoding}; - -fn index(req: HttpRequest) -> HttpResponse { - HttpResponse::Ok() - .body("data") -} -fn main() { - let app = App::new() - .default_encoding(ContentEncoding::Identity) // <- disable compression for all routes - .resource("/index.html", |r| r.with(index)); -} -``` - -## 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` 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`. - -```rust -# extern crate actix_web; -#[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) -> Result { - 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 *HttpResponse::json()*. This method returns a -[*JsonBody*](../actix_web/dev/struct.JsonBody.html) object which resolves into -the deserialized value. - -```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)] -struct MyObj { - name: String, - number: i32, -} - -fn index(mut req: HttpRequest) -> Box> { - req.json().from_err() - .and_then(|val: MyObj| { - println!("model: {:?}", val); - Ok(HttpResponse::Ok().json(val)) // <- send response - }) - .responder() -} -# fn main() {} -``` - -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. - -```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(req: HttpRequest) -> Box> { - // `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| { // <- body is loaded, now we can deserialize json - let obj = serde_json::from_slice::(&body)?; - Ok(HttpResponse::Ok().json(obj)) // <- send response - }) - .responder() -} -# fn main() {} -``` - -> A complete example for both options is available in -> [examples directory](https://github.com/actix/actix-web/tree/master/examples/json/). - -## JSON Response - -The `Json` type allows to respond with well-formed JSON data: simply return a value of -type Json where `T` is the type of a structure to serialize into *JSON*. -The type `T` must implement the `Serialize` trait from *serde*. - -```rust -# extern crate actix_web; -#[macro_use] extern crate serde_derive; -use actix_web::{App, HttpRequest, Json, Result, http::Method}; - -#[derive(Serialize)] -struct MyObj { - name: String, -} - -fn index(req: HttpRequest) -> Result> { - Ok(Json(MyObj{name: req.match_info().query("name")?})) -} - -fn main() { - App::new() - .resource(r"/a/{name}", |r| r.method(Method::GET).f(index)) - .finish(); -} -``` - -## 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. - -Chunked encoding on a response can be enabled with `HttpResponseBuilder::chunked()`. -This takes effect only for `Body::Streaming(BodyStream)` or `Body::StreamingContext` bodies. -If the response payload compression is enabled and a streaming body is used, chunked encoding -is enabled automatically. - -> Enabling chunked encoding for *HTTP/2.0* responses is forbidden. - -```rust -# extern crate bytes; -# extern crate actix_web; -# extern crate futures; -# use futures::Stream; -use actix_web::*; -use bytes::Bytes; -use futures::stream::once; - -fn index(req: HttpRequest) -> HttpResponse { - HttpResponse::Ok() - .chunked() - .body(Body::Streaming(Box::new(once(Ok(Bytes::from_static(b"data")))))) -} -# fn main() {} -``` - -## Multipart body - -Actix provides multipart stream support. -[*Multipart*](../actix_web/multipart/struct.Multipart.html) is implemented as -a stream of multipart items. Each item can be a -[*Field*](../actix_web/multipart/struct.Field.html) 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: - -```rust,ignore -# extern crate actix_web; -use actix_web::*; - -fn index(req: HttpRequest) -> Box> { - 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.headers(), 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(()))) - } - } - }) -} -``` - -> A full example is available in the -> [examples directory](https://github.com/actix/actix-web/tree/master/examples/multipart/). - -## Urlencoded body - -Actix provides support for *application/x-www-form-urlencoded* encoded bodies. -`HttpResponse::urlencoded()` returns a -[*UrlEncoded*](../actix_web/dev/struct.UrlEncoded.html) 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. - -```rust -# extern crate actix_web; -# extern crate futures; -#[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> { - req.urlencoded::() // <- 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: - -```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> { - req.from_err() - .fold((), |_, chunk| { - println!("Chunk: {:?}", chunk); - result::<_, error::PayloadError>(Ok(())) - }) - .map(|_| HttpResponse::Ok().finish()) - .responder() -} -# fn main() {} -``` diff --git a/guide/src/qs_8.md b/guide/src/qs_8.md deleted file mode 100644 index f80fb8eb3..000000000 --- a/guide/src/qs_8.md +++ /dev/null @@ -1,176 +0,0 @@ -# Testing - -Every application should be well tested. Actix provides tools to perform unit and -integration tests. - -## Unit tests - -For unit testing, actix provides a request builder type and a simple handler runner. -[*TestRequest*](../actix_web/test/struct.TestRequest.html) implements a builder-like pattern. -You can generate a `HttpRequest` instance with `finish()`, or you can -run your handler with `run()` or `run_async()`. - -```rust -# extern crate actix_web; -use actix_web::{http, test, HttpRequest, HttpResponse, HttpMessage}; - -fn index(req: HttpRequest) -> HttpResponse { - if let Some(hdr) = req.headers().get(http::header::CONTENT_TYPE) { - if let Ok(s) = hdr.to_str() { - return HttpResponse::Ok().into() - } - } - HttpResponse::BadRequest().into() -} - -fn main() { - let resp = test::TestRequest::with_header("content-type", "text/plain") - .run(index) - .unwrap(); - assert_eq!(resp.status(), http::StatusCode::OK); - - let resp = test::TestRequest::default() - .run(index) - .unwrap(); - assert_eq!(resp.status(), http::StatusCode::BAD_REQUEST); -} -``` - -## Integration tests - -There are several methods for testing your application. Actix provides -[*TestServer*](../actix_web/test/struct.TestServer.html), which can be used -to run the application with specific handlers in a real http server. - -`TestServer::get()`, `TestServer::post()`, and `TestServer::client()` -methods can be used to send requests to the test server. - -A simple form `TestServer` can be configured to use a handler. -`TestServer::new` method accepts a configuration function, and the only argument -for this function is a *test application* instance. - -> Check the [api documentation](../actix_web/test/struct.TestApp.html) for more information. - -```rust -# extern crate actix_web; -use actix_web::{HttpRequest, HttpResponse, HttpMessage}; -use actix_web::test::TestServer; - -fn index(req: HttpRequest) -> HttpResponse { - HttpResponse::Ok().into() -} - -fn main() { - let mut srv = TestServer::new(|app| app.handler(index)); // <- Start new test server - - let request = srv.get().finish().unwrap(); // <- create client request - let response = srv.execute(request.send()).unwrap(); // <- send request to the server - assert!(response.status().is_success()); // <- check response - - let bytes = srv.execute(response.body()).unwrap(); // <- read response body -} -``` - -The other option is to use an application factory. In this case, you need to pass the factory -function the same way as you would for real http server configuration. - -```rust -# extern crate actix_web; -use actix_web::{http, test, App, HttpRequest, HttpResponse}; - -fn index(req: HttpRequest) -> HttpResponse { - HttpResponse::Ok().into() -} - -/// This function get called by http server. -fn create_app() -> App { - App::new() - .resource("/test", |r| r.h(index)) -} - -fn main() { - let mut srv = test::TestServer::with_factory(create_app); // <- Start new test server - - let request = srv.client( - http::Method::GET, "/test").finish().unwrap(); // <- create client request - let response = srv.execute(request.send()).unwrap(); // <- send request to the server - - assert!(response.status().is_success()); // <- check response -} -``` - -If you need more complex application configuration, use the `TestServer::build_with_state()` -method. For example, you may need to initialize application state or start `SyncActor`'s for diesel -interation. This method accepts a closure that constructs the application state, -and it runs when the actix system is configured. Thus, you can initialize any additional actors. - -```rust,ignore -#[test] -fn test() { - let srv = TestServer::build_with_state(|| { // <- construct builder with config closure - // we can start diesel actors - let addr = SyncArbiter::start(3, || { - DbExecutor(SqliteConnection::establish("test.db").unwrap()) - }); - // then we can construct custom state, or it could be `()` - MyState{addr: addr} - }) - .start(|app| { // <- register server handlers and start test server - app.resource( - "/{username}/index.html", |r| r.with( - |p: Path| format!("Welcome {}!", p.username))); - }); - - // now we can run our test code -); -``` - -## WebSocket server tests - -It is possible to register a *handler* with `TestApp::handler()`, which -initiates a web socket connection. *TestServer* provides the method `ws()`, which connects to -the websocket server and returns ws reader and writer objects. *TestServer* also -provides an `execute()` method, which runs future objects to completion and returns -result of the future computation. - -The following example demonstrates how to test a websocket handler: - -```rust -# extern crate actix; -# extern crate actix_web; -# extern crate futures; -# extern crate http; -# extern crate bytes; - -use actix_web::*; -use futures::Stream; -# use actix::prelude::*; - -struct Ws; // <- WebSocket actor - -impl Actor for Ws { - type Context = ws::WebsocketContext; -} - -impl StreamHandler for Ws { - - fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) { - match msg { - ws::Message::Text(text) => ctx.text(text), - _ => (), - } - } -} - -fn main() { - let mut srv = test::TestServer::new( // <- start our server with ws handler - |app| app.handler(|req| ws::start(req, Ws))); - - let (reader, mut writer) = srv.ws().unwrap(); // <- connect to ws server - - writer.text("text"); // <- send message to server - - let (item, reader) = srv.execute(reader.into_future()).unwrap(); // <- wait for one message - assert_eq!(item, Some(ws::Message::Text("text".to_owned()))); -} -``` diff --git a/guide/src/qs_9.md b/guide/src/qs_9.md deleted file mode 100644 index e0d71f12b..000000000 --- a/guide/src/qs_9.md +++ /dev/null @@ -1,48 +0,0 @@ -# WebSockets - -Actix supports WebSockets out-of-the-box. It is possible to convert a request's `Payload` -to a stream of [*ws::Message*](../actix_web/ws/enum.Message.html) with -a [*ws::WsStream*](../actix_web/ws/struct.WsStream.html) and then use stream -combinators to handle actual messages, but it is simpler to handle websocket communications -with an http actor. - -The following is an example of a simple websocket echo server: - -```rust -# extern crate actix; -# extern crate actix_web; -use actix::*; -use actix_web::*; - -/// Define http actor -struct Ws; - -impl Actor for Ws { - type Context = ws::WebsocketContext; -} - -/// Handler for ws::Message message -impl StreamHandler for Ws { - - fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) { - match msg { - ws::Message::Ping(msg) => ctx.pong(&msg), - ws::Message::Text(text) => ctx.text(text), - ws::Message::Binary(bin) => ctx.binary(bin), - _ => (), - } - } -} - -fn main() { - App::new() - .resource("/ws/", |r| r.f(|req| ws::start(req, Ws))) // <- register websocket route - .finish(); -} -``` - -> A simple websocket echo server example is available in the -> [examples directory](https://github.com/actix/actix-web/blob/master/examples/websocket). - -> An example chat server with the ability to chat over a websocket or tcp connection -> is available in [websocket-chat directory](https://github.com/actix/actix-web/tree/master/examples/websocket-chat/) diff --git a/src/lib.rs b/src/lib.rs index fff68afa1..1e32dcc7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ //! Besides the API documentation (which you are currently looking //! at!), several other resources are available: //! -//! * [User Guide](https://actix.rs/actix-web/guide/) +//! * [User Guide](https://actix.rs/book/actix-web/) //! * [Chat on gitter](https://gitter.im/actix/actix) //! * [GitHub repository](https://github.com/actix/actix-web) //! * [Cargo package](https://crates.io/crates/actix-web)