1
0
mirror of https://github.com/actix/actix-website synced 2024-11-30 19:14:36 +01:00

Merge pull request #90 from cldershem/update1.0

[WIP] Update website docs to 1.0.
This commit is contained in:
Nikolay Kim 2019-07-02 11:42:10 +06:00 committed by GitHub
commit 9fcd8c10be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
146 changed files with 3402 additions and 2264 deletions

View File

@ -16,4 +16,6 @@ baseURL = "https://actix.rs"
[params] [params]
actixVersion = "0.7" actixVersion = "0.7"
actixWebVersion = "0.7" actixWebVersion = "1.0"
actixWebMinRustVersion = "1.34"
actixMinRustVersion = "1.31"

View File

@ -12,13 +12,16 @@ weight: 10
Actix is your door to developing web services with Rust and this documentation Actix is your door to developing web services with Rust and this documentation
is going to guide you. is going to guide you.
This documentation currently covers mostly the `actix-web` part which is the This documentation currently covers mostly the `actix-web` part which is the high level
high level web framework build on top of the `actix` actor framework and the web framework previously built on top of the `actix` actor framework and the [Tokio][tokio]
[Tokio](https://tokio.rs/) async IO system. This is the part that is from an async IO system. This is the part that is from an API stability point of view the most stable.
API stability point of view the most stable.
If you haven't used actix yet it's best to start with the [getting started If you haven't used `actix-web` yet it's best to start with the [getting started
guide](getting-started/). If you already know your ways around and you need guide][gettingstarted]. If you already know your ways around and you need
specific information you might want to read the [actix-web API specific information you might want to read the [actix-web API docs][actixwebdocs]
docs](https://actix.rs/api/actix-web/stable/actix_web/) (or the lower level [actix API (or the lower level [actix API docs][actixdocs]).
docs](https://docs.rs/actix)).
[gettingstarted]: ./getting-started
[actixwebdocs]: https://docs.rs/actix-web
[actixdocs]: https://docs.rs/actix
[tokio]: (https://tokio.rs/)

View File

@ -7,33 +7,32 @@ weight: 140
# Writing an Application # Writing an Application
`actix-web` provides various primitives to build web servers and applications with Rust. `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, It provides routing, middlewares, pre-processing of requests, post-processing of
websocket protocol handling, multipart streams, etc. responses, etc.
All actix web servers are built around the `App` instance. It is used for All `actix-web` servers are built around the `App` instance. It is used for
registering routes for resources and middlewares. It also stores application registering routes for resources and middlewares. It also stores application
state shared across all handlers within same application. state shared across all handlers within same scope.
Applications act as a namespace for all routes, i.e. all routes for a specific application An application's `scope` acts as a namespace for all routes, i.e. all routes for a
have the same url path prefix. The application prefix always contains a leading "/" slash. specific application scope have the same url path prefix. The application prefix always
If a supplied prefix does not contain leading slash, it is automatically inserted. contains a leading "/" slash. If a supplied prefix does not contain leading slash,
The prefix should consist of value path segments. it is automatically inserted. The prefix should consist of value path segments.
> For an application with prefix `/app`, > For an application with scope `/app`,
> any request with the paths `/app`, `/app/`, or `/app/test` would match; > any request with the paths `/app`, `/app/`, or `/app/test` would match;
> however, the path `/application` would not match. > however, the path `/application` would not match.
{{< include-example example="application" section="make_app" >}} {{< include-example example="application" file="app.rs" section="setup" >}}
In this example, an application with the `/app` prefix and a `index.html` resource 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. are created. This resource is available through the `/app/index.html` url.
> For more information, check the > For more information, check the [URL Dispatch][usingappprefix] section.
> [URL Dispatch](/docs/url-dispatch/index.html#using-an-application-prefix-to-compose-applications) section.
Multiple applications can be served with one server: Multiple application scopes can be served with one server:
{{< include-example example="application" section="run_server" >}} {{< include-example example="application" file="main.rs" section="multi" >}}
All `/app1` requests route to the first application, `/app2` to the second, and all other to the third. 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 **Applications get matched based on registration order**. If an application with a more generic
@ -43,10 +42,10 @@ as the first application, it would match all incoming requests.
## State ## State
Application state is shared with all routes and resources within the same application. Application state is shared with all routes and resources within the same scope. State
When using an http actor, state can be accessed with the `HttpRequest::state()` as read-only, can be accessed with the `web::Data<State>` extractor as read-only, but interior mutability with
but interior mutability with `RefCell` can be used to achieve state mutability. `Cell` can be used to achieve state mutability. State is also available for route
State is also available for route matching predicates and middlewares. matching guards and middlewares.
Let's write a simple application that uses shared state. We are going to store request count Let's write a simple application that uses shared state. We are going to store request count
in the state: in the state:
@ -57,12 +56,12 @@ When the app is initialized it needs to be passed the initial state:
{{< include-example example="application" file="state.rs" section="make_app" >}} {{< include-example example="application" file="state.rs" section="make_app" >}}
> **Note**: http server accepts an application factory rather than an application > **Note**: `HttpServer` accepts an application factory rather than an application
> instance. Http server constructs an application instance for each thread, thus application state > instance. `HttpServer` constructs an application instance for each thread, thus
> must be constructed multiple times. If you want to share state between different threads, a > application state must be constructed multiple times. If you want to share state between
> shared object should be used, e.g. `Arc`. There is also an [Example](https://github.com/actix/examples/blob/master/state/src/main.rs) using `Arc` for this. Application state does not need to be `Send` and `Sync`, > different threads, a shared object should be used, e.g. `Arc`. There is also an
> but the application factory must be `Send` + `Sync`. > [Example][stateexample] using `Arc` for this. Application state does not need to be
> > `Send` and `Sync`, but the application factory must be `Send` + `Sync`.
To start the previous app, create it into a closure: To start the previous app, create it into a closure:
@ -72,41 +71,58 @@ To start the previous app, create it into a closure:
Combining multiple applications with different state is possible as well. Combining multiple applications with different state is possible as well.
[server::new](https://docs.rs/actix-web/*/actix_web/server/fn.new.html) requires the handler to have a single type. {{< include-example example="application" file="combine.rs" section="combine" >}}
This limitation can easily be overcome with the [App::boxed](https://docs.rs/actix-web/*/actix_web/struct.App.html#method.boxed) method, which converts an App into a boxed trait object. ## Using an Application Scope to Compose Applications
{{< include-example example="application" file="state.rs" section="combine" >}} The `web::scope()` method allows to set a specific application prefix. This scope represents
a resource prefix that will be prepended to all resource patterns added by the resource
## Using an Application Prefix to Compose Applications 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.
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: For example:
{{< include-example example="url-dispatch" file="prefix.rs" section="prefix" >}} {{< include-example example="application" file="scope.rs" section="scope" >}}
In the above example, the *show_users* route will have an effective route pattern of 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 */users/show* instead of */show* because the application's scope argument will be prepended
to the pattern. The route will then only match if the URL path is */users/show*, 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, and when the `HttpRequest.url_for()` function is called with the route name show_users,
it will generate a URL with that same path. it will generate a URL with that same path.
## Application predicates and virtual hosting ## Application guards and virtual hosting
You can think of a predicate as a simple function that accepts a *request* object reference You can think of a guard as a simple function that accepts a *request* object reference
and returns *true* or *false*. Formally, a predicate is any object that implements the and returns *true* or *false*. Formally, a guard is any object that implements the
[`Predicate`](../actix_web/pred/trait.Predicate.html) trait. Actix provides [`Guard`][guardtrait] trait. Actix-web provides several guards, you can check
several predicates, you can check [functions section][guardfuncs] of api docs.
[functions section](../../actix-web/actix_web/pred/index.html#functions) of api docs.
Any of this predicates could be used One of the provided guards is [`Header`][guardheader], it can be used as application's
with [`App::filter()`](../actix_web/struct.App.html#method.filter) method. One of the filter based on request's header information.
provided predicates is [`Host`](../actix_web/pred/fn.Host.html), it can be used
as application's filter based on request's host information.
{{< include-example example="application" file="vh.rs" section="vh" >}} {{< include-example example="application" file="vh.rs" section="vh" >}}
# Configure
For simplicity and reusability both `App` and `web::scope` provide the `configure` method.
This function is useful for moving parts of configuration to a different module or even
library. For example, some of the resource's configuration could be moved to different
module.
{{< include-example example="application" file="config.rs" section="config" >}}
The result of the above example would be:
```
/ -> "/"
/app -> "app"
/api/test -> "test"
```
Each `ServiceConfig` can have it's own `data`, `routes`, and `services`
[usingappprefix]: /docs/url-dispatch/index.html#using-an-application-prefix-to-compose-applications
[stateexample]: https://github.com/actix/examples/blob/master/state/src/main.rs
[guardtrait]: https://docs.rs/actix-web/1.0.2/actix_web/guard/trait.Guard.html
[guardfuncs]: https://docs.rs/actix-web/1.0.2/actix_web/guard/index.html#functions
[guardheader]: ((https://docs.rs/actix-web/1.0.2/actix_web/guard/fn.Header.html

View File

@ -6,13 +6,11 @@ weight: 1000
# Auto-Reloading Development Server # Auto-Reloading Development Server
During development it can be very handy to have cargo automatically recompile During development it can be very handy to have cargo automatically recompile the code
the code on change. This can be accomplished by using on change. This can be accomplished by using [cargo-watch][cargowatch]. Because an
[cargo-watch](https://github.com/passcod/cargo-watch). Because an actix app actix app will typically bind to a port for listening for incoming HTTP requests it makes
will typically bind to a port for listening for incoming HTTP requests it makes sense to combine this with the [listenfd][listenfd] crate and the [systemfd][systemfd]
sense to combine this with the [listenfd](https://crates.io/crates/listenfd) utility to ensure the socket is kept open while the app is compiling and reloading.
crate and the [systemfd](https://github.com/mitsuhiko/systemfd) utility to
ensure the socket is kept open while the app is compiling and reloading.
`systemfd` will open a socket and pass it to `cargo-watch` which will watch for `systemfd` will open a socket and pass it to `cargo-watch` which will watch for
changes and then invoke the compiler and run your actix app. The actix app changes and then invoke the compiler and run your actix app. The actix app
@ -30,8 +28,7 @@ cargo install systemfd cargo-watch
## Code Changes ## Code Changes
Additionally you need to slightly modify your actix app so that it can pick up Additionally you need to slightly modify your actix app so that it can pick up
an external socket opened by `systemfd`. Add the listenfd dependency to your an external socket opened by `systemfd`. Add the listenfd dependency to your app:
app:
```ini ```ini
[dependencies] [dependencies]
@ -40,33 +37,7 @@ listenfd = "0.3"
Then modify your server code to only invoke `bind` as a fallback: Then modify your server code to only invoke `bind` as a fallback:
```rust {{< include-example example="autoreload" file="main.rs" section="autoreload" >}}
extern crate actix_web;
extern crate listenfd;
use listenfd::ListenFd;
use actix_web::{server, App, HttpRequest, Responder};
fn index(_req: &HttpRequest) -> impl Responder {
"Hello World!"
}
fn main() {
let mut listenfd = ListenFd::from_env();
let mut server = server::new(|| {
App::new()
.resource("/", |r| r.f(index))
});
server = if let Some(l) = listenfd.take_tcp_listener(0).unwrap() {
server.listen(l)
} else {
server.bind("127.0.0.1:3000").unwrap()
};
server.run();
}
```
## Running the Server ## Running the Server
@ -75,3 +46,7 @@ To now run the development server invoke this command:
``` ```
systemfd --no-pid -s http::3000 -- cargo watch -x run systemfd --no-pid -s http::3000 -- cargo watch -x run
``` ```
[cargowatch]: https://github.com/passcod/cargo-watch
[listenfd]: https://crates.io/crates/listenfd
[systemfd]: https://github.com/mitsuhiko/systemfd

View File

@ -16,117 +16,33 @@ Let's create a simple database api that can insert a new user row into a SQLite
We must define a sync actor and a connection that this actor will use. The same approach We must define a sync actor and a connection that this actor will use. The same approach
can be used for other databases. can be used for other databases.
```rust {{< include-example example="og_databases" file="main.rs" section="actor" >}}
use actix::prelude::*;
struct DbExecutor(SqliteConnection);
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
}
```
This is the definition of our actor. Now, we must define the *create user* message and response. This is the definition of our actor. Now, we must define the *create user* message and response.
```rust {{< include-example example="og_databases" file="main.rs" section="message" >}}
struct CreateUser {
name: String,
}
impl Message for CreateUser {
type Result = Result<User, Error>;
}
```
We can send a `CreateUser` message to the `DbExecutor` actor, and as a result, we will receive a 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. `User` model instance. Next, we must define the handler implementation for this message.
```rust {{< include-example example="og_databases" file="main.rs" section="handler" >}}
impl Handler<CreateUser> for DbExecutor {
type Result = Result<User, Error>;
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::<models::User>(&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. 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 All we need is to start *DbExecutor* actors and store the address in a state where http handler
can access it. can access it.
```rust {{< include-example example="og_databases" file="main.rs" section="main" >}}
/// This is state where we will store *DbExecutor* address.
struct State {
db: Addr<DbExecutor>,
}
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; We will use the address in a request handler. The handle returns a future object;
thus, we receive the message response asynchronously. thus, we receive the message response asynchronously.
`Route::a()` must be used for async handler registration. `Route::a()` must be used for async handler registration.
{{< include-example example="og_databases" file="main.rs" section="index" >}}
```rust > A full example is available in the [examples directory][examples].
/// Async handler
fn index(req: &HttpRequest<State>) -> Box<Future<Item=HttpResponse, Error=Error>> {
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/examples/tree/master/diesel/).
> More information on sync actors can be found in the > More information on sync actors can be found in the
> [actix documentation](https://docs.rs/actix/0.7.0/actix/sync/index.html). > [actix documentation][actixdocs].
[examples]: https://github.com/actix/examples/tree/master/diesel/
[actixdocs]: https://docs.rs/actix/0.7.0/actix/sync/index.html

View File

@ -6,13 +6,13 @@ weight: 180
# Errors # Errors
Actix uses its own [`actix_web::error::Error`][actixerror] type and Actix-web uses its own [`actix_web::error::Error`][actixerror] type and
[`actix_web::error::ResponseError`][responseerror] trait for error handling [`actix_web::error::ResponseError`][responseerror] trait for error handling
from web handlers. from web handlers.
If a handler returns an `Error` (referring to the [general Rust trait If a handler returns an `Error` (referring to the [general Rust trait
`std::error::Error`][stderror]) in a `Result` that also implements the `std::error::Error`][stderror]) in a `Result` that also implements the
`ResponseError` trait, actix will render that error as an HTTP response. `ResponseError` trait, actix-web will render that error as an HTTP response.
`ResponseError` has a single function called `error_response()` that returns `ResponseError` has a single function called `error_response()` that returns
`HttpResponse`: `HttpResponse`:
@ -30,7 +30,7 @@ A `Responder` coerces compatible `Result`s into HTTP responses:
impl<T: Responder, E: Into<Error>> Responder for Result<T, E> impl<T: Responder, E: Into<Error>> Responder for Result<T, E>
``` ```
`Error` in the code above is actix's error definition, and any errors that `Error` in the code above is actix-web's error definition, and any errors that
implement `ResponseError` can be converted to one automatically. implement `ResponseError` can be converted to one automatically.
Actix-web provides `ResponseError` implementations for some common non-actix Actix-web provides `ResponseError` implementations for some common non-actix
@ -40,34 +40,19 @@ converted into an `HttpInternalServerError`:
```rust ```rust
use std::io; use std::io;
fn index(req: &HttpRequest) -> io::Result<fs::NamedFile> { fn index(_req: HttpRequest) -> io::Result<fs::NamedFile> {
Ok(fs::NamedFile::open("static/index.html")?) Ok(fs::NamedFile::open("static/index.html")?)
} }
``` ```
See [the actix-web API documentation][responseerrorimpls] for a full list of See [the actix-web API documentation][responseerrorimpls] for a full list of foreign
foreign implementations for `ResponseError`. implementations for `ResponseError`.
## An example of a custom error response ## An example of a custom error response
Here's an example implementation for `ResponseError`: Here's an example implementation for `ResponseError`:
```rust {{< include-example example="errors" file="main.rs" section="response-error" >}}
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"})
}
```
`ResponseError` has a default implementation for `error_response()` that will `ResponseError` has a default implementation for `error_response()` that will
render a *500* (internal server error), and that's what will happen when the render a *500* (internal server error), and that's what will happen when the
@ -75,63 +60,19 @@ render a *500* (internal server error), and that's what will happen when the
Override `error_response()` to produce more useful results: Override `error_response()` to produce more useful results:
```rust {{< include-example example="errors" file="override_error.rs" section="override" >}}
#[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)
}
```
# Error helpers # Error helpers
Actix provides a set of error helper functions that are useful for generating Actix-web provides a set of error helper functions that are useful for generating
specific HTTP error codes from other errors. Here we convert `MyError`, which specific HTTP error codes from other errors. Here we convert `MyError`, which
doesn't implement the `ResponseError` trait, to a *400* (bad request) using doesn't implement the `ResponseError` trait, to a *400* (bad request) using
`map_err`: `map_err`:
```rust {{< include-example example="errors" file="helpers.rs" section="helpers" >}}
# extern crate actix_web;
use actix_web::*;
#[derive(Debug)] See the [API documentation for actix-web's `error` module][actixerror]
struct MyError { for a full list of available error helpers.
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.name))?)
}
```
See the [API documentation for actix-web's `error` module][errorhelpers] for a
full list of available error helpers.
# Compatibility with failure # Compatibility with failure
@ -165,27 +106,7 @@ An example of the former is that I might use failure to specify a `UserError`
enum which encapsulates a `ValidationError` to return whenever a user sends bad enum which encapsulates a `ValidationError` to return whenever a user sends bad
input: input:
```rust {{< include-example example="errors" file="recommend_one.rs" section="recommend-one" >}}
#[macro_use] extern crate failure;
use actix_web::{HttpResponse, http, error};
#[derive(Fail, Debug)]
enum UserError {
#[fail(display="Validation error on field: {}", field)]
ValidationError {
field: String,
}
}
impl error::ResponseError for UserError {
fn error_response(&self) -> HttpResponse {
match *self {
UserError::ValidationError { .. } => HttpResponse::new(
http::StatusCode::BAD_REQUEST),
}
}
}
```
This will behave exactly as intended because the error message defined with This will behave exactly as intended because the error message defined with
`display` is written with the explicit intent to be read by a user. `display` is written with the explicit intent to be read by a user.
@ -201,38 +122,21 @@ consumption.
Here's an example that maps an internal error to a user-facing `InternalError` Here's an example that maps an internal error to a user-facing `InternalError`
with a custom message: with a custom message:
```rust {{< include-example example="errors" file="recommend_two.rs" section="recommend-two" >}}
#[macro_use] extern crate failure;
use actix_web::{App, HttpRequest, HttpResponse, http, error, fs};
#[derive(Fail, Debug)]
enum UserError {
#[fail(display="An internal error occurred. Please try again later.")]
InternalError,
}
impl error::ResponseError for UserError {
fn error_response(&self) -> HttpResponse {
match *self {
UserError::InternalError => HttpResponse::new(
http::StatusCode::INTERNAL_SERVER_ERROR),
}
}
}
fn index(_: &HttpRequest) -> Result<&'static str, UserError> {
fs::NamedFile::open("static/index.html").map_err(|_e| UserError::InternalError)?;
Ok("success!")
}
```
By dividing errors into those which are user facing and those which are not, we By dividing errors into those which are user facing and those which are not, we
can ensure that we don't accidentally expose users to errors thrown by can ensure that we don't accidentally expose users to errors thrown by
application internals which they weren't meant to see. application internals which they weren't meant to see.
[actixerror]: ../../actix-web/actix_web/error/struct.Error.html # Error Logging
[errorhelpers]: ../../actix-web/actix_web/error/index.html#functions
This is a basic example using `middleware::Logger`:
{{< include-example example="errors" file="logging.rs" section="logging" >}}
[actixerror]: https://docs.rs/actix-web/1.0.2/actix_web/error/struct.Error.html
[errorhelpers]: https://docs.rs/actix-web/1.0.2/actix_web/trait.ResponseError.html
[failure]: https://github.com/rust-lang-nursery/failure [failure]: https://github.com/rust-lang-nursery/failure
[responseerror]: ../../actix-web/actix_web/error/trait.ResponseError.html [responseerror]: https://docs.rs/actix-web/1.0.2/actix_web/error/trait.ResponseError.html
[responseerrorimpls]: ../../actix-web/actix_web/error/trait.ResponseError.html#foreign-impls [responseerrorimpls]: https://docs.rs/actix-web/1.0.2/actix_web/error/trait.ResponseError.html#foreign-impls
[stderror]: https://doc.rust-lang.org/std/error/trait.Error.html [stderror]: https://doc.rust-lang.org/std/error/trait.Error.html

View File

@ -6,280 +6,134 @@ weight: 170
# Type-safe information extraction # Type-safe information extraction
Actix provides facility for type-safe request information extraction. By default, Actix-web provides a facility for type-safe request information access called *extractors*
actix provides several extractor implementations. (ie, `impl FromRequest`). By default, actix-web provides several extractor implementations.
# Accessing Extractors ## Extractors Within Handler Functions
How you access an Extractor depends on whether you are using a handler function An extractor can be accessed in a few different ways.
or a custom Handler type.
## Within Handler Functions Option 1 - passed as a parameter to a handler function:
An Extractor can be passed to a handler function as a function parameter {{< include-example example="extractors" file="main.rs" section="option-one" >}}
*or* accessed within the function by calling the ExtractorType::<...>::extract(req)
function.
```rust
// Option 1: passed as a parameter to a handler function Option 2 - accessed by calling `extract()` on the Extractor
fn index((params, info): (Path<(String, String,)>, Json<MyInfo>)) -> HttpResponse {
...
}
{{< include-example example="extractors" file="main.rs" section="option-two" >}}
// Option 2: accessed by calling extract() on the Extractor
use actix_web::FromRequest;
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.
```rust
struct MyHandler(String);
impl<S> Handler<S> for MyHandler {
type Result = HttpResponse;
/// Handle request
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
let params = Path::<(String, String)>::extract(req);
let info = Json::<MyInfo>::extract(req);
...
HttpResponse::Ok().into()
}
}
```
# Path # Path
[*Path*](../../actix-web/actix_web/struct.Path.html) provides information that can [*Path*][pathstruct] provides information that can be extracted from the Request's
be extracted from the Request's path. You can deserialize any variable path. You can deserialize any variable segment from the path.
segment from the path.
For instance, for resource that registered for the `/users/{userid}/{friend}` path For instance, for resource that registered for the `/users/{userid}/{friend}` path
two segments could be deserialized, `userid` and `friend`. These segments two segments could be deserialized, `userid` and `friend`. These segments could be
could be extracted into a `tuple`, i.e. `Path<(u32, String)>` or any structure extracted into a `tuple`, i.e. `Path<(u32, String)>` or any structure that implements
that implements the `Deserialize` trait from the *serde* crate. the `Deserialize` trait from the *serde* crate.
```rust {{< include-example example="extractors" file="path_one.rs" section="path-one" >}}
use actix_web::{App, Path, Result, http};
/// extract path info from "/users/{userid}/{friend}" url It is also possible to extract path information to a specific type that implements the
/// {userid} - - deserializes to a u32 `Deserialize` trait from *serde*. Here is an equivalent example that uses *serde*
/// {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! A handler function that uses extractors has to be registered using the
[*Route::with()*](../../actix-web/actix_web/dev/struct.Route.html#method.with) method.
It is also possible to extract path information to a specific type that
implements the `Deserialize` trait from *serde*. Here is an equivalent example that uses *serde*
instead of a *tuple* type. instead of a *tuple* type.
```rust {{< include-example example="extractors" file="path_two.rs" section="path-two" >}}
#[macro_use] extern crate serde_derive;
use actix_web::{App, Path, Result, http};
#[derive(Deserialize)] It is also possible to `get` or `query` the request for path parameters by name:
struct Info {
userid: u32,
friend: String,
}
/// extract path info using serde {{< include-example example="extractors" file="path_three.rs" section="path-three" >}}
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 # Query
Same can be done with the request's query. The [*Query*][querystruct] type provides extraction functionality for the request's
The [*Query*](../../actix-web/actix_web/struct.Query.html) query parameters. Underneath it uses *serde_urlencoded* crate.
type provides extraction functionality. Underneath it uses *serde_urlencoded* crate.
```rust {{< include-example example="extractors" file="query.rs" section="query" >}}
#[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
[*Json*](../../actix-web/actix_web/struct.Json.html) allows to deserialize [*Json*][jsonstruct] allows to deserialize a request body into a struct. To extract
a request body into a struct. To extract typed information from a request's body, typed information from a request's body, the type `T` must implement the `Deserialize`
the type `T` must implement the `Deserialize` trait from *serde*. trait from *serde*.
```rust {{< include-example example="extractors" file="json_one.rs" section="json-one" >}}
#[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 the extraction process. Json extractor Some extractors provide a way to configure the extraction process. Json extractor
[*JsonConfig*](../../actix-web/actix_web/dev/struct.JsonConfig.html) type for configuration. [*JsonConfig*][jsonconfig] type for configuration. To configure an extractor, pass it's
When you register a handler using `Route::with()`, it returns a configuration instance. In case of configuration object to the resource's `.data()` method. In case of a *Json* extractor
a *Json* extractor it returns a *JsonConfig*. You can configure the maximum size of the json it returns a *JsonConfig*. You can configure the maximum size of the json payload as
payload as well as a custom error handler function. well as a custom error handler function.
The following example limits the size of the payload to 4kb and uses a custom error handler. The following example limits the size of the payload to 4kb and uses a custom error handler.
```rust {{< include-example example="extractors" file="json_two.rs" section="json-two" >}}
#[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_config(index, |cfg| {
cfg.limit(4096) // <- change json extractor configuration
cfg.error_handler(|err, req| { // <- create custom error response
error::InternalError::from_response(
err, HttpResponse::Conflict().finish()).into()
})
});
});
}
```
# Form # Form
At the moment only url-encoded forms are supported. The url-encoded body At the moment only url-encoded forms are supported. The url-encoded body could be
could be extracted to a specific type. This type must implement extracted to a specific type. This type must implement the `Deserialize` trait from
the `Deserialize` trait from the *serde* crate. the *serde* crate.
[*FormConfig*](../../actix-web/actix_web/dev/struct.FormConfig.html) allows [*FormConfig*][formconfig] allows configuring the extraction process.
configuring the extraction process.
```rust {{< include-example example="extractors" file="form.rs" section="form" >}}
#[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 gets called only if the content type is *x-www-form-urlencoded*
/// and the 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 # Multiple extractors
Actix provides extractor implementations for tuples (up to 10 elements) Actix-web provides extractor implementations for tuples (up to 10 elements) whose
whose elements implement `FromRequest`. elements implement `FromRequest`.
For example we can use a path extractor and a query extractor at the same time. For example we can use a path extractor and a query extractor at the same time.
```rust {{< include-example example="extractors" file="multiple.rs" section="multi" >}}
#[macro_use] extern crate serde_derive;
use actix_web::{App, Query, Path, http};
#[derive(Deserialize)]
struct Info {
username: String,
}
fn index((path, query): (Path<(u32, String)>, Query<Info>)) -> String {
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 # Other
Actix also provides several other extractors: Actix-web also provides several other extractors:
* [*State*](../../actix-web/actix_web/struct.State.html) - If you need * [*Data*][datastruct] - If you need access to an application state.
access to an application state. This is similar to a `HttpRequest::state()`. * *HttpRequest* - *HttpRequest* itself is an extractor which returns self, in case you
* *HttpRequest* - *HttpRequest* itself is an extractor which returns self, need access to the request.
in case you need access to the request. * *String* - You can convert a request's payload to a *String*. [*Example*][stringexample]
* *String* - You can convert a request's payload to a *String*.
[*Example*](../../actix-web/actix_web/trait.FromRequest.html#example-1)
is available in doc strings. is available in doc strings.
* *bytes::Bytes* - You can convert a request's payload into *Bytes*. * *bytes::Bytes* - You can convert a request's payload into *Bytes*.
[*Example*](../../actix-web/actix_web/trait.FromRequest.html#example) [*Example*][bytesexample]
is available in doc strings. is available in doc strings.
* *Payload* - You can access a request's payload.
[*Example*][payloadexample]
# Async Data Access
Application state is accessible from the handler with the `web::Data` extractor;
however, state is accessible as a read-only reference. If you need mutable access to state,
it must be implemented.
> **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:
{{< include-example example="request-handlers" file="main.rs" section="data" >}}
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`.
{{< include-example example="request-handlers" file="handlers_arc.rs" section="arc" >}}
> Be careful with synchronization primitives like `Mutex` or `RwLock`. The `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][actix] actor system.
[pathstruct]: (https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html
[querystruct]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Query.html
[jsonstruct]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Json.html
[jsonconfig]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.JsonConfig.html
[formconfig]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.FormConfig.html
[datastruct]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Data.html
[stringexample]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html#example-2
[bytesexample]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html#example-4
[payloadexample]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Payload.html
[actix]: https://actix.github.io/actix/actix/

View File

@ -6,7 +6,7 @@ weight: 130
# Getting Started # Getting Started
Lets write our first actix web application! Lets write our first `actix-web` application!
## Hello, world! ## Hello, world!
@ -27,19 +27,16 @@ actix-web = "{{< actix-version "actix-web" >}}"
In order to implement a web server, we first need to create a request handler. 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 A request handler is a function that accepts zero or more parameters that can be
and returns a type that can be converted into `HttpResponse`: extracted from a request (ie, `impl FromRequest`) and returns a type that can be
converted into an `HttpResponse` (ie, `impl Responder`):
Filename: `src/main.rs`
{{< include-example example="getting-started" section="setup" >}} {{< include-example example="getting-started" section="setup" >}}
Next, create an `Application` instance and register the request handler with Next, create an `App` instance and register the request handler with the application's
the application's `resource` on a particular *HTTP method* and *path* and `route` on a *path* and with a particular *HTTP method*. After that, the application
after that, the application instance can be used with `HttpServer` to listen instance can be used with `HttpServer` to listen for incoming connections. The server
for incoming connections. The server accepts a function that should return an accepts a function that should return an application factory.
`HttpHandler` instance. For simplicity `server::new` could be used, this
function is shortcut for `HttpServer::new`:
{{< include-example example="getting-started" section="main" >}} {{< include-example example="getting-started" section="main" >}}
@ -48,4 +45,6 @@ Head over to ``http://localhost:8088/`` to see the results.
If you want, you can have an automatic reloading server during development If you want, you can have an automatic reloading server during development
that recompiles on demand. To see how this can be accomplished have a look that recompiles on demand. To see how this can be accomplished have a look
at the [autoreload pattern](../autoreload/). at the [autoreload pattern][autoload].
[autoload]: ../autoreload/

View File

@ -6,30 +6,29 @@ weight: 160
# Request Handlers # Request Handlers
A request handler can be any object that implements A request handler is a function that accepts zero or more parameters that can be extracted
[*Handler*](../../actix-web/actix_web/dev/trait.Handler.html) trait. from a request (ie, [*impl FromRequest*][implfromrequest]) and returns a type that can
be converted into an HttpResponse (ie, [*impl Responder*][implresponder]).
Request handling happens in two stages. First the handler object is called, Request handling happens in two stages. First the handler object is called, returning any
returning any object that implements the object that implements the [*Responder*][respondertrait] trait. Then, `respond_to()` is
[*Responder*](../../actix-web/actix_web/trait.Responder.html#foreign-impls) trait. called on the returned object, converting itself to a `HttpResponse` or `Error`.
Then, `respond_to()` is called on the returned object, converting itself to a `AsyncResult` or `Error`.
By default actix provides `Responder` implementations for some standard types, By default actix-web provides `Responder` implementations for some standard types,
such as `&'static str`, `String`, etc. such as `&'static str`, `String`, etc.
> For a complete list of implementations, check > For a complete list of implementations, check [*Responder documentation*][responderimpls].
> [*Responder documentation*](../../actix-web/actix_web/trait.Responder.html#foreign-impls).
Examples of valid handlers: Examples of valid handlers:
```rust ```rust
fn index(req: &HttpRequest) -> &'static str { fn index(_req: HttpRequest) -> &'static str {
"Hello world!" "Hello world!"
} }
``` ```
```rust ```rust
fn index(req: &HttpRequest) -> String { fn index(_req: HttpRequest) -> String {
"Hello world!".to_owned() "Hello world!".to_owned()
} }
``` ```
@ -38,255 +37,59 @@ You can also change the signature to return `impl Responder` which works well if
complex types are involved. complex types are involved.
```rust ```rust
fn index(req: &HttpRequest) -> impl Responder { fn index(_req: HttpRequest) -> impl Responder {
Bytes::from_static("Hello world!") Bytes::from_static("Hello world!")
} }
``` ```
```rust,ignore ```rust
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> { fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
... ...
} }
``` ```
*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 use interior mutably to access its own
> state. **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
use actix_web::{App, HttpRequest, HttpResponse, dev::Handler};
struct MyHandler(Cell<usize>);
impl<S> Handler<S> for MyHandler {
type Result = HttpResponse;
/// Handle request
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
let i = self.0.get();
self.0.set(i + 1);
HttpResponse::Ok().into()
}
}
fn main(){
server::new(|| App::new()
.resource("/", |r| r.h(MyHandler(Cell::new(0))))) //use r.h() to bind handler, not the r.f()
.bind("127.0.0.1:8080")
.unwrap()
.run();
}
```
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
use actix_web::{server, App, HttpRequest, HttpResponse, dev::Handler};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
struct MyHandler(Arc<AtomicUsize>);
impl<S> Handler<S> for MyHandler {
type Result = HttpResponse;
/// Handle request
fn handle(&self, req: &HttpRequest<S>) -> 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");
let _ = sys.run();
}
```
> Be careful with synchronization primitives like `Mutex` or `RwLock`. The `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 ## Response with custom type
To return a custom type directly from a handler function, the type needs to implement the `Responder` trait. 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: Let's create a response for a custom type that serializes to an `application/json` response:
```rust {{< include-example example="responder-trait" file="main.rs" section="responder-trait" >}}
# 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<S>(self, req: &HttpRequest<S>) -> Result<HttpResponse, Error> {
let body = serde_json::to_string(&self)?;
// Create response and set content type
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body))
}
}
fn index(req: &HttpRequest) -> impl Responder {
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");
let _ = sys.run();
}
```
## Async handlers ## Async handlers
There are two different types of async handlers. Response objects can be generated asynchronously 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/actix_web/trait.Responder.html) trait. or more precisely, any type that implements the [*Responder*][respondertrait] trait.
In this case, the handler must return a `Future` object that resolves to the *Responder* type, i.e: In this case, the handler must return a `Future` object that resolves to the *Responder* type, i.e:
```rust {{< include-example example="async-handlers" file="main.rs" section="async-responder" >}}
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
use futures::future::{Future, result};
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> { Or the response body can be generated asynchronously. In this case, body must implement
the stream trait `Stream<Item=Bytes, Error=Error>`, i.e:
result(Ok(HttpResponse::Ok() {{< include-example example="async-handlers" file="stream.rs" section="stream" >}}
.content_type("text/html")
.body(format!("Hello!"))))
.responder()
}
fn index2(req: &HttpRequest) -> Box<Future<Item=&'static str, Error=Error>> {
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<Item=Bytes, Error=Error>`, i.e:
```rust
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) 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`. It is possible to return a `Result` where the `Result::Item` type can be `Future`. In
In this example, the `index` handler can return an error immediately or return a this example, the `index` handler can return an error immediately or return a future
future that resolves to a `HttpResponse`. that resolves to a `HttpResponse`.
```rust {{< include-example example="async-handlers" file="async_stream.rs" section="async-stream" >}}
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
use futures::future::{Future, result};
fn index(req: &HttpRequest) -> Result<Box<Future<Item=HttpResponse, Error=Error>>, Error> {
if is_error() {
Err(error::ErrorBadRequest("bad request"))
} else {
Ok(Box::new(
result(Ok(HttpResponse::Ok()
.content_type("text/html")
.body(format!("Hello!"))))))
}
}
```
## Different return types (Either) ## Different return types (Either)
Sometimes, you need to return different types of responses. For example, Sometimes, you need to return different types of responses. For example, you can error
you can error check and return errors, return async responses, or any result that requires two different types. check and return errors, return async responses, or any result that requires two different types.
For this case, the [*Either*](../../actix-web/actix_web/enum.Either.html) type can be used. For this case, the [*Either*][either] type can be used. `Either` allows combining two
`Either` allows combining two different responder types into a single type. different responder types into a single type.
```rust {{< include-example example="either" file="main.rs" section="either" >}}
use futures::future::{Future, result};
use actix_web::{Either, Error, HttpResponse};
type RegisterResult = Either<HttpResponse, Box<Future<Item=HttpResponse, Error=Error>>>; [implfromrequest]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html
[implresponder]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html
fn index(req: &HttpRequest) -> RegisterResult { [respondertrait]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html
if is_a_variant() { // <- choose variant A [responderimpls]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html#foreign-impls
Either::A( [either]: https://docs.rs/actix-web/1.0.2/actix_web/enum.Either.html
HttpResponse::BadRequest().body("Bad data"))
} else {
Either::B( // <- variant B
result(Ok(HttpResponse::Ok()
.content_type("text/html")
.body(format!("Hello!")))).responder())
}
}
```

View File

@ -8,43 +8,28 @@ weight: 250
# Negotiation # Negotiation
*HTTP/2.0* protocol over tls without prior knowledge requires *HTTP/2.0* protocol over tls without prior knowledge requires [tls alpn][tlsalpn].
[tls alpn](https://tools.ietf.org/html/rfc7301).
> Currently, only `rust-openssl` has support. > Currently, only `rust-openssl` has support.
`alpn` negotiation requires enabling the feature. When enabled, `HttpServer` provides the `alpn` negotiation requires enabling the feature. When enabled, `HttpServer` provides the
[serve_tls](../../actix-web/actix_web/server/struct.HttpServer.html#method.serve_tls) method. [bind_ssl][bindssl] method.
```toml ```toml
[dependencies] [dependencies]
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["alpn"] } actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["ssl"] }
openssl = { version = "0.10", features = ["v110"] } openssl = { version = "0.10", features = ["v110"] }
``` ```
{{< include-example example="http2" file="main.rs" section="main" >}}
```rust Upgrades to *HTTP/2.0* schema described in [rfc section 3.2][rfcsection32] is not
use std::fs::File; supported. Starting *HTTP/2* with prior knowledge is supported for both clear text
use actix_web::*; connection and tls connection. [rfc section 3.4][rfcsection34].
use openssl::ssl::{SslMethod, SslAcceptor, SslFiletype};
fn main() { > Check out [examples/tls][examples] for a concrete example.
// 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( [rfcsection32]: https://http2.github.io/http2-spec/#rfc.section.3.2
|| App::new() [rfcsection34]: https://http2.github.io/http2-spec/#rfc.section.3.4
.resource("/index.html", |r| r.f(index))) [bindssl]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.bind_ssl
.bind("127.0.0.1:8080").unwrap(); [tlsalpn]: https://tools.ietf.org/html/rfc7301
.serve_ssl(builder).unwrap(); [examples]: https://github.com/actix/examples/tree/master/tls
}
```
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/examples/tree/master/tls)
> for a concrete example.

View File

@ -6,21 +6,19 @@ weight: 110
# Installing Rust # Installing Rust
Since `actix-web` is a Rust framework you will need Rust to get started with it. Since `actix-web` is a Rust framework you will need Rust to get started with it. If you
If you don't have it yet we recommend you use `rustup` to manage your Rust don't have it yet we recommend you use `rustup` to manage your Rust installation. The
installation. The [official rust [official rust guide][rustguide] has a wonderful section on getting started.
guide](https://doc.rust-lang.org/book/second-edition/ch01-01-installation.html)
has a wonderful section on getting started.
We currently require at least Rust 1.24 so make sure you run `rustup update` We currently require at least Rust {{< rust-version "actix-web" >}} so make sure you run
to have the latest and greatest Rust version available. In particular this `rustup update` to have the latest and greatest Rust version available. In particular
guide will assume that you actually run Rust 1.26 or later. this guide will assume that you actually run Rust {{< rust-version "actix-web" >}} or later.
# Installing `actix-web` # Installing `actix-web`
Thank's to Rust's `cargo` package manager you won't need to explicitly install Thanks to Rust's `cargo` package manager you won't need to explicitly install
`actix-web`. Just depend on it and you're ready to go. For the unlikely `actix-web`. Just depend on it and you're ready to go. For the unlikely
case that you want to use the development version of actix-web you can case that you want to use the development version of `actix-web` you can
depend on the git repository directly. depend on the git repository directly.
Release version: Release version:
@ -39,10 +37,9 @@ actix-web = { git = "https://github.com/actix/actix-web" }
# Diving In # Diving In
There are two paths you can take here. You can follow the guide along or if There are two paths you can take here. You can follow the guide along or if you are very
you are very impatient you might want to have a look at our impatient you might want to have a look at our [extensive example repository][exmaples]
[extensive example repository](https://github.com/actix/examples) and run the and run the included examples. Here for instance is how you run the included `basics`
included examples. Here for instance is how you run the included `basics`
example: example:
``` ```
@ -50,3 +47,6 @@ git clone https://github.com/actix/examples
cd examples/basics cd examples/basics
cargo run cargo run
``` ```
[rustguide]: https://doc.rust-lang.org/book/ch01-01-installation.html
[examples]: https://github.com/actix/examples

View File

@ -6,9 +6,9 @@ weight: 220
# Middleware # Middleware
Actix's middleware system allows us to add additional behavior to request/response processing. Actix-web's middleware system allows us to add additional behavior to request/response
Middleware can hook into an incoming request process, enabling us to modify requests processing. Middleware can hook into an incoming request process, enabling us to modify
as well as halt request processing to return a response early. requests as well as halt request processing to return a response early.
Middleware can also hook into response processing. Middleware can also hook into response processing.
@ -19,87 +19,38 @@ Typically, middleware is involved in the following actions:
* Modify application state * Modify application state
* Access external services (redis, logging, sessions) * Access external services (redis, logging, sessions)
Middleware is registered for each application and executed in same order as Middleware is registered for each `App`, `scope`, or `Resource` and executed in opposite
registration. In general, a *middleware* is a type that implements the order as registration. In general, a *middleware* is a type that implements the
[*Middleware trait*](../../actix-web/actix_web/middleware/trait.Middleware.html). [*Service trait*][servicetrait] and [*Transform trait*][transformtrait]. Each method in
Each method in this trait has a default implementation. Each method can return the traits has a default implementation. Each method can return a result immediately
a result immediately or a *future* object. or a *future* object.
The following demonstrates using middleware to add request and response headers: The following demonstrates creating a simple middleware:
```rust {{< include-example example="middleware" file="main.rs" section="simple" >}}
use http::{header, HttpTryFrom};
use actix_web::{App, HttpRequest, HttpResponse, Result};
use actix_web::middleware::{Middleware, Started, Response};
struct Headers; // <- Our middleware > Actix-web provides several useful middlewares, such as *logging*, *user sessions*,
> *compress*, etc.
/// Middleware implementation, middlewares are generic over application state,
/// so you can access state with `HttpRequest::state()` method.
impl<S> Middleware<S> 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: &HttpRequest<S>) -> Result<Started> {
Ok(Started::Done)
}
/// Method is called when handler returns response,
/// but before sending http message to peer.
fn response(&self, req: &HttpRequest<S>, mut resp: HttpResponse)
-> Result<Response>
{
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()
// Register middleware, this method can be called multiple times
.middleware(Headers)
.resource("/", |r| r.f(|_| HttpResponse::Ok()));
}
```
> Actix provides several useful middlewares, such as *logging*, *user sessions*, etc.
# Logging # Logging
Logging is implemented as a middleware. Logging is implemented as a middleware. It is common to register a logging middleware
It is common to register a logging middleware as the first middleware for the application. as the first middleware for the application. Logging middleware must be registered for
Logging middleware must be registered for each application. each application.
The `Logger` middleware uses the standard log crate to log information. You should enable logger 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/) for *actix_web* package to see access log ([env_logger][envlogger] or similar).
or similar).
## Usage ## Usage
Create `Logger` middleware with the specified `format`. Create `Logger` middleware with the specified `format`. Default `Logger` can be created
Default `Logger` can be created with `default` method, it uses the default format: with `default` method, it uses the default format:
```ignore ```ignore
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T %a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
``` ```
```rust {{< include-example example="middleware" file="logger.rs" section="logger" >}}
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: The following is an example of the default logging format:
@ -110,29 +61,18 @@ INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800]
## Format ## Format
`%%` The percent sign - `%%` The percent sign
- `%a` Remote IP-address (IP-address of proxy if using reverse proxy)
`%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
`%t` Time when the request was started to process - `%r` First line of request
- `%s` Response status code
`%P` The process ID of the child that serviced the request - `%b` Size of response in bytes, including HTTP headers
- `%T` Time taken to serve the request, in seconds with floating fraction in .06f format
`%r` First line of request - `%D` Time taken to serve the request, in milliseconds
- `%{FOO}i` request.headers['FOO']
`%s` Response status code - `%{FOO}o` response.headers['FOO']
- `%{FOO}e` os.environ['FOO']
`%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 ## Default headers
@ -140,79 +80,37 @@ To set default response headers, the `DefaultHeaders` middleware can be used. Th
*DefaultHeaders* middleware does not set the header if response headers already contain *DefaultHeaders* middleware does not set the header if response headers already contain
a specified header. a specified header.
```rust {{< include-example example="middleware" file="default_headers.rs" section="default-headers" >}}
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 ## User sessions
Actix provides a general solution for session management. The Actix-web provides a general solution for session management. The
[**SessionStorage**](../../actix-web/actix_web/middleware/session/struct.SessionStorage.html) middleware can be [**actix-session**][actixsession] middleware can be used with different backend types
used with different backend types to store session data in different backends. to store session data in different backends.
> By default, only cookie session backend is implemented. Other backend implementations > By default, only cookie session backend is implemented. Other backend implementations
> can be added. > can be added.
[**CookieSessionBackend**](../../actix-web/actix_web/middleware/session/struct.CookieSessionBackend.html) [**CookieSession**][cookiesession] uses cookies as session storage. `CookieSessionBackend`
uses cookies as session storage. `CookieSessionBackend` creates sessions which creates sessions which are limited to storing fewer than 4000 bytes of data, as the payload
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a must fit into a single cookie. An internal server error is generated if a session
single cookie. An internal server error is generated if a session contains more than 4000 bytes. contains more than 4000 bytes.
A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor. A cookie may have a security policy of *signed* or *private*. Each has a respective
`CookieSession` 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. 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. 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 In general, you create a `SessionStorage` middleware and initialize it with specific
`SessionStorage` middleware and initialize it with specific backend implementation, backend implementation, such as a `CookieSession`. To access session data the
such as a `CookieSessionBackend`. To access session data, [`Session`][requestsession] extractor must be used. This method returns a
[*HttpRequest::session()*](../../actix-web/actix_web/middleware/session/trait.RequestSession.html#tymethod.session) [*Session*][sessionobj] object, which allows us to get or set session data.
must be used. This method returns a
[*Session*](../../actix-web/actix_web/middleware/session/struct.Session.html) object, which allows us to get or set
session data.
```rust {{< include-example example="middleware" file="user_sessions.rs" section="user-session" >}}
use actix_web::{server, App, HttpRequest, Result};
use actix_web::middleware::session::{RequestSession, SessionStorage, CookieSessionBackend};
fn index(req: &HttpRequest) -> Result<&'static str> {
// access session data
if let Some(count) = req.session().get::<i32>("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(
CookieSessionBackend::signed(&[0; 32])
.secure(false)
)))
.bind("127.0.0.1:59880").unwrap()
.start();
let _ = sys.run();
}
```
# Error handlers # Error handlers
@ -223,26 +121,12 @@ for a specific status code. You can modify an existing response or create a comp
one. The error handler can return a response immediately or return a future that resolves one. The error handler can return a response immediately or return a future that resolves
into a response. into a response.
```rust {{< include-example example="middleware" file="errorhandler.rs" section="error-handler" >}}
use actix_web::{
App, HttpRequest, HttpResponse, Result,
http, middleware::Response, middleware::ErrorHandlers};
fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> { [sessionobj]: https://docs.rs/actix-session/0.1.1/actix_session/struct.Session.html
let mut builder = resp.into_builder(); [requestsession]: https://docs.rs/actix-session/0.1.1/actix_session/struct.Session.html
builder.header(http::header::CONTENT_TYPE, "application/json"); [cookiesession]: https://docs.rs/actix-session/0.1.1/actix_session/struct.CookieSession.html
Ok(Response::Done(builder.into())) [actixsession]: https://docs.rs/actix-session/0.1.1/actix_session/
} [envlogger]: https://docs.rs/env_logger/*/env_logger/
[servicetrait]: https://docs.rs/actix-web/1.0.2/actix_web/dev/trait.Service.html
fn main() { [transformtrait]: https://docs.rs/actix-web/1.0.2/actix_web/dev/trait.Transform.html
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();
}
```

View File

@ -6,153 +6,58 @@ weight: 200
# Content Encoding # Content Encoding
Actix automatically *decompresses* payloads. The following codecs are supported: Actix-web automatically *decompresses* payloads. The following codecs are supported:
* Brotli * Brotli
* Chunked
* Compress
* Gzip * Gzip
* Deflate * Deflate
* Identity * Identity
* Trailers
* EncodingExt
If request headers contain a `Content-Encoding` header, the request payload is decompressed If request headers contain a `Content-Encoding` header, the request payload is decompressed
according to the header value. Multiple codecs are not supported, according to the header value. Multiple codecs are not supported, i.e: `Content-Encoding: br, gzip`.
i.e: `Content-Encoding: br, gzip`.
# JSON Request # JSON Request
There are several options for json body deserialization. There are several options for json body deserialization.
The first option is to use *Json* extractor. First, you define a handler function The first option is to use *Json* extractor. First, you define a handler function
that accepts `Json<T>` as a parameter, then, you use the `.with()` method for registering that accepts `Json<T>` as a parameter, then, you use the `.to()` method for registering
this handler. It is also possible to accept arbitrary valid json object by this handler. It is also possible to accept arbitrary valid json object by
using `serde_json::Value` as a type `T`. using `serde_json::Value` as a type `T`.
```rust {{< include-example example="requests" file="main.rs" section="json-request" >}}
#[macro_use] extern crate serde_derive;
use actix_web::{App, Json, Result, http};
#[derive(Deserialize)]
struct Info {
username: String,
}
/// extract `Info` using serde
fn index(info: Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new().resource(
"/index.html",
|r| r.method(http::Method::POST).with(index)); // <- use `with` extractor
}
```
Another option is to use *HttpRequest::json()*. This method returns a
[*JsonBody*](../../actix-web/actix_web/dev/struct.JsonBody.html) object which resolves into
the deserialized value.
```rust
#[derive(Debug, Serialize, Deserialize)]
struct MyObj {
name: String,
number: i32,
}
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req.json().from_err()
.and_then(|val: MyObj| {
println!("model: {:?}", val);
Ok(HttpResponse::Ok().json(val)) // <- send response
})
.responder()
}
```
You may also manually load the payload into memory and then deserialize it. You may also manually load the payload into memory and then deserialize it.
In the following example, we will deserialize a *MyObj* struct. We need to load the request In the following example, we will deserialize a *MyObj* struct. We need to load the request
body first and then deserialize the json into an object. body first and then deserialize the json into an object.
```rust {{< include-example example="requests" file="manual.rs" section="json-manual" >}}
extern crate serde_json;
use futures::{Future, Stream};
#[derive(Serialize, Deserialize)] > A complete example for both options is available in [examples directory][examples].
struct MyObj {name: String, number: i32}
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
// `concat2` will asynchronously read each chunk of the request body and
// return a single, concatenated, chunk
req.concat2()
// `Future::from_err` acts like `?` in that it coerces the error type from
// the future into the final error type
.from_err()
// `Future::and_then` can be used to merge an asynchronous workflow with a
// synchronous workflow
.and_then(|body| {
let obj = serde_json::from_slice::<MyObj>(&body)?;
Ok(HttpResponse::Ok().json(obj))
})
.responder()
}
```
> A complete example for both options is available in
> [examples directory](https://github.com/actix/examples/tree/master/json/).
# Chunked transfer encoding # Chunked transfer encoding
Actix automatically decodes *chunked* encoding. `HttpRequest::payload()` already contains Actix automatically decodes *chunked* encoding. The [`web::Payload`][payloadextractor]
the decoded byte stream. If the request payload is compressed with one of the supported extractor already contains the decoded byte stream. If the request payload is compressed
compression codecs (br, gzip, deflate), then the byte stream is decompressed. with one of the supported compression codecs (br, gzip, deflate), then the byte stream
is decompressed.
# Multipart body # Multipart body
Actix provides multipart stream support. Actix-web provides multipart stream support with an external crate, [`actix-multipart`][multipartcrate].
[*Multipart*](../../actix-web/actix_web/multipart/struct.Multipart.html) is implemented as
a stream of multipart items. Each item can be a
[*Field*](../../actix-web/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: > A full example is available in the [examples directory][multipartexample].
```rust
use actix_web::*;
fn index(req: &HttpRequest) -> Box<Future<...>> {
// get multipart and iterate over multipart items
req.multipart()
.and_then(|item| {
match item {
multipart::MultipartItem::Field(field) => {
println!("==== FIELD ==== {:?} {:?}",
field.headers(),
field.content_type());
Either::A(
field.map(|chunk| {
println!("-- CHUNK: \n{}",
std::str::from_utf8(&chunk).unwrap());})
.fold((), |_, _| result(Ok(()))))
},
multipart::MultipartItem::Nested(mp) => {
Either::B(result(Ok(())))
}
}
})
}
```
> A full example is available in the
> [examples directory](https://github.com/actix/examples/tree/master/multipart/).
# Urlencoded body # Urlencoded body
Actix provides support for *application/x-www-form-urlencoded* encoded bodies. Actix-web provides support for *application/x-www-form-urlencoded* encoded bodies with
`HttpResponse::urlencoded()` returns a the [`web::Form`][formencoded] extractor which resolves to the deserialized instance. The
[*UrlEncoded*](../../actix-web/actix_web/dev/struct.UrlEncoded.html) future, which resolves type of the instance must implement the `Deserialize` trait from *serde*.
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: The *UrlEncoded* future can resolve into an error in several cases:
@ -161,27 +66,7 @@ The *UrlEncoded* future can resolve into an error in several cases:
* content-length is greater than 256k * content-length is greater than 256k
* payload terminates with error. * payload terminates with error.
```rust {{< include-example example="requests" file="urlencoded.rs" section="urlencoded" >}}
#[macro_use] extern crate serde_derive;
use actix_web::*;
use futures::future::{Future, ok};
#[derive(Deserialize)]
struct FormData {
username: String,
}
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req.urlencoded::<FormData>() // <- get UrlEncoded future
.from_err()
.and_then(|data| { // <- deserialized instance
println!("USERNAME: {:?}", data.username);
ok(HttpResponse::Ok().into())
})
.responder()
}
# fn main() {}
```
# Streaming request # Streaming request
@ -190,20 +75,13 @@ body payload.
In the following example, we read and print the request payload chunk by chunk: In the following example, we read and print the request payload chunk by chunk:
```rust {{< include-example example="requests" file="streaming.rs" section="streaming" >}}
use actix_web::*;
use futures::{Future, Stream};
[examples]: https://github.com/actix/examples/tree/master/json/
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> { [multipartstruct]: https://docs.rs/actix-multipart/0.1.2/actix_multipart/struct.Multipart.html
req [fieldstruct]: https://docs.rs/actix-multipart/0.1.2/actix_multipart/struct.Field.html
.payload() [multipartexample]: https://github.com/actix/examples/tree/master/multipart/
.from_err() [urlencoded]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.UrlEncoded.html
.fold((), |_, chunk| { [payloadextractor]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Payload.html
println!("Chunk: {:?}", chunk); [multipartcrate]: https://crates.io/crates/actix-multipart
result::<_, error::PayloadError>(Ok(())) [formencoded]:Jhttps://docs.rs/actix-web/1.0.2/actix_web/web/struct.Form.html
})
.map(|_| HttpResponse::Ok().finish())
.responder()
}
```

View File

@ -6,112 +6,62 @@ weight: 210
# Response # Response
A builder-like pattern is used to construct an instance of `HttpResponse`. A builder-like pattern is used to construct an instance of `HttpResponse`. `HttpResponse`
`HttpResponse` provides several methods that return a `HttpResponseBuilder` instance, provides several methods that return a `HttpResponseBuilder` instance, which implements
which implements various convenience methods for building responses. various convenience methods for building responses.
> Check the [documentation](../../actix-web/actix_web/dev/struct.HttpResponseBuilder.html) > Check the [documentation][responsebuilder] for type descriptions.
> for type descriptions.
The methods `.body`, `.finish`, and `.json` finalize response creation and The methods `.body`, `.finish`, and `.json` finalize response creation and return a
return a constructed *HttpResponse* instance. If this methods is called on the same constructed *HttpResponse* instance. If this methods is called on the same builder
builder instance multiple times, the builder will panic. instance multiple times, the builder will panic.
```rust {{< include-example example="responses" file="main.rs" section="builder" >}}
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")
}
```
# Content encoding # Content encoding
Actix automatically *compresses* payloads. The following codecs are supported: Actix-web can automatically *compresses* payloads with the [*Compress middleware*][compressmidddleware].
The following codecs are supported:
* Brotli * Brotli
* Gzip * Gzip
* Deflate * Deflate
* Identity * Identity
Response payload is compressed based on the *content_encoding* parameter. {{< include-example example="responses" file="compress.rs" section="compress" >}}
By default, `ContentEncoding::Auto` is used. If `ContentEncoding::Auto` is selected,
then the compression depends on the request's `Accept-Encoding` header. Response payload is compressed based on the *encoding* parameter from the
`middleware::BodyEncoding` trait. 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. > `ContentEncoding::Identity` can be used to disable compression.
> If another content encoding is selected, the compression is enforced for that codec. > If another content encoding is selected, the compression is enforced for that codec.
For example, to enable `brotli` use `ContentEncoding::Br`: For example, to enable `brotli` for a single handler use `ContentEncoding::Br`:
```rust {{< include-example example="responses" file="brotli.rs" section="brotli" >}}
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
fn index(req: HttpRequest) -> HttpResponse { or for the entire application:
HttpResponse::Ok()
.content_encoding(ContentEncoding::Br)
.body("data")
}
```
In this case we explicitly disable content compression {{< include-example example="responses" file="brotli_two.rs" section="brotli-two" >}}
by setting content encoding to a `Identity` value:
```rust In this case we explicitly disable content compression by setting content encoding to
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding}; an `Identity` value:
fn index(req: HttpRequest) -> HttpResponse { {{< include-example example="responses" file="identity.rs" section="identity" >}}
HttpResponse::Ok()
// v- disable compression
.content_encoding(ContentEncoding::Identity)
.body("data")
}
```
When dealing with an already compressed body (for example when serving assets), When dealing with an already compressed body (for example when serving assets),
set the content encoding to `Identity` to avoid compressing the already compressed set the content encoding to `Identity` to avoid compressing the already compressed
data and set the `content-encoding` header manually: data and set the `content-encoding` header manually:
```rust {{< include-example example="responses" file="identity_two.rs" section="identity-two" >}}
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
static HELLO_WORLD: &[u8] = &[
0x1f,0x8b,0x08,0x00,0xa2,0x30,0x10,0x5c,
0x00,0x03,0xcb,0x48,0xcd,0xc9,0xc9,0x57,
0x28,0xcf,0x2f,0xca,0x49,0xe1,0x02,0x00,
0x2d,0x3b,0x08,0xaf,0x0c,0x00,0x00,0x00
];
pub fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.content_encoding(ContentEncoding::Identity)
.header("content-encoding", "gzip")
.body(HELLO_WORLD)
}
```
Also it is possible to set default content encoding on application level, by Also it is possible to set default content encoding on application level, by
default `ContentEncoding::Auto` is used, which implies automatic content compression default `ContentEncoding::Auto` is used, which implies automatic content compression
negotiation. negotiation.
```rust {{< include-example example="responses" file="auto.rs" section="auto" >}}
use actix_web::{App, HttpRequest, HttpResponse, http::ContentEncoding};
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.body("data")
}
fn main() {
let app = App::new()
// v- disable compression for all routes
.default_encoding(ContentEncoding::Identity)
.resource("/index.html", |r| r.with(index));
}
```
# JSON Response # JSON Response
@ -119,26 +69,7 @@ The `Json` type allows to respond with well-formed JSON data: simply return a va
type Json<T> where `T` is the type of a structure to serialize into *JSON*. type Json<T> where `T` is the type of a structure to serialize into *JSON*.
The type `T` must implement the `Serialize` trait from *serde*. The type `T` must implement the `Serialize` trait from *serde*.
```rust {{< include-example example="responses" file="json_resp.rs" section="json-resp" >}}
# 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<Json<MyObj>> {
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 # Chunked transfer encoding
@ -149,14 +80,7 @@ is enabled automatically.
> Enabling chunked encoding for *HTTP/2.0* responses is forbidden. > Enabling chunked encoding for *HTTP/2.0* responses is forbidden.
```rust {{< include-example example="responses" file="chunked.rs" section="chunked" >}}
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
fn index(req: HttpRequest) -> HttpResponse { [responsebuilder]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.HttpResponseBuilder.html
HttpResponse::Ok() [compressmidddleware]: https://docs.rs/actix-web/1.0.2/actix_web/middleware/struct.Compress.html
.chunked()
.body(Body::Streaming(Box::new(once(Ok(Bytes::from_static(b"data"))))))
}
```

View File

@ -6,61 +6,33 @@ weight: 1020
# Sentry Crash Reporting # Sentry Crash Reporting
[Sentry](https://sentry.io/) is a crash reporting system that supports the [Sentry][sentrysite] is a crash reporting system that supports the failure crate which
failure crate which is the base of the actix error reporting. With a is the base of the actix error reporting. With a middleware it's possible to
middleware it's possible to automatically report server errors to Sentry. automatically report server errors to Sentry.
# Middleware # Middleware
This middleware captures any error in the server error range (500 - 599) This middleware captures any error in the server error range (500 - 599)
and sends the attached error to sentry with its stacktrace. and sends the attached error to sentry with its stacktrace.
To use the middleware the [sentry crate](https://crates.io/crates/sentry) needs to be To use the middleware the [sentry crate][sentrycrate] needs to be initialized and configured
initialized and configured and the [sentry-actix middleware](https://crates.io/crates/sentry-actix) and the [sentry-actix middleware][sentrymiddleware] needs to be added. Additionally it
needs to be added. Additionally it makes sense to also register the panic handler makes sense to also register the panic handler to be informed about hard panics.
to be informed about hard panics.
```rust {{< include-example example="sentry" file="main.rs" section="middleware" >}}
extern crate sentry;
extern crate sentry_actix;
use sentry_actix::SentryMiddleware;
use std::env;
fn main() {
sentry::init("SENTRY_DSN_GOES_HERE");
env::set_var("RUST_BACKTRACE", "1");
sentry::integrations::panic::register_panic_handler();
let mut app = App::with_state(state)
.middleware(SentryMiddleware::new())
// ...
}
```
# Reusing the Hub # Reusing the Hub
If you use this integration the default sentry hub (`Hub::current()`) is typically the wrong one. If you use this integration the default sentry hub (`Hub::current()`) is typically the wrong one.
To get the request specific one you need to use the `ActixWebHubExt` trait: To get the request specific one you need to use the `ActixWebHubExt` trait:
```rust {{< include-example example="sentry" file="main.rs" section="hub" >}}
use sentry::{Hub, Level};
use sentry_actix::ActixWebHubExt;
let hub = Hub::from_request(req);
hub.capture_message("Something is not well", Level::Warning);
```
The hub can also be made current for the duration of a call. Then `Hub::current()` works correctly The hub can also be made current for the duration of a call. Then `Hub::current()` works correctly
until the end of the `run` block. until the end of the `run` block.
```rust {{< include-example example="sentry" file="main.rs" section="hub2" >}}
use sentry::{Hub, Level};
use sentry_actix::ActixWebHubExt;
let hub = Hub::from_request(req); [sentrysite]: https://sentry.io/
Hub::run(hub, || { [sentrycrate]: https://crates.io/crates/sentry
sentry::capture_message("Something is not well", Level::Warning); [sentrymiddleware]: https://crates.io/crates/sentry-actix
});
```

View File

@ -6,25 +6,17 @@ weight: 150
# The HTTP Server # The HTTP Server
The [**HttpServer**](../../actix-web/actix_web/server/struct.HttpServer.html) type is responsible for The [**HttpServer**][httpserverstruct] type is responsible for serving http requests.
serving http requests.
`HttpServer` accepts an application factory as a parameter, and the `HttpServer` accepts an application factory as a parameter, and the application factory
application factory must have `Send` + `Sync` boundaries. More about that in the must have `Send` + `Sync` boundaries. More about that in the *multi-threading* section.
*multi-threading* section.
To bind to a specific socket address, To bind to a specific socket address, [`bind()`][bindmethod] must be used, and it may be
[`bind()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.bind) called multiple times. To bind ssl socket, [`bind_ssl()`][bindsslmethod] or
must be used, and it may be called multiple times. To bind ssl socket, [`bind_rustls()`][bindrusttls] should be used. To start the http server, use one of the
[`bind_ssl()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.bind_ssl) start methods.
or [`bind_tls()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.bind_tls)
should be used. To start the http server, use one of the start methods.
- use [`start()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.start) - use [`start()`] for a server
for a server
`HttpServer` is an actix actor. It must be initialized within a properly
configured actix system:
{{< include-example example="server" section="main" >}} {{< include-example example="server" section="main" >}}
@ -33,8 +25,8 @@ configured actix system:
> this server, send a `StopServer` message. > this server, send a `StopServer` message.
`HttpServer` is implemented as an actix actor. It is possible to communicate with the server `HttpServer` is implemented as an actix actor. It is possible to communicate with the server
via a messaging system. Start method, e.g. `start()`, returns the via a messaging system. Start method, e.g. `start()`, returns the address of the started
address of the started http server. It accepts several messages: http server. It accepts several messages:
- `PauseServer` - Pause accepting incoming connections - `PauseServer` - Pause accepting incoming connections
- `ResumeServer` - Resume accepting incoming connections - `ResumeServer` - Resume accepting incoming connections
@ -44,36 +36,32 @@ address of the started http server. It accepts several messages:
## Multi-threading ## Multi-threading
`HttpServer` automatically starts a number of http workers, by default `HttpServer` automatically starts a number of http workers, by default this number is
this number is equal to number of logical CPUs in the system. This number equal to number of logical CPUs in the system. This number can be overridden with the
can be overridden with the [`HttpServer::workers()`][workers] method.
[`HttpServer::workers()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.workers) method.
{{< include-example example="server" file="workers.rs" section="workers" >}} {{< include-example example="server" file="workers.rs" section="workers" >}}
The server creates a separate application instance for each created worker. Application state 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. is not shared between threads. To share state, `Arc` could be used.
> Application state does not need to be `Send` and `Sync`, > Application state does not need to be `Send` and `Sync`, but factories must be `Send` + `Sync`.
> but factories must be `Send` + `Sync`.
## SSL ## SSL
There are two features for ssl server: `tls` and `alpn`. The `tls` feature is There are two features for ssl server: `rust-tls` and `ssl`. The `tls` feature is
for `native-tls` integration and `alpn` is for `openssl`. for `rust-tls` integration and `ssl` is for `openssl`.
```toml ```toml
[dependencies] [dependencies]
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["alpn"] } actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["ssl"] }
``` ```
{{< include-example example="server" file="ssl.rs" section="ssl" >}} {{< include-example example="server" file="ssl.rs" section="ssl" >}}
> **Note**: the *HTTP/2.0* protocol requires > **Note**: the *HTTP/2.0* protocol requires [tls alpn][tlsalpn].
> [tls alpn](https://tools.ietf.org/html/rfc7301).
> At the moment, only `openssl` has `alpn` support. > At the moment, only `openssl` has `alpn` support.
> For a full example, check out > For a full example, check out [examples/tls][exampletls].
> [examples/tls](https://github.com/actix/examples/tree/master/tls).
To create the key.pem and cert.pem use the command. **Fill in your own subject** To create the key.pem and cert.pem use the command. **Fill in your own subject**
```bash ```bash
@ -95,39 +83,46 @@ Actix can wait for requests on a keep-alive connection.
- `None` or `KeepAlive::Disabled` - disable *keep alive*. - `None` or `KeepAlive::Disabled` - disable *keep alive*.
- `KeepAlive::Tcp(75)` - use `SO_KEEPALIVE` socket option. - `KeepAlive::Tcp(75)` - use `SO_KEEPALIVE` socket option.
{{< include-example example="server" file="ka.rs" section="ka" >}} {{< include-example example="server" file="keep_alive.rs" section="keep-alive" >}}
If the first option is selected, then *keep alive* state is If the first option is selected, then *keep alive* state is calculated based on the
calculated based on the response's *connection-type*. By default response's *connection-type*. By default `HttpResponse::connection_type` is not
`HttpResponse::connection_type` is not defined. In that case *keep alive* is defined. In that case *keep alive* is defined by the request's http version.
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*. > *keep alive* is **off** for *HTTP/1.0* and is **on** for *HTTP/1.1* and *HTTP/2.0*.
*Connection type* can be changed with `HttpResponseBuilder::connection_type()` method. *Connection type* can be changed with `HttpResponseBuilder::connection_type()` method.
{{< include-example example="server" file="ka_tp.rs" section="example" >}} {{< include-example example="server" file="keep_alive_tp.rs" section="example" >}}
## Graceful shutdown ## Graceful shutdown
`HttpServer` supports graceful shutdown. After receiving a stop signal, workers `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 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. timeout are force-dropped. By default the shutdown timeout is set to 30 seconds. You
You can change this parameter with the can change this parameter with the [`HttpServer::shutdown_timeout()`][shutdowntimeout]
[`HttpServer::shutdown_timeout()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.shutdown_timeout) method. method.
You can send a stop message to the server with the server address and specify if you want You can send a stop message to the server with the server address and specify if you want
graceful shutdown or not. The graceful shutdown or not. The [`start()`][startmethod] method returns address of the server.
[`start()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.start)
method returns address of the server.
`HttpServer` handles several OS signals. *CTRL-C* is available on all OSs, `HttpServer` handles several OS signals. *CTRL-C* is available on all OSs, other signals
other signals are available on unix systems. are available on unix systems.
- *SIGINT* - Force shutdown workers - *SIGINT* - Force shutdown workers
- *SIGTERM* - Graceful shutdown workers - *SIGTERM* - Graceful shutdown workers
- *SIGQUIT* - Force shutdown workers - *SIGQUIT* - Force shutdown workers
> It is possible to disable signal handling with > It is possible to disable signal handling with
> [`HttpServer::disable_signals()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.disable_signals) [`HttpServer::disable_signals()`][disablesignals] method.
> method.
[httpserverstruct]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html
[bindmethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.bind
[bindsslmethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.bind_ssl
[bindrusttls]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.bind_rustls
[startmethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.start
[workers]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.workers
[tlsalpn]: https://tools.ietf.org/html/rfc7301
[exampletls]: https://github.com/actix/examples/tree/master/tls
[shutdowntimeout]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.shutdown_timeout
[disablesignals]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.disable_signals

View File

@ -9,125 +9,40 @@ weight: 230
It is possible to serve static files with a custom path pattern and `NamedFile`. To It is possible to serve static files with a custom path pattern and `NamedFile`. To
match a path tail, we can use a `[.*]` regex. match a path tail, we can use a `[.*]` regex.
```rust {{< include-example example="static-files" file="main.rs" section="individual-file" >}}
extern crate actix_web;
use std::path::PathBuf;
use actix_web::{App, HttpRequest, Result, http::Method, fs::NamedFile};
fn index(req: &HttpRequest) -> Result<NamedFile> {
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 # Directory
To serve files from specific directories and sub-directories, `StaticFiles` can be used. To serve files from specific directories and sub-directories, `Files` can be used.
`StaticFiles` must be registered with an `App::handler()` method, otherwise `Files` must be registered with an `App::service()` method, otherwise
it will be unable to serve sub-paths. it will be unable to serve sub-paths.
```rust {{< include-example example="static-files" file="directory.rs" section="directory" >}}
extern crate actix_web;
use actix_web::{App, fs};
fn main() { By default files listing for sub-directories is disabled. Attempt to load directory
App::new() listing will return *404 Not Found* response. To enable files listing, use
.handler( [*Files::show_files_listing()*][showfileslisting]
"/static",
fs::StaticFiles::new(".")
.unwrap()
.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/actix_web/fs/struct.StaticFiles.html#method.show_files_listing)
method. method.
Instead of showing files listing for directory, it is possible to redirect Instead of showing files listing for directory, it is possible to redirect to a specific
to a specific index file. Use the index file. Use the [*Files::index_file()*][indexfile] method to configure this redirect.
[*StaticFiles::index_file()*](../../actix-web/actix_web/fs/struct.StaticFiles.html#method.index_file)
method to configure this redirect.
# Configuration # Configuration
Generic trait `StaticFileConfig` can be used to specify various options `NamedFiles` can specify various options for serving files:
for serving files:
- `content_disposition_map` - function to be used for mapping file's mime to corresponding `Content-Disposition` type - `set_content_dispostion` - function to be used for mapping file's mime to corresponding `Content-Disposition` type
- `is_use_etag` - specifies whether `ETag` shall be calculated and included in headers. - `use_etag` - specifies whether `ETag` shall be calculated and included in headers.
- `is_use_last_modifier` - specifies whether file modified timestamp should be used and added to `Last-Modified` header. - `use_last_modifier` - specifies whether file modified timestamp should be used and added to `Last-Modified` header.
- `is_method_allowed` - allows to control which HTTP methods are allowed to be used when accessing file.
All of the above methods are optional and provided with the best defaults. All of the above methods are optional and provided with the best defaults, But it is
But it is possible to customize any of them by implementing the trait onto own struct. possible to customize any of them.
```rust {{< include-example example="static-files" file="configuration.rs" section="config-one" >}}
extern crate mime;
extern crate actix_web;
use actix_web::{App, HttpRequest, Result, http::Method};
use actix_web::fs::{StaticFileConfig, NamedFile};
use actix_web::http::header::DispositionType;
use std::path::PathBuf; The Configuration can also be applied to directory service:
#[derive(Default)] {{< include-example example="static-files" file="configuration_two.rs" section="config-two" >}}
struct MyConfig;
impl StaticFileConfig for MyConfig { [showfileslisting]: https://docs.rs/actix-files/0.1.2/actix_files/struct.Files.html
fn content_disposition_map(typ: mime::Name) -> DispositionType { [indexfile]: https://docs.rs/actix-files/0.1.2/actix_files/struct.Files.html#method.index_file
DispositionType::Attachment
}
}
fn index(req: &HttpRequest) -> Result<NamedFile<MyConfig>> {
let path: PathBuf = req.match_info().query("tail")?;
Ok(NamedFile::open_with_config(path, MyConfig)?)
}
fn main() {
App::new()
.resource(r"/a/{tail:.*}", |r| r.method(Method::GET).f(index))
.finish();
}
```
The Configuration cal also be applied to directory service:
```rust
extern crate actix_web;
use actix_web::{App};
use actix_web::fs::{StaticFileConfig, StaticFiles};
#[derive(Default)]
struct MyConfig;
impl StaticFileConfig for MyConfig {
fn is_use_etag() -> bool {
false
}
fn is_use_last_modifier() -> bool {
false
}
}
fn main() {
App::new()
.handler(
"/static",
StaticFiles::with_config(".", MyConfig).unwrap()
.show_files_listing()
).finish();
}
```

View File

@ -6,237 +6,48 @@ weight: 210
# Testing # Testing
Every application should be well tested. Actix provides tools to perform unit and Every application should be well tested. Actix-web provides tools to perform unit and
integration tests. integration tests.
# Unit Tests # Unit Tests
For unit testing, actix provides a request builder type and a simple handler runner. For unit testing, actix-web provides a request builder type and a simple handler runner.
[*TestRequest*](../../actix-web/actix_web/test/struct.TestRequest.html) [*TestRequest*][testrequest] implements a builder-like pattern. You can generate a
implements a builder-like pattern. `HttpRequest` instance with `to_http_request()`, or you can
You can generate a `HttpRequest` instance with `finish()`, or you can run your handler with `block_on()`.
run your handler with `run()` or `run_async()`.
```rust {{< include-example example="testing" file="main.rs" section="unit-tests" >}}
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 # Integration tests
There are several methods for testing your application. Actix provides There a few methods for testing your application. Actix-web can be used
[*TestServer*](../../actix-web/actix_web/test/struct.TestServer.html), which can be used
to run the application with specific handlers in a real http server. to run the application with specific handlers in a real http server.
`TestServer::get()`, `TestServer::post()`, and `TestServer::client()` `TestRequest::get()`, `TestRequest::post()`, and `TestRequest::client()`
methods can be used to send requests to the test server. methods can be used to send requests to the test server.
A simple form `TestServer` can be configured to use a handler. To create a `Service` for testing, use the `test::init_serivce` method which accepts a
`TestServer::new` method accepts a configuration function, and the only argument regular `App` builder.
for this function is a *test application* instance.
> Check the [api documentation](../../actix-web/actix_web/test/struct.TestApp.html) > Check the [api documentation][actixdocs] for more information.
> for more information.
```rust {{< include-example example="testing" file="integration_one.rs" section="integration-one" >}}
use actix_web::{HttpRequest, HttpMessage};
use actix_web::test::TestServer;
use std::str;
fn index(req: &HttpRequest) -> &'static str { If you need more complex application configuration testing should be very similar to creating
"Hello world!" the normal application. For example, you may need to initialize application state. Create an
} `App` with a `data` method and attach state just like you would from a normal application.
fn main() {
// start new test server
let mut srv = TestServer::new(|app| app.handler(index));
let request = srv.get().finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success());
let bytes = srv.execute(response.body()).unwrap();
let body = str::from_utf8(&bytes).unwrap();
assert_eq!(body, "Hello world!");
}
```
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
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);
let request = srv.client(
http::Method::GET, "/test").finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success());
}
```
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
#[test]
fn test() {
let srv = TestServer::build_with_state(|| {
// 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}
})
// register server handlers and start test server
.start(|app| {
app.resource(
"/{username}/index.html", |r| r.with(
|p: Path<PParam>| format!("Welcome {}!", p.username)));
});
// now we can run our test code
);
```
{{< include-example example="testing" file="integration_two.rs" section="integration-two" >}}
# Stream response tests # Stream response tests
If you need to test stream it would be enough to convert a [*ClientResponse*](../../actix-web/actix_web/client/struct.ClientResponse.html) to future and execute it. If you need to test stream it would be enough to convert a [*ClientResponse*][clientresponse]
For example of testing [*Server Sent Events*](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events). to future and execute it.
For example of testing [*Server Sent Events*][serversentevents].
```rust {{< include-example example="testing" file="stream_response.rs" section="stream-response" >}}
extern crate bytes;
extern crate futures;
extern crate actix_web;
use bytes::Bytes; [serversentevents]: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
use futures::stream::poll_fn; [clientresponse]: https://docs.rs/actix-web/1.0.2/actix_web/client/struct.ClientResponse.html
use futures::{Async, Poll, Stream}; [actixdocs]: (https://docs.rs/actix-web/1.0.2/actix_web/test/index.html)
[testrequest]: https://docs.rs/actix-web/1.0.2/actix_web/error/trait.ResponseError.html#foreign-impls
use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse};
use actix_web::http::{ContentEncoding, StatusCode};
use actix_web::test::TestServer;
fn sse(_req: &HttpRequest) -> HttpResponse {
let mut counter = 5usize;
// yields `data: N` where N in [5; 1]
let server_events = poll_fn(move || -> Poll<Option<Bytes>, Error> {
if counter == 0 {
return Ok(Async::Ready(None));
}
let payload = format!("data: {}\n\n", counter);
counter -= 1;
Ok(Async::Ready(Some(Bytes::from(payload))))
});
HttpResponse::build(StatusCode::OK)
.content_encoding(ContentEncoding::Identity)
.content_type("text/event-stream")
.streaming(server_events)
}
fn main() {
// start new test server
let mut srv = TestServer::new(|app| app.handler(sse));
// request stream
let request = srv.get().finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success());
// convert ClientResponse to future, start read body and wait first chunk
let mut stream = response.payload();
loop {
match srv.execute(stream.into_future()) {
Ok((Some(bytes), remain)) => {
println!("{:?}", bytes);
stream = remain
}
Ok((None, _)) => break,
Err(_) => panic!(),
}
}
}
```
# 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
use actix::{Actor, StreamHandler};
use actix_web::*;
use futures::Stream;
struct Ws; // <- WebSocket actor
impl Actor for Ws {
type Context = ws::WebsocketContext<Self>;
}
impl StreamHandler<ws::Message, ws::ProtocolError> 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(
|app| app.handler(|req| ws::start(req, Ws)));
let (reader, mut writer) = srv.ws().unwrap();
writer.text("text");
let (item, reader) = srv.execute(reader.into_future()).unwrap();
assert_eq!(item, Some(ws::Message::Text("text".to_owned())));
}
```

View File

@ -6,112 +6,95 @@ weight: 190
# URL Dispatch # URL Dispatch
URL dispatch provides a simple way for mapping URLs to `Handler` code using a simple pattern 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, matching language. If one of the patterns matches the path information associated with a request,
a particular handler object is invoked. a particular handler object is invoked.
> A handler is a specific object that implements the > A request handler is a function that accepts zero or more parameters that can be extracted
> `Handler` trait, defined in your application, that receives the request and returns > from a request (ie, [*impl FromRequest*][implfromrequest]) and returns a type that can
> a response object. More information is available in the > be converted into an HttpResponse (ie, [*impl Responder*][implresponder]). More information
> [handler section](sec-4-handler.html). > is available in the [handler section][handlersection].
# Resource configuration # Resource configuration
Resource configuration is the act of adding a new resources to an application. Resource configuration is the act of adding a new resources to an application. A resource
A resource has a name, which acts as an identifier to be used for URL generation. has a name, which acts as an identifier to be used for URL generation. The name also
The name also allows developers to add routes to existing resources. allows developers to add routes to existing resources. A resource also has a pattern,
A resource also has a pattern, meant to match against the *PATH* portion of a *URL* (the portion following the scheme and meant to match against the *PATH* portion of a *URL* (the portion following the scheme and
port, e.g. */foo/bar* in the *URL* *http://localhost:8080/foo/bar?q=value*). port, e.g. */foo/bar* in the *URL* *http://localhost:8080/foo/bar?q=value*). It does not
It does not match against the *QUERY* portion (the portion that follows *?*, e.g. *q=value* in *http://localhost:8080/foo/bar?q=value*). match against the *QUERY* portion (the portion that follows *?*, e.g. *q=value*
in *http://localhost:8080/foo/bar?q=value*).
The [*App::route()*](../../actix-web/actix_web/struct.App.html#method.route) method provides The [*App::route()*][approute] method provides simple way of registering routes. This
simple way of registering routes. This method adds a single route to application method adds a single route to application routing table. This method accepts a *path pattern*,
routing table. This method accepts a *path pattern*,
*http method* and a handler function. `route()` method could be called multiple times *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. for the same path, in that case, multiple routes register for the same resource path.
{{< include-example example="url-dispatch" section="main" >}} {{< include-example example="url-dispatch" section="main" >}}
While *App::route()* provides simple way of registering routes, to access While *App::route()* provides simple way of registering routes, to access complete resource
complete resource configuration, a different method has to be used. configuration, a different method has to be used. The [*App::service()*][appservice] method
The [*App::resource()*](../../actix-web/actix_web/struct.App.html#method.resource) method adds a single [resource][webresource] to application routing table. This method accepts a
adds a single resource to application routing table. This method accepts a *path pattern* *path pattern*, guards, and one or more routes.
and a resource configuration function.
{{< include-example example="url-dispatch" file="resource.rs" section="resource" >}} {{< include-example example="url-dispatch" file="resource.rs" section="resource" >}}
The *Configuration function* has the following type:
```rust
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 If a resource does not contain any route or does not have any matching routes, it
returns *NOT FOUND* http response. returns *NOT FOUND* http response.
## Configuring a Route ## Configuring a Route
Resource contains a set of routes. Each route in turn has a set of predicates and a handler. Resource contains a set of routes. Each route in turn has a set of `guards` and a handler.
New routes can be created with `Resource::route()` method which returns a reference 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 to new *Route* instance. By default the *route* does not contain any guards, so matches
all requests and the default handler is `HttpNotFound`. all requests and the default handler is `HttpNotFound`.
The application routes incoming requests based on route criteria which are defined during 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 resource registration and route registration. Resource matches all routes it contains in
the order the routes were registered via `Resource::route()`. the order the routes were registered via `Resource::route()`.
> A *Route* can contain any number of *predicates* but only one handler. > A *Route* can contain any number of *guards* but only one handler.
{{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}} {{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}}
In this example, `HttpResponse::Ok()` is returned for *GET* requests. In this example, `HttpResponse::Ok()` is returned for *GET* requests if the request
If a request contains `Content-Type` header, the value of this header is *text/plain*, contains `Content-Type` header, the value of this header is *text/plain*, and path
and path equals to `/path`, Resource calls handle of the first matching route. equals to `/path`.
If a resource can not match any route, a "NOT FOUND" response is returned. If a resource can not match any route, a "NOT FOUND" response is returned.
[*ResourceHandler::route()*](../../actix-web/actix_web/dev/struct.ResourceHandler.html#method.route) returns a [*ResourceHandler::route()*][resourcehandler] returns a [*Route*][route] object. Route
[*Route*](../../actix-web/actix_web/dev/struct.Route.html) object. Route can be configured with a can be configured with a builder-like pattern. Following configuration methods are available:
builder-like pattern. Following configuration methods are available:
* [*Route::filter()*](../../actix-web/actix_web/dev/struct.Route.html#method.filter) * [*Route::guard()*][routeguard] registers a new guard. Any number of guards can be
registers a new predicate. Any number of predicates can be registered for each route. registered for each route.
* [*Route::f()*](../../actix-web/actix_web/dev/struct.Route.html#method.f) registers * [*Route::method()*][routemethod] registers a method guard. Any number of guards can be
handler function for this route. Only one handler can be registered. registered for each route.
Usually handler registration * [*Route::to()*][routeto] registers handler function for this route. Only one handler
is the last config operation. Handler function can be a function or closure can be registered. Usually handler registration is the last config operation.
and has the type * [*Route::to_async()*][routetoasync] registers an async handler function for this route.
`Fn(&HttpRequest<S>) -> R + 'static` Only one handler can be registered. Handler registration is the last config operation.
* [*Route::h()*](../../actix-web/actix_web/dev/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/actix_web/dev/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<S>) -> Future<Item = HttpResponse, Error = Error> + 'static`
# Route matching # Route matching
The main purpose of route configuration is to match (or not match) the request's `path` 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. 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, The way that *actix-web* does this is very simple. When a request enters the system,
for each resource configuration declaration present in the system, actix checks 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 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 routes were declared via `App::service()` method. If resource can not be found,
the *default resource* is used as the matched resource. the *default resource* is used as the matched resource.
When a route configuration is declared, it may contain route predicate arguments. All route When a route configuration is declared, it may contain route guard arguments. All route
predicates associated with a route declaration must be `true` for the route configuration to guards 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 be used for a given request during a check. If any guard in the set of route guard
arguments provided to a route configuration returns `false` during a check, that route is 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. 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 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. the route is invoked. If no route matches after all route patterns are exhausted, a
*NOT FOUND* response get returned.
# Resource pattern syntax # Resource pattern syntax
@ -269,18 +252,12 @@ A *scoped* path can contain variable path segments as resources. Consistent with
unscoped paths. unscoped paths.
You can get variable path segments from `HttpRequest::match_info()`. You can get variable path segments from `HttpRequest::match_info()`.
`Path` extractor also is able to extract scope level variable segments. [`Path` extractor][pathextractor] also is able to extract scope level variable segments.
# Match information # Match information
All values representing matched path segments are available in All values representing matched path segments are available in [`HttpRequest::match_info`][matchinfo].
[`HttpRequest::match_info`](../actix_web/struct.HttpRequest.html#method.match_info). Specific values can be retrieved with [`Path::get()`][pathget].
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.:
{{< include-example example="url-dispatch" file="minfo.rs" section="minfo" >}} {{< include-example example="url-dispatch" file="minfo.rs" section="minfo" >}}
@ -304,18 +281,13 @@ safe to interpolate within, or use as a suffix of, a path without additional che
{{< include-example example="url-dispatch" file="pbuf.rs" section="pbuf" >}} {{< include-example example="url-dispatch" file="pbuf.rs" section="pbuf" >}}
List of `FromParam` implementations can be found in
[api docs](../../actix-web/actix_web/dev/trait.FromParam.html#foreign-impls)
## Path information extractor ## Path information extractor
Actix provides functionality for type safe path information extraction. Actix provides functionality for type safe path information extraction. [*Path*][pathstruct]
[*Path*](../../actix-web/actix_web/struct.Path.html) extracts information, destination type extracts information, destination type could be defined in several different forms. Simplest
could be defined in several different forms. Simplest approach is to use approach is to use `tuple` type. Each element in tuple must correpond to one element from
`tuple` type. Each element in tuple must correpond to one element from
path pattern. i.e. you can match path pattern `/{id}/{username}/` against path pattern. i.e. you can match path pattern `/{id}/{username}/` against
`Path<(u32, String)>` type, but `Path<(String, String, String)>` type will `Path<(u32, String)>` type, but `Path<(String, String, String)>` type will always fail.
always fail.
{{< include-example example="url-dispatch" file="path.rs" section="path" >}} {{< include-example example="url-dispatch" file="path.rs" section="path" >}}
@ -324,21 +296,19 @@ this struct must implement *serde's *`Deserialize` trait.
{{< include-example example="url-dispatch" file="path2.rs" section="path" >}} {{< include-example example="url-dispatch" file="path2.rs" section="path" >}}
[*Query*](../../actix-web/actix_web/struct.Query.html) provides similar [*Query*][query] provides similar functionality for request query parameters.
functionality for request query parameters.
# Generating resource URLs # Generating resource URLs
Use the [*HttpRequest.url_for()*](../../actix-web/actix_web/struct.HttpRequest.html#method.url_for) Use the [*HttpRequest.url_for()*][urlfor] method to generate URLs based on resource
method to generate URLs based on resource patterns. For example, if you've configured a patterns. For example, if you've configured a resource with the name "foo" and the
resource with the name "foo" and the pattern "{a}/{b}/{c}", you might do this: pattern "{a}/{b}/{c}", you might do this:
{{< include-example example="url-dispatch" file="urls.rs" section="url" >}} {{< include-example example="url-dispatch" file="urls.rs" section="url" >}}
This would return something like the string *http://example.com/test/1/2/3* (at least if 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). the current protocol and hostname implied http://example.com). `url_for()` method
`url_for()` method returns [*Url object*](https://docs.rs/url/1.7.0/url/struct.Url.html) so you returns [*Url object*][urlobj] so you can modify this url (add query parameters, anchor, etc).
can modify this url (add query parameters, anchor, etc).
`url_for()` could be called only for *named* resources otherwise error get returned. `url_for()` could be called only for *named* resources otherwise error get returned.
# External resources # External resources
@ -355,22 +325,14 @@ By normalizing it means:
* To add a trailing slash to the path. * To add a trailing slash to the path.
* To replace multiple slashes with one. * To replace multiple slashes with one.
The handler returns as soon as it finds a path that resolves The handler returns as soon as it finds a path that resolves correctly. The order of
correctly. The order of normalization conditions, if all are enabled, is 1) merge, 2) both merge and append normalization conditions, if all are enabled, is 1) merge, 2) both merge and append and
and 3) append. If the path resolves with 3) append. If the path resolves with at least one of those conditions, it will redirect
at least one of those conditions, it will redirect to the new path. 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*.
{{< include-example example="url-dispatch" file="norm.rs" section="norm" >}} {{< include-example example="url-dispatch" file="norm.rs" section="norm" >}}
In this example `/resource`, `//resource///` will be redirected to `/resource/`. In this example `//resource///` will be redirected to `/resource/`.
In this example, the path normalization handler is registered for all methods, 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 but you should not rely on this mechanism to redirect *POST* requests. The redirect of the
@ -383,68 +345,88 @@ It is possible to register path normalization only for *GET* requests only:
## Using an Application Prefix to Compose Applications ## Using an Application Prefix to Compose Applications
The `App::prefix()` method allows to set a specific application prefix. The `web::scope()` method allows to set a specific application scope. This scope represents
This prefix represents a resource prefix that will be prepended to all resource patterns added a resource prefix that will be prepended to all resource patterns added by the resource
by the resource configuration. This can be used to help mount a set of routes at a different configuration. This can be used to help mount a set of routes at a different location
location than the included callable's author intended while still maintaining the same than the included callable's author intended while still maintaining the same resource names.
resource names.
For example: For example:
{{< include-example example="url-dispatch" file="prefix.rs" section="prefix" >}} {{< include-example example="url-dispatch" file="scope.rs" section="scope" >}}
In the above example, the *show_users* route will have an effective route pattern of 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 */users/show* instead of */show* because the application's scope will be prepended
to the pattern. The route will then only match if the URL path is */users/show*, 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, and when the `HttpRequest.url_for()` function is called with the route name show_users,
it will generate a URL with that same path. it will generate a URL with that same path.
# Custom route predicates # Custom route guard
You can think of a predicate as a simple function that accepts a *request* object reference You can think of a guard as a simple function that accepts a *request* object reference
and returns *true* or *false*. Formally, a predicate is any object that implements the and returns *true* or *false*. Formally, a guard is any object that implements the
[`Predicate`](../actix_web/pred/trait.Predicate.html) trait. Actix provides [`Guard`][guardtrait] trait. Actix provides several predicates, you can check
several predicates, you can check [functions section][guardfuncs] of api docs.
[functions section](../../actix-web/actix_web/pred/index.html#functions) of api docs.
Here is a simple predicate that check that a request contains a specific *header*: Here is a simple guard that check that a request contains a specific *header*:
{{< include-example example="url-dispatch" file="pred.rs" section="pred" >}} {{< include-example example="url-dispatch" file="guard.rs" section="guard" >}}
In this example, *index* handler will be called only if request contains *CONTENT-TYPE* header. 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()`. Guards can not access or modify the request object, but it is possible to store extra
Also predicates can store extra information in information in [request extensions][requestextensions].
[request extensions](../../actix-web/actix_web/struct.HttpRequest.html#method.extensions).
## Modifying predicate values ## Modifying guard values
You can invert the meaning of any predicate value by wrapping it in a `Not` predicate. 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 For example, if you want to return "METHOD NOT ALLOWED" response for all methods
except "GET": except "GET":
{{< include-example example="url-dispatch" file="pred2.rs" section="pred" >}} {{< include-example example="url-dispatch" file="guard2.rs" section="guard2" >}}
The `Any` predicate accepts a list of predicates and matches if any of the supplied The `Any` guard accepts a list of guards and matches if any of the supplied
predicates match. i.e: guards match. i.e:
```rust ```rust
pred::Any(pred::Get()).or(pred::Post()) guard::Any(guard::Get()).or(guard::Post())
``` ```
The `All` predicate accepts a list of predicates and matches if all of the supplied The `All` guard accepts a list of guard and matches if all of the supplied
predicates match. i.e: guards match. i.e:
```rust ```rust
pred::All(pred::Get()).and(pred::Header("content-type", "plain/text")) guard::All(guard::Get()).and(guard::Header("content-type", "plain/text"))
``` ```
# Changing the default Not Found response # 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 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*. 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()`. It is possible to override the *NOT FOUND* response with `App::default_service()`.
This method accepts a *configuration function* same as normal resource configuration This method accepts a *configuration function* same as normal resource configuration
with `App::resource()` method. with `App::service()` method.
{{< include-example example="url-dispatch" file="dhandler.rs" section="default" >}} {{< include-example example="url-dispatch" file="dhandler.rs" section="default" >}}
[handlersection]: ../handlers/
[approute]: https://docs.rs/actix-web/1.0.2/actix_web/struct.App.html#method.route
[appservice]: https://docs.rs/actix-web/1.0.2/actix_web/struct.App.html?search=#method.service
[webresource]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Resource.html
[resourcehandler]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Resource.html#method.route
[route]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html
[routeguard]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.guard
[routemethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.method
[routeto]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.to
[routetoasync]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.to_async
[matchinfo]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.match_info
[pathget]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html#method.get
[pathstruct]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html
[query]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Query.html
[urlfor]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.url_for
[urlobj]: https://docs.rs/url/1.7.2/url/struct.Url.html
[guardtrait]: https://docs.rs/actix-web/1.0.2/actix_web/guard/trait.Guard.html
[guardfuncs]: https://docs.rs/actix-web/1.0.2/actix_web/guard/index.html#functions
[requestextensions]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.extensions
[implfromrequest]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html
[implresponder]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html
[pathextractor]: ../extractors

View File

@ -4,47 +4,21 @@ menu: docs_proto
weight: 240 weight: 240
--- ---
Actix supports WebSockets out-of-the-box. It is possible to convert a request's `Payload` Actix-web supports WebSockets with the `actix-web-actors` crate. It is possible to convert a
to a stream of [*ws::Message*](../../actix-web/actix_web/ws/enum.Message.html) with request's `Payload` to a stream of [*ws::Message*][message] with a [*web::Payload*][payload]
a [*ws::WsStream*](../../actix-web/actix_web/ws/struct.WsStream.html) and then use stream and then use stream combinators to handle actual messages, but it is simpler to handle
combinators to handle actual messages, but it is simpler to handle websocket communications websocket communications with an http actor.
with an http actor.
The following is an example of a simple websocket echo server: The following is an example of a simple websocket echo server:
```rust {{< include-example example="websockets" file="main.rs" section="websockets" >}}
use actix::*;
use actix_web::*;
/// Define http actor > A simple websocket echo server example is available in the [examples directory][examples].
struct Ws;
impl Actor for Ws {
type Context = ws::WebsocketContext<Self>;
}
/// Handler for ws::Message message
impl StreamHandler<ws::Message, ws::ProtocolError> 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)))
.finish();
}
```
> A simple websocket echo server example is available in the
> [examples directory](https://github.com/actix/examples/tree/master/websocket/).
> An example chat server with the ability to chat over a websocket or tcp connection > 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/examples/tree/master/websocket-chat/) > is available in [websocket-chat directory][chat]
[message]: https://docs.rs/actix-web-actors/1.0.0/actix_web_actors/ws/enum.Message.html
[payload]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Payload.html
[examples]: https://github.com/actix/examples/tree/master/websocket/
[chat]: https://github.com/actix/examples/tree/master/websocket-chat/

View File

@ -7,14 +7,14 @@ weight: 100
# Actix is Multiple Things # Actix is Multiple Things
Actix is a few things. The base of it is a powerful actor system for Rust on Actix is a few things. The base of it is a powerful actor system for Rust on
top of which the `actix-web` system is built. This is what you are most likely top of which the `actix-web` system was originally built. This is what you are most
going to work with. What `actix-web` gives you is a fun and very fast web likely going to work with. What `actix-web` gives you is a fun and very fast web
development framework. development framework.
We call `actix-web` a small and pragmatic framework. For all intents and purposes We call `actix-web` a small and pragmatic framework. For all intents and purposes
it's a microframework with a few twists. If you are already a Rust programmer it's a microframework with a few twists. If you are already a Rust programmer
you will probably find yourself at home quickly, but even if you are coming from you will probably find yourself at home quickly, but even if you are coming from
another programming language you should find actix-web easy to pick up. another programming language you should find `actix-web` easy to pick up.
An application developed with `actix-web` will expose an HTTP server contained An application developed with `actix-web` will expose an HTTP server contained
within a native executable. You can either put this behind another HTTP server like within a native executable. You can either put this behind another HTTP server like
@ -23,5 +23,5 @@ server `actix-web` is powerful enough to provide HTTP 1 and HTTP 2 support as
well as SSL/TLS. This makes it useful for building small services ready for well as SSL/TLS. This makes it useful for building small services ready for
distribution. distribution.
Most importantly: `actix-web` runs on Rust 1.26 or later and it works with Most importantly: `actix-web` runs on Rust {{< rust-version "actix-web" >}} or later
stable releases. and it works with stable releases.

View File

@ -1,7 +1,30 @@
[workspace] [workspace]
members = [ members = [
"application", "application",
"easy-form-handling",
"flexible-responders",
"getting-started", "getting-started",
"main-example",
"powerful-extractors",
"request-routing",
"server", "server",
"url-dispatch", "url-dispatch",
"responder-trait",
"either",
"extractors",
"autoreload",
"errors",
"requests",
"responses",
"middleware",
"static-files",
"http2",
"testing",
"async-handlers",
"websockets",
"request-handlers",
]
exclude = [
"og_databases",
"sentry",
] ]

View File

@ -1,7 +1,8 @@
[package] [package]
name = "application" name = "application"
version = "0.7.0" version = "1.0.0"
edition = "2018"
workspace = "../" workspace = "../"
[dependencies] [dependencies]
actix-web = "0.7" actix-web = "1.0"

View File

@ -0,0 +1,14 @@
// <setup>
use actix_web::{web, App, Responder};
fn index() -> impl Responder {
"Hello world!"
}
#[rustfmt::skip]
pub fn main() {
App::new().service(
web::scope("/app")
.route("/index.html", web::get().to(index)));
}
// </setup>

View File

@ -0,0 +1,25 @@
use actix_web::{web, App, HttpResponse, HttpServer};
// <combine>
struct State1;
struct State2;
#[rustfmt::skip]
pub fn main() {
HttpServer::new(|| {
App::new()
.service(
web::scope("/app1")
.data(State1)
.route("/", web::to(|| HttpResponse::Ok())))
.service(
web::scope("/app2")
.data(State2)
.route("/", web::to(|| HttpResponse::Ok())))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </combine>

View File

@ -0,0 +1,34 @@
// <config>
use actix_web::{web, App, HttpResponse, HttpServer};
// this function could be located in different module
fn scoped_config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::resource("/test")
.route(web::get().to(|| HttpResponse::Ok().body("test")))
.route(web::head().to(|| HttpResponse::MethodNotAllowed())),
);
}
// this function could be located in different module
fn config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::resource("/app")
.route(web::get().to(|| HttpResponse::Ok().body("app")))
.route(web::head().to(|| HttpResponse::MethodNotAllowed())),
);
}
pub fn main() {
HttpServer::new(|| {
App::new()
.configure(config)
.service(web::scope("/api").configure(scoped_config))
.route("/", web::get().to(|| HttpResponse::Ok().body("/")))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </config>

View File

@ -1,42 +1,22 @@
#![allow(unused)] use actix_web::{web, App, HttpResponse};
extern crate actix_web;
use actix_web::{http::Method, server, App, HttpRequest, HttpResponse, Responder};
mod state; pub mod app;
mod vh; pub mod combine;
pub mod config;
fn make_app() { pub mod scope;
// <make_app> pub mod state;
fn index(req: &HttpRequest) -> impl Responder { pub mod vh;
"Hello world!"
}
let app = App::new()
.prefix("/app")
.resource("/index.html", |r| r.method(Method::GET).f(index))
.finish()
// </make_app>
;
}
fn run_server() {
// <run_server>
let server = 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())),
]
});
// </run_server>
}
#[rustfmt::skip]
// <multi>
fn main() { fn main() {
make_app(); App::new()
run_server(); .service(
state::test(); web::scope("/app1")
.route("/", web::to(|| HttpResponse::Ok())))
.service(
web::scope("/app2")
.route("/", web::to(|| HttpResponse::Ok())))
.route("/", web::to(|| HttpResponse::Ok()));
} }
// </multi>

View File

@ -0,0 +1,15 @@
use actix_web::{web, App, HttpRequest, Responder};
fn show_users(_req: HttpRequest) -> impl Responder {
unimplemented!()
}
#[rustfmt::skip]
// <scope>
pub fn main() {
App::new()
.service(
web::scope("/users")
.route("/show", web::get().to(show_users)));
}
// </scope>

View File

@ -1,71 +1,42 @@
// <setup> // <setup>
use actix_web::{http, App, HttpRequest}; use actix_web::{web, App, HttpServer};
use std::cell::Cell; use std::cell::Cell;
// This struct represents state // This struct represents state
struct AppState { struct AppState {
counter: Cell<usize>, counter: Cell<i32>,
} }
fn index(req: &HttpRequest<AppState>) -> String { fn index(data: web::Data<AppState>) -> String {
let count = req.state().counter.get() + 1; // <- get count let count = data.counter.get() + 1; // <- get count
req.state().counter.set(count); // <- store new count in state data.counter.set(count); // <- store new count in state
format!("Request number: {}", count) // <- response with count format!("Request number: {}", count) // <- response with count
} }
// </setup> // </setup>
fn make_app() {
// <make_app> // <make_app>
App::with_state(AppState { counter: Cell::new(0) }) fn _main() {
.resource("/", |r| r.method(http::Method::GET).f(index)) App::new()
.finish() .data(AppState {
// </make_app> counter: Cell::new(0),
; })
.route("/", web::get().to(index));
} }
// </make_app>
fn start_app() {
// <start_app> // <start_app>
server::new(|| { pub fn main() {
App::with_state(AppState { counter: Cell::new(0) }) HttpServer::new(|| {
.resource("/", |r| r.method(http::Method::GET).f(index)) App::new()
}).bind("127.0.0.1:8080") .data(AppState {
counter: Cell::new(0),
})
.route("/", web::get().to(index))
})
.bind("127.0.0.1:8088")
.unwrap() .unwrap()
.run() .run()
.unwrap();
}
// </start_app> // </start_app>
;
}
use actix_web::{server, HttpResponse};
use std::thread;
fn combine() {
thread::spawn(|| {
// <combine>
struct State1;
struct State2;
fn main() {
server::new(|| {
vec![
App::with_state(State1)
.prefix("/app1")
.resource("/", |r| r.f(|r| HttpResponse::Ok()))
.boxed(),
App::with_state(State2)
.prefix("/app2")
.resource("/", |r| r.f(|r| HttpResponse::Ok()))
.boxed(),
]
}).bind("127.0.0.1:8080")
.unwrap()
.run()
}
// </combine>
});
}
pub fn test() {
make_app();
combine();
}

View File

@ -1,20 +1,24 @@
#![allow(unused)] use actix_web::{guard, web, App, HttpResponse, HttpServer};
use actix_web::{http::Method, pred, server, App, HttpRequest, HttpResponse, Responder};
// <vh> // <vh>
fn main() { pub fn main() {
let server = server::new(|| { HttpServer::new(|| {
vec![ App::new()
App::new() .service(
.filter(pred::Host("www.rust-lang.org")) web::scope("/")
.resource("/", |r| r.f(|r| HttpResponse::Ok())), .guard(guard::Header("Host", "www.rust-lang.org"))
App::new() .route("", web::to(|| HttpResponse::Ok().body("www"))),
.filter(pred::Host("users.rust-lang.org")) )
.resource("/", |r| r.f(|r| HttpResponse::Ok())), .service(
App::new().resource("/", |r| r.f(|r| HttpResponse::Ok())), web::scope("/")
] .guard(guard::Header("Host", "users.rust-lang.org"))
}); .route("", web::to(|| HttpResponse::Ok().body("user"))),
)
server.run(); .route("/", web::to(|| HttpResponse::Ok()))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </vh> // </vh>

View File

@ -0,0 +1,9 @@
[package]
name = "async-handlers"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
futures = "0.1"
bytes = "0.4"

View File

@ -0,0 +1,28 @@
fn is_error() -> bool {
false
}
// <async-stream>
use actix_web::{error, Error, HttpResponse};
use futures::future::{result, Future};
fn index() -> Result<Box<Future<Item = HttpResponse, Error = Error>>, Error> {
if is_error() {
Err(error::ErrorBadRequest("bad request"))
} else {
Ok(Box::new(result(Ok(HttpResponse::Ok()
.content_type("text/html")
.body("Hello!")))))
}
}
// </async-stream>
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| App::new().route("/", web::to_async(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,30 @@
pub mod async_stream;
pub mod stream;
// <async-responder>
use actix_web::{Error, HttpResponse};
use futures::future::{ok, Future};
fn index() -> Box<Future<Item = HttpResponse, Error = Error>> {
Box::new(ok::<_, Error>(
HttpResponse::Ok().content_type("text/html").body("Hello!"),
))
}
fn index2() -> Box<Future<Item = &'static str, Error = Error>> {
Box::new(ok::<_, Error>("Welcome!"))
}
fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.route("/async", web::to_async(index))
.route("/", web::to_async(index2))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </async-responder>

View File

@ -0,0 +1,23 @@
// <stream>
use actix_web::{Error, HttpResponse};
use bytes::Bytes;
use futures::stream::once;
fn index() -> HttpResponse {
let body = once::<Bytes, Error>(Ok(Bytes::from_static(b"test")));
HttpResponse::Ok()
.content_type("application/json")
.streaming(Box::new(body))
}
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| App::new().route("/async", web::to_async(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </stream>

View File

@ -0,0 +1,8 @@
[package]
name = "autoreload"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
listenfd = "0.3"

View File

@ -0,0 +1,21 @@
// <autoreload>
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
use listenfd::ListenFd;
fn index(_req: HttpRequest) -> impl Responder {
"Hello World!"
}
fn main() {
let mut listenfd = ListenFd::from_env();
let mut server = HttpServer::new(|| App::new().route("/", web::get().to(index)));
server = if let Some(l) = listenfd.take_tcp_listener(0).unwrap() {
server.listen(l).unwrap()
} else {
server.bind("127.0.0.1:3000").unwrap()
};
server.run().unwrap();
}
// </autoreload>

View File

@ -0,0 +1,8 @@
[package]
name = "easy-form-handling"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
serde = "1.0"

View File

@ -0,0 +1,32 @@
// <easy-form-handling>
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::Deserialize;
#[derive(Deserialize)]
struct Register {
username: String,
country: String,
}
fn index() -> HttpResponse {
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(include_str!("../static/form.html"))
}
fn register(form: web::Form<Register>) -> impl Responder {
format!("Hello {} from {}!", form.username, form.country)
}
fn main() {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.route("/register", web::post().to(register))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </easy-form-handling>

View File

@ -0,0 +1,28 @@
<!doctype htmtl>
<html>
<head>
<meta charset=utf-8>
<title>Forms</title>
</head>
<body>
<h3>Its a form.</h3>
<form action=/register method=POST>
<label>
Name:
<input name="username">
</label>
<label>
Country:
<input name="country">
</label>
<button type=submit>Submit</button>
</form>
</body>
</html>

View File

@ -0,0 +1,8 @@
[package]
name = "either"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
futures = "0.1"

View File

@ -0,0 +1,35 @@
// <either>
use actix_web::{Either, Error, HttpResponse};
use futures::future::{ok, Future};
type RegisterResult =
Either<HttpResponse, Box<Future<Item = HttpResponse, Error = Error>>>;
fn index() -> RegisterResult {
if is_a_variant() {
// <- choose variant A
Either::A(HttpResponse::BadRequest().body("Bad data"))
} else {
Either::B(
// <- variant B
Box::new(ok(HttpResponse::Ok()
.content_type("text/html")
.body("Hello!".to_string()))),
)
}
}
fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| App::new().route("/", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </either>
fn is_a_variant() -> bool {
true
}

View File

@ -0,0 +1,10 @@
[package]
name = "my_errors"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
failure = "0.1"
env_logger = "0.6"
log = "0.4"

View File

@ -0,0 +1,25 @@
use actix_web::{web, App};
// <helpers>
use actix_web::{error, Result};
#[derive(Debug)]
struct MyError {
name: &'static str,
}
pub fn index() -> Result<&'static str> {
let result: Result<&'static str, MyError> = Err(MyError { name: "test error" });
Ok(result.map_err(|e| error::ErrorBadRequest(e.name))?)
}
// </helpers>
pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| App::new().route("/", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,38 @@
// <logging>
use actix_web::{error, Result};
use failure::Fail;
use log::debug;
#[derive(Fail, Debug)]
#[fail(display = "my error")]
pub struct MyError {
name: &'static str,
}
// Use default implementation for `error_response()` method
impl error::ResponseError for MyError {}
pub fn index() -> Result<&'static str, MyError> {
let err = MyError { name: "test error" };
debug!("{}", err);
Err(err)
}
pub fn main() {
use actix_web::{middleware::Logger, web, App, HttpServer};
std::env::set_var("RUST_LOG", "my_errors=debug,actix_web=info");
std::env::set_var("RUST_BACKTRACE", "1");
env_logger::init();
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.route("/", web::get().to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </logging>

View File

@ -0,0 +1,33 @@
pub mod helpers;
pub mod logging;
pub mod override_error;
pub mod recommend_one;
pub mod recommend_two;
// <response-error>
use actix_web::{error, Result};
use failure::Fail;
#[derive(Fail, Debug)]
#[fail(display = "my error")]
pub struct MyError {
name: &'static str,
}
// Use default implementation for `error_response()` method
impl error::ResponseError for MyError {}
fn index() -> Result<&'static str, MyError> {
Err(MyError { name: "test" })
}
// </response-error>
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| App::new().route("/", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,54 @@
use actix_web::{web, App};
// <override>
use actix_web::{error, http, HttpResponse};
use failure::Fail;
#[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() -> Result<&'static str, MyError> {
Err(MyError::BadClientData)
}
// </override>
fn error2() -> Result<&'static str, MyError> {
Err(MyError::InternalError)
}
fn error3() -> Result<&'static str, MyError> {
Err(MyError::Timeout)
}
pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.route("/e2", web::get().to(error2))
.route("/e3", web::get().to(error3))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,35 @@
// <recommend-one>
use actix_web::{error, http, HttpResponse};
use failure::Fail;
#[derive(Fail, Debug)]
enum UserError {
#[fail(display = "Validation error on field: {}", field)]
ValidationError { field: String },
}
impl error::ResponseError for UserError {
fn error_response(&self) -> HttpResponse {
match *self {
UserError::ValidationError { .. } => {
HttpResponse::new(http::StatusCode::BAD_REQUEST)
}
}
}
}
// </recommend-one>
fn index() -> Result<&'static str, UserError> {
Err(UserError::ValidationError {
field: "bad stuff".to_string(),
})
}
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| App::new().route("/", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,39 @@
// <recommend-two>
use actix_web::{error, http, HttpResponse};
use failure::Fail;
#[derive(Fail, Debug)]
enum UserError {
#[fail(display = "An internal error occurred. Please try again later.")]
InternalError,
}
impl error::ResponseError for UserError {
fn error_response(&self) -> HttpResponse {
match *self {
UserError::InternalError => {
HttpResponse::new(http::StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
}
fn index() -> Result<&'static str, UserError> {
do_thing_that_failes().map_err(|_e| UserError::InternalError)?;
Ok("success!")
}
// </recommend-two>
fn do_thing_that_failes() -> Result<(), std::io::Error> {
Err(std::io::Error::new(std::io::ErrorKind::Other, "some error"))
}
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| App::new().route("/", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,10 @@
[package]
name = "extractors"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
serde = "1.0"
futures = "0.1"
serde_json = "1.0"

View File

@ -0,0 +1,26 @@
// <form>
use actix_web::{web, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct FormData {
username: String,
}
/// extract form data using serde
/// this handler gets called only if the content type is *x-www-form-urlencoded*
/// and the content of the request could be deserialized to a `FormData` struct
fn index(form: web::Form<FormData>) -> Result<String> {
Ok(format!("Welcome {}!", form.username))
}
// </form>
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| App::new().route("/", web::post().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,24 @@
// <json-one>
use actix_web::{web, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
/// deserialize `Info` from request's body
fn index(info: web::Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
// </json-one>
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| App::new().route("/", web::post().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,42 @@
// <json-two>
use actix_web::{error, web, FromRequest, HttpResponse, Responder};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
/// deserialize `Info` from request's body, max payload size is 4kb
fn index(info: web::Json<Info>) -> impl Responder {
format!("Welcome {}!", info.username)
}
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().service(
web::resource("/")
.data(
// change json extractor configuration
web::Json::<Info>::configure(|cfg| {
cfg.limit(4096).error_handler(|err, _req| {
// <- create custom error response
error::InternalError::from_response(
err,
HttpResponse::Conflict().finish(),
)
.into()
})
}),
)
.route(web::post().to(index)),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </json-two>

View File

@ -0,0 +1,49 @@
use actix_web::{web, App, FromRequest, HttpRequest, HttpServer, Responder};
use futures::future::Future;
use serde::Deserialize;
// pub mod custom_handler;
pub mod form;
pub mod json_one;
pub mod json_two;
pub mod multiple;
pub mod path_one;
pub mod path_three;
pub mod path_two;
pub mod query;
#[derive(Deserialize, Debug)]
struct MyInfo {
username: String,
id: u32,
}
// <option-one>
fn index(path: web::Path<(String, String)>, json: web::Json<MyInfo>) -> impl Responder {
format!("{} {} {} {}", path.0, path.1, json.id, json.username)
}
// </option-one>
// <option-two>
fn extract(req: HttpRequest) -> impl Responder {
let params = web::Path::<(String, String)>::extract(&req).unwrap();
let info = web::Json::<MyInfo>::extract(&req)
.wait()
.expect("Err with reading json.");
format!("{} {} {} {}", params.0, params.1, info.username, info.id)
}
// </option-two>
fn main() {
HttpServer::new(|| {
App::new()
.route("/{name}/{id}", web::post().to(index))
.route("/{name}/{id}/extract", web::post().to(extract))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,31 @@
// <multi>
use actix_web::web;
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
fn index((path, query): (web::Path<(u32, String)>, web::Query<Info>)) -> String {
format!(
"Welcome {}, friend {}, useri {}!",
query.username, path.1, path.0
)
}
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/users/{userid}/{friend}", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </multi>

View File

@ -0,0 +1,25 @@
// <path-one>
use actix_web::{web, Result};
/// extract path info from "/users/{userid}/{friend}" url
/// {userid} - - deserializes to a u32
/// {friend} - deserializes to a String
fn index(info: web::Path<(u32, String)>) -> Result<String> {
Ok(format!("Welcome {}, userid {}!", info.1, info.0))
}
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/users/{userid}/{friend}", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </path-one>

View File

@ -0,0 +1,25 @@
use actix_web::{web, HttpRequest, Result};
// <path-three>
fn index(req: HttpRequest) -> Result<String> {
let name: String = req.match_info().get("friend").unwrap().parse().unwrap();
let userid: i32 = req.match_info().query("userid").parse().unwrap();
Ok(format!("Welcome {}, userid {}!", name, userid))
}
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/users/{userid}/{friend}", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </path-three>

View File

@ -0,0 +1,30 @@
// <path-two>
use actix_web::{web, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
userid: u32,
friend: String,
}
/// extract path info using serde
fn index(info: web::Path<Info>) -> Result<String> {
Ok(format!("Welcome {}, userid {}!", info.friend, info.userid))
}
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/users/{userid}/{friend}", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </path-two>

View File

@ -0,0 +1,24 @@
// <query>
use actix_web::web;
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
// this handler get called only if the request's query contains `username` field
fn index(info: web::Query<Info>) -> String {
format!("Welcome {}!", info.username)
}
// </query>
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| App::new().route("/", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,8 @@
[package]
name = "flexible-responders"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
serde = "1.0"

View File

@ -0,0 +1,29 @@
use actix_web::{web, App, HttpServer, Responder};
use serde::Serialize;
// <flexible-responders>
#[derive(Serialize)]
struct Measurement {
temperature: f32,
}
fn hello_world() -> impl Responder {
"Hello World!"
}
fn current_temperature() -> impl Responder {
web::Json(Measurement { temperature: 42.3 })
}
fn main() {
HttpServer::new(|| {
App::new()
.service(web::resource("/").to(hello_world))
.service(web::resource("/temp").to(current_temperature))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </flexible-responders>

View File

@ -1,7 +1,8 @@
[package] [package]
name = "getting-started" name = "getting-started"
version = "0.7.0" version = "1.0.0"
edition = "2018"
workspace = "../" workspace = "../"
[dependencies] [dependencies]
actix-web = "0.7" actix-web = "1.0"

View File

@ -1,16 +1,25 @@
// <setup> // <setup>
extern crate actix_web; use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use actix_web::{server, App, HttpRequest};
fn index(_req: &HttpRequest) -> &'static str { fn index() -> impl Responder {
"Hello world!" HttpResponse::Ok().body("Hello world!")
}
fn index2() -> impl Responder {
HttpResponse::Ok().body("Hello world again!")
} }
// </setup> // </setup>
// <main> // <main>
fn main() { fn main() {
server::new(|| App::new().resource("/", |r| r.f(index))) HttpServer::new(|| {
.bind("127.0.0.1:8088") App::new()
.unwrap() .route("/", web::get().to(index))
.run(); .route("/again", web::get().to(index2))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </main> // </main>

View File

@ -0,0 +1,8 @@
[package]
name = "http2"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = { version = "1.0", features = ["ssl"] }
openssl = { version = "0.10", features = ["v110"] }

View File

@ -0,0 +1,25 @@
// <main>
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
fn index(_req: HttpRequest) -> impl Responder {
"Hello."
}
fn main() {
// load ssl keys
// to create a self-signed temporary cert for testing:
// `openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'`
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().route("/", web::get().to(index)))
.bind_ssl("127.0.0.1:8088", builder)
.unwrap()
.run()
.unwrap();
}
// </main>

View File

@ -0,0 +1,7 @@
[package]
name = "main-example"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"

View File

@ -0,0 +1,20 @@
// <main-example>
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
fn greet(req: HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap_or("World");
format!("Hello {}!", &name)
}
fn main() {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(greet))
.route("/{name}", web::get().to(greet))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </main-example>

View File

@ -0,0 +1,11 @@
[package]
name = "middleware"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
actix-service = "0.4"
actix-session = "0.1"
futures = "0.1"
env_logger = "0.6"

View File

@ -0,0 +1,24 @@
// <default-headers>
use actix_web::{http, middleware, HttpResponse};
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
.service(
web::resource("/test")
.route(web::get().to(|| HttpResponse::Ok()))
.route(
web::method(http::Method::HEAD)
.to(|| HttpResponse::MethodNotAllowed()),
),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </default-headers>

View File

@ -0,0 +1,33 @@
// <error-handler>
use actix_web::middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers};
use actix_web::{dev, http, HttpResponse, Result};
fn render_500<B>(mut res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
res.response_mut().headers_mut().insert(
http::header::CONTENT_TYPE,
http::HeaderValue::from_static("Error"),
);
Ok(ErrorHandlerResponse::Response(res))
}
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(
ErrorHandlers::new()
.handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500),
)
.service(
web::resource("/test")
.route(web::get().to(|| HttpResponse::Ok()))
.route(web::head().to(|| HttpResponse::MethodNotAllowed())),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </error-handler>

View File

@ -0,0 +1,21 @@
// <logger>
use actix_web::middleware::Logger;
use env_logger;
pub fn main() {
use actix_web::{App, HttpServer};
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.wrap(Logger::new("%a %{User-Agent}i"))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </logger>

View File

@ -0,0 +1,82 @@
pub mod default_headers;
pub mod errorhandler;
pub mod logger;
pub mod user_sessions;
// <simple>
use actix_service::{Service, Transform};
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error};
use futures::future::{ok, FutureResult};
use futures::{Future, Poll};
// There are two steps in middleware processing.
// 1. Middleware initialization, middleware factory gets called with
// next service in chain as parameter.
// 2. Middleware's call method gets called with normal request.
pub struct SayHi;
// Middleware factory is `Transform` trait from actix-service crate
// `S` - type of the next service
// `B` - type of response's body
impl<S, B> Transform<S> for SayHi
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = SayHiMiddleware<S>;
type Future = FutureResult<Self::Transform, Self::InitError>;
fn new_transform(&self, service: S) -> Self::Future {
ok(SayHiMiddleware { service })
}
}
pub struct SayHiMiddleware<S> {
service: S,
}
impl<S, B> Service for SayHiMiddleware<S>
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
println!("Hi from start. You requested: {}", req.path());
Box::new(self.service.call(req).and_then(|res| {
println!("Hi from response");
Ok(res)
}))
}
}
// </simple>
fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new().wrap(SayHi).service(
web::resource("/")
.to(|| "Hello, middleware! Check the console where the server is run."),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,33 @@
// <user-session>
use actix_session::{CookieSession, Session};
use actix_web::{web, App, Error, HttpResponse, HttpServer};
pub fn index(session: Session) -> Result<HttpResponse, Error> {
// access session data
if let Some(count) = session.get::<i32>("counter")? {
session.set("counter", count + 1)?;
} else {
session.set("counter", 1)?;
}
Ok(HttpResponse::Ok().body(format!(
"Count is {:?}!",
session.get::<i32>("counter")?.unwrap()
)))
}
pub fn main() {
HttpServer::new(|| {
App::new()
.wrap(
CookieSession::signed(&[0; 32]) // <- create cookie based session middleware
.secure(false),
)
.service(web::resource("/").to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </user-session>

View File

@ -0,0 +1,7 @@
[package]
name = "og_databases"
version = "0.7.0"
edition = "2018"
[dependencies]
actix-web = "0.7"

View File

@ -0,0 +1,98 @@
// <actor>
// use actix::prelude::*;
// struct DbExecutor(SqliteConnection);
// impl Actor for DbExecutor {
// type Context = SyncContext<Self>;
// }
// </actor>
// <message>
// struct CreateUser {
// name: String,
// }
// impl Message for CreateUser {
// type Result = Result<User, Error>;
// }
// </message>
// <handler>
// impl Handler<CreateUser> for DbExecutor {
// type Result = Result<User, Error>;
// 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::<models::User>(&self.0)
// .expect("Error loading person");
// Ok(items.pop().unwrap())
// }
// }
// </handler>
// <main>
// /// This is state where we will store *DbExecutor* address.
// struct State {
// db: Addr<DbExecutor>,
// }
// 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();
// }
// </main>
// <index>
// /// Async handler
// fn index(req: &HttpRequest<State>) -> Box<Future<Item = HttpResponse, Error = Error>> {
// 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()
// }
// </index>

View File

@ -0,0 +1,8 @@
[package]
name = "powerful-extractors"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
serde = "1.0"

View File

@ -0,0 +1,44 @@
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct Event {
id: Option<i32>,
timestamp: f64,
kind: String,
tags: Vec<String>,
}
fn store_in_db(timestamp: f64, kind: &str, tags: &[String]) -> Event {
// store item in db and get new_event
// use id to lookup item
Event {
id: Some(1),
timestamp,
kind: kind.to_string(),
tags: tags.to_vec(),
}
}
fn capture_event(evt: web::Json<Event>) -> impl Responder {
let new_event = store_in_db(evt.timestamp, &evt.kind, &evt.tags);
format!("got event {}", new_event.id.unwrap())
}
fn index() -> HttpResponse {
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(include_str!("../static/form.html"))
}
fn main() {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.route("/event", web::post().to(capture_event))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,43 @@
<!doctype htmtl>
<html>
<head>
<meta charset=utf-8>
<title>Forms</title>
</head>
<body>
<h3>Submit Json</h3>
<button onclick="submitJson()">Submit</button>
<script>
let payload = {
timestamp: 12345,
kind: "this is a kind",
tags: ['tag1', 'tag2', 'tag3'],
}
function submitJson() {
fetch('http://localhost:8088/event', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(function (res) {
return res.json();
})
.then(function (data) {
let expected = {
...payload,
id: 1
};
console.log("expected: ", expected);
console.log("received: ", data);
});
}
</script>
</body>
</html>

View File

@ -0,0 +1,7 @@
[package]
name = "request-handlers"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"

View File

@ -0,0 +1,39 @@
// <arc>
use actix_web::{web, Responder};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[derive(Clone)]
struct AppState {
count: Arc<AtomicUsize>,
}
fn show_count(data: web::Data<AppState>) -> impl Responder {
format!("count: {}", data.count.load(Ordering::Relaxed))
}
fn add_one(data: web::Data<AppState>) -> impl Responder {
data.count.fetch_add(1, Ordering::Relaxed);
format!("count: {}", data.count.load(Ordering::Relaxed))
}
pub fn main() {
use actix_web::{App, HttpServer};
let data = AppState {
count: Arc::new(AtomicUsize::new(0)),
};
HttpServer::new(move || {
App::new()
.data(data.clone())
.route("/", web::to(show_count))
.route("/add", web::to(add_one))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </arc>

View File

@ -0,0 +1,40 @@
pub mod handlers_arc;
// <data>
use actix_web::{web, Responder};
use std::cell::Cell;
#[derive(Clone)]
struct AppState {
count: Cell<i32>,
}
fn show_count(data: web::Data<AppState>) -> impl Responder {
format!("count: {}", data.count.get())
}
fn add_one(data: web::Data<AppState>) -> impl Responder {
let count = data.count.get();
data.count.set(count + 1);
format!("count: {}", data.count.get())
}
fn main() {
use actix_web::{App, HttpServer};
let data = AppState {
count: Cell::new(0),
};
HttpServer::new(move || {
App::new()
.data(data.clone())
.route("/", web::to(show_count))
.route("/add", web::to(add_one))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </data>

View File

@ -0,0 +1,7 @@
[package]
name = "request-routing"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"

View File

@ -0,0 +1,23 @@
// <request-routing>
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
fn index(_req: HttpRequest) -> impl Responder {
"Hello from the index page."
}
fn hello(path: web::Path<String>) -> impl Responder {
format!("Hello {}!", &path)
}
fn main() {
HttpServer::new(|| {
App::new()
.service(web::resource("/").to(index))
.service(web::resource("/{name}").to(hello))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </request-routing>

View File

@ -0,0 +1,12 @@
[package]
name = "requests"
version = "1.0.0"
edition = "2018"
[dependencies]
serde = "1.0"
serde_json = "1.0"
actix-web = "1.0"
futures = "0.1"
bytes = "0.4"
actix-multipart = "0.1"

View File

@ -0,0 +1,21 @@
// // <json-two>
// use actix_web::{error::Error, HttpRequest, HttpResponse};
// use futures::Future;
// use serde::{Deserialize, Serialize};
// #[derive(Debug, Serialize, Deserialize)]
// struct MyObj {
// name: String,
// number: i32,
// }
// pub fn index(req: HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
// req.json()
// .from_err()
// .and_then(|val: MyObj| {
// println!("model: {:?}", val);
// Ok(HttpResponse::Ok().json(val)) // <- send response
// })
// .responder()
// }
// // </json-two>

View File

@ -0,0 +1,28 @@
pub mod json_two;
pub mod manual;
pub mod multipart;
pub mod streaming;
pub mod urlencoded;
// <json-request>
use actix_web::{web, App, HttpServer, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
/// extract `Info` using serde
fn index(info: web::Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
HttpServer::new(|| App::new().route("/", web::post().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </json-request>

View File

@ -0,0 +1,53 @@
// <json-manual>
use actix_web::{error, web, App, Error, HttpResponse};
use bytes::BytesMut;
use futures::{Future, Stream};
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Serialize, Deserialize)]
struct MyObj {
name: String,
number: i32,
}
const MAX_SIZE: usize = 262_144; // max payload size is 256k
pub fn index_manual(
payload: web::Payload,
) -> impl Future<Item = HttpResponse, Error = Error> {
// payload is a stream of Bytes objects
payload
// `Future::from_err` acts like `?` in that it coerces the error type from
// the future into the final error type
.from_err()
// `fold` will asynchronously read each chunk of the request body and
// call supplied closure, then it resolves to result of closure
.fold(BytesMut::new(), move |mut body, chunk| {
// limit max size of in-memory payload
if (body.len() + chunk.len()) > MAX_SIZE {
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
// `Future::and_then` can be used to merge an asynchronous workflow with a
// synchronous workflow
.and_then(|body| {
// body is loaded, now we can deserialize serde-json
let obj = serde_json::from_slice::<MyObj>(&body)?;
Ok(HttpResponse::Ok().json(obj)) // <- send response
})
}
// </json-manual>
pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| App::new().route("/", web::post().to_async(index_manual)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,25 @@
// <multipart>
// use actix_web::{error, Error, HttpRequest, HttpResponse};
// use futures::Future;
// pub fn index(req: HttpRequest) -> Box<Future<Item = HttpResponse, Error = Error>> {
// // get multipart and iterate over multipart items
// req.multipart().and_then(|item| match item {
// multipart::MultipartItem::Field(field) => {
// println!(
// "==== FIELD ==== {:?} {:?}",
// field.headers(),
// field.content_type()
// );
// Either::A(
// field
// .map(|chunk| {
// println!("-- CHUNK: \n{}", std::str::from_utf8(&chunk).unwrap());
// })
// .fold((), |_, _| result(Ok(()))),
// )
// }
// multipart::MultipartItem::Nested(mp) => Either::B(result(Ok(()))),
// })
// }
// </multipart>

View File

@ -0,0 +1,26 @@
// <streaming>
use actix_web::{error, web, Error, HttpResponse};
use futures::{future::result, Future, Stream};
pub fn index(payload: web::Payload) -> Box<Future<Item = HttpResponse, Error = Error>> {
Box::new(
payload
.from_err()
.fold((), |_, chunk| {
println!("Chunk: {:?}", chunk);
result::<_, error::PayloadError>(Ok(()))
})
.map(|_| HttpResponse::Ok().into()),
)
}
// </streaming>
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| App::new().route("/", web::post().to_async(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,23 @@
// <urlencoded>
use actix_web::{web, HttpResponse};
use serde::Deserialize;
#[derive(Deserialize)]
struct FormData {
username: String,
}
fn index(form: web::Form<FormData>) -> HttpResponse {
HttpResponse::Ok().body(format!("username: {}", form.username))
}
// </urlencoded>
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| App::new().route("/", web::post().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,9 @@
[package]
name = "responder-trait"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
serde = "1.0"
serde_json = "1.0"

View File

@ -0,0 +1,38 @@
// <responder-trait>
use actix_web::{Error, HttpRequest, HttpResponse, Responder};
use serde::Serialize;
#[derive(Serialize)]
struct MyObj {
name: &'static str,
}
// Responder
impl Responder for MyObj {
type Error = Error;
type Future = Result<HttpResponse, Error>;
fn respond_to(self, _req: &HttpRequest) -> Self::Future {
let body = serde_json::to_string(&self)?;
// Create response and set content type
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body))
}
}
fn index() -> impl Responder {
MyObj { name: "user" }
}
// </responder-trait>
fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| App::new().route("/", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,10 @@
[package]
name = "responses"
version = "1.0.0"
edition = "2018"
[dependencies]
actix-web = "1.0"
serde = "1.0"
futures = "0.1"
bytes = "0.4"

View File

@ -0,0 +1,21 @@
// <auto>
use actix_web::{http::ContentEncoding, middleware, HttpResponse};
fn index() -> HttpResponse {
HttpResponse::Ok().body("data")
}
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(middleware::Compress::new(ContentEncoding::Br))
.route("/", web::get().to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </auto>

View File

@ -0,0 +1,23 @@
// <brotli>
use actix_web::{http::ContentEncoding, middleware::BodyEncoding, HttpResponse};
fn index_br() -> HttpResponse {
HttpResponse::Ok()
.encoding(ContentEncoding::Br)
.body("data")
}
pub fn main() {
use actix_web::{middleware, web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(middleware::Compress::default())
.route("/", web::get().to(index_br))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </brotli>

View File

@ -0,0 +1,21 @@
// <brotli-two>
use actix_web::{http::ContentEncoding, middleware::BodyEncoding, HttpResponse};
fn index_br() -> HttpResponse {
HttpResponse::Ok().body("data")
}
pub fn main() {
use actix_web::{middleware, web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(middleware::Compress::new(ContentEncoding::Br))
.route("/", web::get().to(index_br))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </brotli-two>

View File

@ -0,0 +1,23 @@
// // <chunked>
// use actix_web::{web, HttpRequest, HttpResponse};
// 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",
// ))))))
// }
// // </chunked>
// pub fn main() {
// use actix_web::{web, App, HttpServer};
// HttpServer::new(|| App::new().route("/", web::get().to(index)))
// .bind("127.0.0.1:8088")
// .unwrap()
// .run()
// .unwrap();
// }

View File

@ -0,0 +1,21 @@
// <compress>
use actix_web::{middleware, HttpResponse};
fn index_br() -> HttpResponse {
HttpResponse::Ok().body("data")
}
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(middleware::Compress::default())
.route("/", web::get().to(index_br))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </compress>

View File

@ -0,0 +1,26 @@
// <identity>
use actix_web::{
http::ContentEncoding, middleware, middleware::BodyEncoding, HttpResponse,
};
fn index() -> HttpResponse {
HttpResponse::Ok()
// v- disable compression
.encoding(ContentEncoding::Identity)
.body("data")
}
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(middleware::Compress::default())
.route("/", web::get().to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </identity>

View File

@ -0,0 +1,32 @@
// <identity-two>
use actix_web::{
http::ContentEncoding, middleware, middleware::BodyEncoding, HttpResponse,
};
static HELLO_WORLD: &[u8] = &[
0x1f, 0x8b, 0x08, 0x00, 0xa2, 0x30, 0x10, 0x5c, 0x00, 0x03, 0xcb, 0x48, 0xcd, 0xc9,
0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf,
0x0c, 0x00, 0x00, 0x00,
];
pub fn index() -> HttpResponse {
HttpResponse::Ok()
.encoding(ContentEncoding::Identity)
.header("content-encoding", "gzip")
.body(HELLO_WORLD)
}
// </identity-two>
pub fn main() {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(middleware::Compress::default())
.route("/", web::get().to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -0,0 +1,25 @@
// <json-resp>
use actix_web::{web, HttpResponse, Result};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct MyObj {
name: String,
}
fn index(obj: web::Path<MyObj>) -> Result<HttpResponse> {
Ok(HttpResponse::Ok().json(MyObj {
name: obj.name.to_string(),
}))
}
pub fn main() {
use actix_web::{App, HttpServer};
HttpServer::new(|| App::new().route(r"/a/{name}", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </json-resp>

Some files were not shown because too many files have changed in this diff Show More