1
0
mirror of https://github.com/actix/actix-website synced 2024-11-27 18:12:57 +01:00

Merge branch 'master' into master

This commit is contained in:
Nikolay Kim 2019-07-19 14:15:14 +06:00 committed by GitHub
commit a4765b0577
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6340 changed files with 3461 additions and 439449 deletions

View File

@ -14,7 +14,7 @@ brew update && brew install hugo
Then, get the website running locally:
```sh
git clone https://github.com/mitsuhiko/actix-website
git clone https://github.com/actix/actix-website.git
cd actix-website
hugo server
```

View File

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

View File

@ -12,7 +12,7 @@ Here are the most important projects and the link to their github repositories
and related resources:
* [actix](https://github.com/actix/actix) ([issues](https://github.com/actix/actix/issues), [ci](https://travis-ci.org/actix/actix), [crate](https://crates.io/crates/actix), [api docs](https://docs.rs/actix))
* [actix-web](https://github.com/actix/actix-web) ([issues](https://github.com/actix/actix-web/issues), [ci](https://travis-ci.org/actix/actix-web), [crate](https://crates.io/crates/actix-web), [api docs](https://actix.rs/api/actix-web/stable/actix_web/))
* [actix-web](https://github.com/actix/actix-web) ([issues](https://github.com/actix/actix-web/issues), [ci](https://travis-ci.org/actix/actix-web), [crate](https://crates.io/crates/actix-web), [api docs](https://docs.rs/actix))
* [example code](https://github.com/actix/examples)
* [this website](https://github.com/actix/actix-website)

View File

@ -12,13 +12,16 @@ weight: 10
Actix is your door to developing web services with Rust and this documentation
is going to guide you.
This documentation currently covers mostly the `actix-web` part which is the
high level web framework build on top of the `actix` actor framework and the
[Tokio](https://tokio.rs/) async IO system. This is the part that is from an
API stability point of view the most stable.
This documentation currently covers mostly the `actix-web` part which is the high level
web framework previously built on top of the `actix` actor framework and the [Tokio][tokio]
async IO system. This is the part that is from an API stability point of view the most stable.
If you haven't used actix yet it's best to start with the [getting started
guide](getting-started/). If you already know your ways around and you need
specific information you might want to read the [actix-web API
docs](https://actix.rs/api/actix-web/stable/actix_web/) (or the lower level [actix API
docs](https://docs.rs/actix)).
If you haven't used `actix-web` yet it's best to start with the [getting started
guide][gettingstarted]. If you already know your ways around and you need
specific information you might want to read the [actix-web API docs][actixwebdocs]
(or the lower level [actix API docs][actixdocs]).
[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
`actix-web` provides various primitives to build web servers and applications with Rust.
It provides routing, middlewares, pre-processing of requests, post-processing of responses,
websocket protocol handling, multipart streams, etc.
It provides routing, middlewares, pre-processing of requests, post-processing of
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
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
have the same url path prefix. The application prefix always contains a leading "/" slash.
If a supplied prefix does not contain leading slash, it is automatically inserted.
The prefix should consist of value path segments.
An application's `scope` acts as a namespace for all routes, i.e. all routes for a
specific application scope have the same url path prefix. The application prefix always
contains a leading "/" slash. If a supplied prefix does not contain leading slash,
it is automatically inserted. The prefix should consist of value path segments.
> For an application with prefix `/app`,
> For an application with scope `/app`,
> any request with the paths `/app`, `/app/`, or `/app/test` would 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
are created. This resource is available through the `/app/index.html` url.
> For more information, check the
> [URL Dispatch](/docs/url-dispatch/index.html#using-an-application-prefix-to-compose-applications) section.
> For more information, check the [URL Dispatch][usingappprefix] 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.
**Applications get matched based on registration order**. If an application with a more generic
@ -43,70 +42,90 @@ as the first application, it would match all incoming requests.
## State
Application state is shared with all routes and resources within the same application.
When using an http actor, state can be accessed with the `HttpRequest::state()` as read-only,
but interior mutability with `RefCell` can be used to achieve state mutability.
State is also available for route matching predicates and middlewares.
Application state is shared with all routes and resources within the same scope. State
can be accessed with the `web::Data<State>` extractor. State is also available for route matching guards and middlewares.
Let's write a simple application that uses shared state. We are going to store request count
in the state:
Let's write a simple application and store the application name in the state:
{{< include-example example="application" file="state.rs" section="setup" >}}
When the app is initialized it needs to be passed the initial state:
{{< include-example example="application" file="state.rs" section="make_app" >}}
> **Note**: http server accepts an application factory rather than an application
> instance. Http server constructs an application instance for each thread, thus application state
> must be constructed multiple times. If you want to share state between different threads, a
> shared object should be used, e.g. `Arc`. There is also an [Example](https://github.com/actix/examples/blob/master/state/src/main.rs) use `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:
and pass in the state when initializing the App, and start the application:
{{< include-example example="application" file="state.rs" section="start_app" >}}
## Shared Mutable State
`HttpServer` accepts an application factory rather than an application instance.
Http server constructs an application instance for each thread, thus application data must be
constructed multiple times. If you want to share data between different threads, a shareable
object should be used, e.g. Send + Sync.
Internally, `web::Data` uses Arc. Thus, in order to avoid double Arc, we should create our Data before registering it using `register_data()`.
In the following example, we will write an application with mutable, shared state. First, we define our state and create our handler:
{{< include-example example="application" file="state.rs" section="setup_mutable" >}}
and register the data in an App:
{{< include-example example="application" file="state.rs" section="make_app_mutable" >}}
## Combining applications with different state
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" >}}
## Using an Application Prefix to Compose Applications
The `App::prefix()` method allows to set a specific application prefix.
This prefix represents a resource prefix that will be prepended to all resource patterns added
by the resource configuration. This can be used to help mount a set of routes at a different
location than the included callable's author intended while still maintaining the same
resource names.
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
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:
{{< 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
*/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*,
and when the `HttpRequest.url_for()` function is called with the route name show_users,
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
and returns *true* or *false*. Formally, a predicate is any object that implements the
[`Predicate`](../actix_web/pred/trait.Predicate.html) trait. Actix provides
several predicates, you can check
[functions section](../../actix-web/actix_web/pred/index.html#functions) of api docs.
You can think of a guard as a simple function that accepts a *request* object reference
and returns *true* or *false*. Formally, a guard is any object that implements the
[`Guard`][guardtrait] trait. Actix-web provides several guards, you can check
[functions section][guardfuncs] of api docs.
Any of this predicates could be used
with [`App::filter()`](../actix_web/struct.App.html#method.filter) method. One of the
provided predicates is [`Host`](../actix_web/pred/fn.Host.html), it can be used
as application's filter based on request's host information.
One of the provided guards is [`Header`][guardheader], it can be used as application's
filter based on request's header information.
{{< 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
During development it can be very handy to have cargo automatically recompile
the code on change. This can be accomplished by using
[cargo-watch](https://github.com/passcod/cargo-watch). Because an actix app
will typically bind to a port for listening for incoming HTTP requests it makes
sense to combine this with the [listenfd](https://crates.io/crates/listenfd)
crate and the [systemfd](https://github.com/mitsuhiko/systemfd) utility to
ensure the socket is kept open while the app is compiling and reloading.
During development it can be very handy to have cargo automatically recompile the code
on change. This can be accomplished by using [cargo-watch][cargowatch]. Because an
actix app 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]
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
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
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
app:
an external socket opened by `systemfd`. Add the listenfd dependency to your app:
```ini
[dependencies]
@ -40,33 +37,7 @@ listenfd = "0.3"
Then modify your server code to only invoke `bind` as a fallback:
```rust
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(listener) = listenfd.take_tcp_listener(0).unwrap() {
server.listen(listener)
} else {
server.bind("127.0.0.1:3000").unwrap()
};
server.run();
}
```
{{< include-example example="autoreload" file="main.rs" section="autoreload" >}}
## 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
```
[cargowatch]: https://github.com/passcod/cargo-watch
[listenfd]: https://crates.io/crates/listenfd
[systemfd]: https://github.com/mitsuhiko/systemfd

View File

@ -6,6 +6,14 @@ weight: 1010
# Diesel
{{% alert %}}
NOTE: The `actix-web` 1.0 version of this section is still
[being updated](https://github.com/cldershem/actix-website/tree/update1.0-db). Checkout
this [example](https://github.com/actix/examples/tree/master/async_db) until then.
{{% /alert %}}
[being updated](https://github.com/cldershem/actix-website/tree/update1.0-db).
At the moment, Diesel 1.0 does not support asynchronous operations,
but it's possible to use the `actix` synchronous actor system as a database interface api.
@ -16,117 +24,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
can be used for other databases.
```rust
use actix::prelude::*;
struct DbExecutor(SqliteConnection);
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
}
```
{{< include-example example="og_databases" file="main.rs" section="actor" >}}
This is the definition of our actor. Now, we must define the *create user* message and response.
```rust
struct CreateUser {
name: String,
}
impl Message for CreateUser {
type Result = Result<User, Error>;
}
```
{{< include-example example="og_databases" file="main.rs" section="message" >}}
We can send a `CreateUser` message to the `DbExecutor` actor, and as a result, we will receive a
`User` model instance. Next, we must define the handler implementation for this message.
```rust
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())
}
}
```
{{< include-example example="og_databases" file="main.rs" section="handler" >}}
That's it! Now, we can use the *DbExecutor* actor from any http handler or middleware.
All we need is to start *DbExecutor* actors and store the address in a state where http handler
can access it.
```rust
/// 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();
}
```
{{< include-example example="og_databases" file="main.rs" section="main" >}}
We will use the address in a request handler. The handle returns a future object;
thus, we receive the message response asynchronously.
`Route::a()` must be used for async handler registration.
{{< include-example example="og_databases" file="main.rs" section="index" >}}
```rust
/// 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/).
> A full example is available in the [examples directory][examples].
> 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
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
from web handlers.
If a handler returns an `Error` (referring to the [general Rust trait
`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
`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>
```
`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.
Actix-web provides `ResponseError` implementations for some common non-actix
@ -40,34 +40,19 @@ converted into an `HttpInternalServerError`:
```rust
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")?)
}
```
See [the actix-web API documentation][responseerrorimpls] for a full list of
foreign implementations for `ResponseError`.
See [the actix-web API documentation][responseerrorimpls] for a full list of foreign
implementations for `ResponseError`.
## An example of a custom error response
Here's an example implementation for `ResponseError`:
```rust
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"})
}
```
{{< include-example example="errors" file="main.rs" section="response-error" >}}
`ResponseError` has a default implementation for `error_response()` that will
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:
```rust
#[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)
}
```
{{< include-example example="errors" file="override_error.rs" section="override" >}}
# 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
doesn't implement the `ResponseError` trait, to a *400* (bad request) using
`map_err`:
```rust
# extern crate actix_web;
use actix_web::*;
{{< include-example example="errors" file="helpers.rs" section="helpers" >}}
#[derive(Debug)]
struct MyError {
name: &'static str
}
fn index(req: &HttpRequest) -> Result<&'static str> {
let result: Result<&'static str, MyError> = Err(MyError{name: "test"});
Ok(result.map_err(|e| error::ErrorBadRequest(e.name))?)
}
```
See the [API documentation for actix-web's `error` module][errorhelpers] for a
full list of available error helpers.
See the [API documentation for actix-web's `error` module][actixerror]
for a full list of available error helpers.
# 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
input:
```rust
#[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),
}
}
}
```
{{< include-example example="errors" file="recommend_one.rs" section="recommend-one" >}}
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.
@ -201,38 +122,21 @@ consumption.
Here's an example that maps an internal error to a user-facing `InternalError`
with a custom message:
```rust
#[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!")
}
```
{{< include-example example="errors" file="recommend_two.rs" section="recommend-two" >}}
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
application internals which they weren't meant to see.
[actixerror]: ../../actix-web/actix_web/error/struct.Error.html
[errorhelpers]: ../../actix-web/actix_web/error/index.html#functions
# Error Logging
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
[responseerror]: ../../actix-web/actix_web/error/trait.ResponseError.html
[responseerrorimpls]: ../../actix-web/actix_web/error/trait.ResponseError.html#foreign-impls
[responseerror]: https://docs.rs/actix-web/1.0.2/actix_web/error/trait.ResponseError.html
[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

View File

@ -6,280 +6,134 @@ weight: 170
# Type-safe information extraction
Actix provides facility for type-safe request information extraction. By default,
actix provides several extractor implementations.
Actix-web provides a facility for type-safe request information access called *extractors*
(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
or a custom Handler type.
An extractor can be accessed in a few different ways.
## 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
*or* accessed within the function by calling the ExtractorType::<...>::extract(req)
function.
```rust
{{< include-example example="extractors" file="main.rs" section="option-one" >}}
// Option 1: passed as a parameter to a handler function
fn index((params, info): (Path<(String, String,)>, Json<MyInfo>)) -> HttpResponse {
...
}
Option 2 - accessed by calling `extract()` on the Extractor
// 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()
}
}
```
{{< include-example example="extractors" file="main.rs" section="option-two" >}}
# Path
[*Path*](../../actix-web/actix_web/struct.Path.html) provides information that can
be extracted from the Request's path. You can deserialize any variable
segment from the path.
[*Path*][pathstruct] provides information that can be extracted from the Request's
path. You can deserialize any variable segment from the path.
For instance, for resource that registered for the `/users/{userid}/{friend}` path
two segments could be deserialized, `userid` and `friend`. These segments
could be extracted into a `tuple`, i.e. `Path<(u32, String)>` or any structure
that implements the `Deserialize` trait from the *serde* crate.
two segments could be deserialized, `userid` and `friend`. These segments could be
extracted into a `tuple`, i.e. `Path<(u32, String)>` or any structure that implements
the `Deserialize` trait from the *serde* crate.
```rust
use actix_web::{App, Path, Result, http};
{{< include-example example="extractors" file="path_one.rs" section="path-one" >}}
/// extract path info from "/users/{userid}/{friend}" url
/// {userid} - - deserializes to a u32
/// {friend} - deserializes to a String
fn index(info: Path<(u32, String)>) -> Result<String> {
Ok(format!("Welcome {}! {}", info.1, info.0))
}
fn main() {
let app = App::new().resource(
"/users/{userid}/{friend}", // <- define path parameters
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}
```
Remember! 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*
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.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Path, Result, http};
{{< include-example example="extractors" file="path_two.rs" section="path-two" >}}
#[derive(Deserialize)]
struct Info {
userid: u32,
friend: String,
}
It is also possible to `get` or `query` the request for path parameters by name:
/// extract path info using serde
fn index(info: Path<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.friend))
}
fn main() {
let app = App::new().resource(
"/users/{userid}/{friend}", // <- define path parameters
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}
```
{{< include-example example="extractors" file="path_three.rs" section="path-three" >}}
# Query
Same can be done with the request's query.
The [*Query*](../../actix-web/actix_web/struct.Query.html)
type provides extraction functionality. Underneath it uses *serde_urlencoded* crate.
The [*Query*][querystruct] type provides extraction functionality for the request's
query parameters. Underneath it uses *serde_urlencoded* crate.
```rust
#[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
}
```
{{< include-example example="extractors" file="query.rs" section="query" >}}
# Json
[*Json*](../../actix-web/actix_web/struct.Json.html) allows to deserialize
a request body into a struct. To extract typed information from a request's body,
the type `T` must implement the `Deserialize` trait from *serde*.
[*Json*][jsonstruct] allows to deserialize a request body into a struct. To extract
typed information from a request's body, the type `T` must implement the `Deserialize`
trait from *serde*.
```rust
#[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
}
```
{{< include-example example="extractors" file="json_one.rs" section="json-one" >}}
Some extractors provide a way to configure the extraction process. Json extractor
[*JsonConfig*](../../actix-web/actix_web/dev/struct.JsonConfig.html) type for configuration.
When you register a handler using `Route::with()`, it returns a configuration instance. In case of
a *Json* extractor it returns a *JsonConfig*. You can configure the maximum size of the json
payload as well as a custom error handler function.
[*JsonConfig*][jsonconfig] type for configuration. To configure an extractor, pass it's
configuration object to the resource's `.data()` method. In case of a *Json* extractor
it returns a *JsonConfig*. You can configure the maximum size of the json payload as
well as a custom error handler function.
The following example limits the size of the payload to 4kb and uses a custom error handler.
```rust
#[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()
})
});
});
}
```
{{< include-example example="extractors" file="json_two.rs" section="json-two" >}}
# Form
At the moment only url-encoded forms are supported. The url-encoded body
could be extracted to a specific type. This type must implement
the `Deserialize` trait from the *serde* crate.
At the moment only url-encoded forms are supported. The url-encoded body could be
extracted to a specific type. This type must implement the `Deserialize` trait from
the *serde* crate.
[*FormConfig*](../../actix-web/actix_web/dev/struct.FormConfig.html) allows
configuring the extraction process.
[*FormConfig*][formconfig] allows configuring the extraction process.
```rust
#[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() {}
```
{{< include-example example="extractors" file="form.rs" section="form" >}}
# Multiple extractors
Actix provides extractor implementations for tuples (up to 10 elements)
whose elements implement `FromRequest`.
Actix-web provides extractor implementations for tuples (up to 10 elements) whose
elements implement `FromRequest`.
For example we can use a path extractor and a query extractor at the same time.
```rust
#[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
}
```
{{< include-example example="extractors" file="multiple.rs" section="multi" >}}
# 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
access to an application state. This is similar to a `HttpRequest::state()`.
* *HttpRequest* - *HttpRequest* itself is an extractor which returns self,
in case you need access to the request.
* *String* - You can convert a request's payload to a *String*.
[*Example*](../../actix-web/actix_web/trait.FromRequest.html#example-1)
* [*Data*][datastruct] - If you need access to an application state.
* *HttpRequest* - *HttpRequest* itself is an extractor which returns self, in case you
need access to the request.
* *String* - You can convert a request's payload to a *String*. [*Example*][stringexample]
is available in doc strings.
* *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.
* *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
Lets write our first actix web application!
Lets write our first `actix-web` application!
## Hello, world!
@ -27,25 +27,24 @@ actix-web = "{{< actix-version "actix-web" >}}"
In order to implement a web server, we first need to create a request handler.
A request handler is a function that accepts an `HttpRequest` instance as its only parameter
and returns a type that can be converted into `HttpResponse`:
Filename: `src/main.rs`
A request handler is a function that accepts zero or more parameters that can be
extracted from a request (ie, `impl FromRequest`) and returns a type that can be
converted into an `HttpResponse` (ie, `impl Responder`):
{{< include-example example="getting-started" section="setup" >}}
Next, create an `Application` instance and register the request handler with
the application's `resource` on a particular *HTTP method* and *path* and
after that, the application instance can be used with `HttpServer` to listen
for incoming connections. The server accepts a function that should return an
`HttpHandler` instance. For simplicity `server::new` could be used, this
function is shortcut for `HttpServer::new`:
Next, create an `App` instance and register the request handler with the application's
`route` on a *path* and with a particular *HTTP method*. After that, the application
instance can be used with `HttpServer` to listen for incoming connections. The server
accepts a function that should return an application factory.
{{< include-example example="getting-started" section="main" >}}
That's it! Now, compile and run the program with `cargo run`.
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
at the [autoreload pattern](../autoreload/).
at the [autoreload pattern][autoload].
[autoload]: ../autoreload/

View File

@ -6,30 +6,29 @@ weight: 160
# Request Handlers
A request handler can be any object that implements
[*Handler*](../../actix-web/actix_web/dev/trait.Handler.html) trait.
A request handler is a function that accepts zero or more parameters that can be extracted
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,
returning any object that implements the
[*Responder*](../../actix-web/actix_web/trait.Responder.html#foreign-impls) trait.
Then, `respond_to()` is called on the returned object, converting itself to a `AsyncResult` or `Error`.
Request handling happens in two stages. First the handler object is called, returning any
object that implements the [*Responder*][respondertrait] trait. Then, `respond_to()` is
called on the returned object, converting itself to a `HttpResponse` 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.
> For a complete list of implementations, check
> [*Responder documentation*](../../actix-web/actix_web/trait.Responder.html#foreign-impls).
> For a complete list of implementations, check [*Responder documentation*][responderimpls].
Examples of valid handlers:
```rust
fn index(req: &HttpRequest) -> &'static str {
fn index(_req: HttpRequest) -> &'static str {
"Hello world!"
}
```
```rust
fn index(req: &HttpRequest) -> String {
fn index(_req: HttpRequest) -> String {
"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.
```rust
fn index(req: &HttpRequest) -> impl Responder {
Bytes::from_static("Hello world!")
fn index(_req: HttpRequest) -> impl Responder {
Bytes::from_static(b"Hello world!")
}
```
```rust,ignore
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
```rust
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
To return a custom type directly from a handler function, the type needs to implement the `Responder` trait.
Let's create a response for a custom type that serializes to an `application/json` response:
```rust
# extern crate actix;
# extern crate actix_web;
extern crate serde;
extern crate serde_json;
#[macro_use] extern crate serde_derive;
use actix_web::{server, App, HttpRequest, HttpResponse, Error, Responder, http};
#[derive(Serialize)]
struct MyObj {
name: &'static str,
}
/// Responder
impl Responder for MyObj {
type Item = HttpResponse;
type Error = Error;
fn respond_to<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();
}
```
{{< include-example example="responder-trait" file="main.rs" section="responder-trait" >}}
## Async handlers
There are two different types of async handlers. Response objects can be generated asynchronously
or more precisely, any type that implements the [*Responder*](../../actix-web/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:
```rust
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
use futures::future::{Future, result};
{{< include-example example="async-handlers" file="main.rs" section="async-responder" >}}
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()
.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();
}
```
{{< include-example example="async-handlers" file="stream.rs" section="stream" >}}
Both methods can be combined. (i.e Async response with streaming body)
It is possible to return a `Result` where the `Result::Item` type can be `Future`.
In this example, the `index` handler can return an error immediately or return a
future that resolves to a `HttpResponse`.
It is possible to return a `Result` where the `Result::Item` type can be `Future`. In
this example, the `index` handler can return an error immediately or return a future
that resolves to a `HttpResponse`.
```rust
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!"))))))
}
}
```
{{< include-example example="async-handlers" file="async_stream.rs" section="async-stream" >}}
## Different return types (Either)
Sometimes, you need to return different types of responses. For example,
you can error check and return errors, return async responses, or any result that requires two different types.
Sometimes, you need to return different types of responses. For example, you can error
check and return errors, return async responses, or any result that requires two different types.
For this case, the [*Either*](../../actix-web/actix_web/enum.Either.html) type can be used.
`Either` allows combining two different responder types into a single type.
For this case, the [*Either*][either] type can be used. `Either` allows combining two
different responder types into a single type.
```rust
use futures::future::{Future, result};
use actix_web::{Either, Error, HttpResponse};
{{< include-example example="either" file="main.rs" section="either" >}}
type RegisterResult = Either<HttpResponse, Box<Future<Item=HttpResponse, Error=Error>>>;
fn index(req: &HttpRequest) -> RegisterResult {
if is_a_variant() { // <- choose variant A
Either::A(
HttpResponse::BadRequest().body("Bad data"))
} else {
Either::B( // <- variant B
result(Ok(HttpResponse::Ok()
.content_type("text/html")
.body(format!("Hello!")))).responder())
}
}
```
[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
[respondertrait]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html
[responderimpls]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html#foreign-impls
[either]: https://docs.rs/actix-web/1.0.2/actix_web/enum.Either.html

View File

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

View File

@ -6,21 +6,19 @@ weight: 110
# Installing Rust
Since `actix-web` is a Rust framework you will need Rust to get started with it.
If you don't have it yet we recommend you use `rustup` to manage your Rust
installation. The [official rust
guide](https://doc.rust-lang.org/book/second-edition/ch01-01-installation.html)
has a wonderful section on getting started.
Since `actix-web` is a Rust framework you will need Rust to get started with it. If you
don't have it yet we recommend you use `rustup` to manage your Rust installation. The
[official rust guide][rustguide] has a wonderful section on getting started.
We currently require at least Rust 1.24 so make sure you run `rustup update`
to have the latest and greatest Rust version available. In particular this
guide will assume that you actually run Rust 1.26 or later.
We currently require at least Rust {{< rust-version "actix-web" >}} so make sure you run
`rustup update` to have the latest and greatest Rust version available. In particular
this guide will assume that you actually run Rust {{< rust-version "actix-web" >}} or later.
# 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
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.
Release version:
@ -39,10 +37,9 @@ actix-web = { git = "https://github.com/actix/actix-web" }
# Diving In
There are two paths you can take here. You can follow the guide along or if
you are very impatient you might want to have a look at our
[extensive example repository](https://github.com/actix/examples) and run the
included examples. Here for instance is how you run the included `basics`
There are two paths you can take here. You can follow the guide along or if you are very
impatient you might want to have a look at our [extensive example repository][examples]
and run the included examples. Here for instance is how you run the included `basics`
example:
```
@ -50,3 +47,6 @@ git clone https://github.com/actix/examples
cd examples/basics
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
Actix's middleware system allows us to add additional behavior to request/response processing.
Middleware can hook into an incoming request process, enabling us to modify requests
as well as halt request processing to return a response early.
Actix-web's middleware system allows us to add additional behavior to request/response
processing. Middleware can hook into an incoming request process, enabling us to modify
requests as well as halt request processing to return a response early.
Middleware can also hook into response processing.
@ -19,87 +19,38 @@ Typically, middleware is involved in the following actions:
* Modify application state
* Access external services (redis, logging, sessions)
Middleware is registered for each application and executed in same order as
registration. In general, a *middleware* is a type that implements the
[*Middleware trait*](../../actix-web/actix_web/middleware/trait.Middleware.html).
Each method in this trait has a default implementation. Each method can return
a result immediately or a *future* object.
Middleware is registered for each `App`, `scope`, or `Resource` and executed in opposite
order as registration. In general, a *middleware* is a type that implements the
[*Service trait*][servicetrait] and [*Transform trait*][transformtrait]. Each method in
the traits has a default implementation. Each method can return a result immediately
or a *future* object.
The following demonstrates using middleware to add request and response headers:
The following demonstrates creating a simple middleware:
```rust
use http::{header, HttpTryFrom};
use actix_web::{App, HttpRequest, HttpResponse, Result};
use actix_web::middleware::{Middleware, Started, Response};
{{< include-example example="middleware" file="main.rs" section="simple" >}}
struct Headers; // <- Our middleware
/// 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.
> Actix-web provides several useful middlewares, such as *logging*, *user sessions*,
> *compress*, etc.
# Logging
Logging is implemented as a middleware.
It is common to register a logging middleware as the first middleware for the application.
Logging middleware must be registered for each application.
Logging is implemented as a middleware. It is common to register a logging middleware
as the first middleware for the application. Logging middleware must be registered for
each application.
The `Logger` middleware uses the standard log crate to log information. You should enable logger
for *actix_web* package to see access log ([env_logger](https://docs.rs/env_logger/*/env_logger/)
or similar).
for *actix_web* package to see access log ([env_logger][envlogger] or similar).
## Usage
Create `Logger` middleware with the specified `format`.
Default `Logger` can be created with `default` method, it uses the default format:
Create `Logger` middleware with the specified `format`. Default `Logger` can be created
with `default` method, it uses the default format:
```ignore
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
```
```rust
extern crate 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();
}
```
{{< include-example example="middleware" file="logger.rs" section="logger" >}}
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
`%%` The percent sign
`%a` Remote IP-address (IP-address of proxy if using reverse proxy)
`%t` Time when the request was started to process
`%P` The process ID of the child that serviced the request
`%r` First line of request
`%s` Response status code
`%b` Size of response in bytes, including HTTP headers
`%T` Time taken to serve the request, in seconds with floating fraction in .06f format
`%D` Time taken to serve the request, in milliseconds
`%{FOO}i` request.headers['FOO']
`%{FOO}o` response.headers['FOO']
`%{FOO}e` os.environ['FOO']
- `%%` The percent sign
- `%a` Remote IP-address (IP-address of proxy if using reverse proxy)
- `%t` Time when the request was started to process
- `%P` The process ID of the child that serviced the request
- `%r` First line of request
- `%s` Response status code
- `%b` Size of response in bytes, including HTTP headers
- `%T` Time taken to serve the request, in seconds with floating fraction in .06f format
- `%D` Time taken to serve the request, in milliseconds
- `%{FOO}i` request.headers['FOO']
- `%{FOO}o` response.headers['FOO']
- `%{FOO}e` os.environ['FOO']
## Default headers
@ -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
a specified header.
```rust
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();
}
```
{{< include-example example="middleware" file="default_headers.rs" section="default-headers" >}}
## User sessions
Actix provides a general solution for session management. The
[**SessionStorage**](../../actix-web/actix_web/middleware/session/struct.SessionStorage.html) middleware can be
used with different backend types to store session data in different backends.
Actix-web provides a general solution for session management. The
[**actix-session**][actixsession] middleware can be used with different backend types
to store session data in different backends.
> By default, only cookie session backend is implemented. Other backend implementations
> can be added.
[**CookieSessionBackend**](../../actix-web/actix_web/middleware/session/struct.CookieSessionBackend.html)
uses cookies as session storage. `CookieSessionBackend` creates sessions which
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a
single cookie. An internal server error is generated if a session contains more than 4000 bytes.
[**CookieSession**][cookiesession] uses cookies as session storage. `CookieSessionBackend`
creates sessions which are limited to storing fewer than 4000 bytes of data, as the payload
must fit into a single cookie. An internal server error is generated if a session
contains more than 4000 bytes.
A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor.
A 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
`SessionStorage` middleware and initialize it with specific backend implementation,
such as a `CookieSessionBackend`. To access session data,
[*HttpRequest::session()*](../../actix-web/actix_web/middleware/session/trait.RequestSession.html#tymethod.session)
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.
In general, you create a `SessionStorage` middleware and initialize it with specific
backend implementation, such as a `CookieSession`. To access session data the
[`Session`][requestsession] extractor must be used. This method returns a
[*Session*][sessionobj] object, which allows us to get or set session data.
```rust
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();
}
```
{{< include-example example="middleware" file="user_sessions.rs" section="user-session" >}}
# 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
into a response.
```rust
use actix_web::{
App, HttpRequest, HttpResponse, Result,
http, middleware::Response, middleware::ErrorHandlers};
{{< include-example example="middleware" file="errorhandler.rs" section="error-handler" >}}
fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
let mut builder = resp.into_builder();
builder.header(http::header::CONTENT_TYPE, "application/json");
Ok(Response::Done(builder.into()))
}
fn main() {
let app = App::new()
.middleware(
ErrorHandlers::new()
.handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500))
.resource("/test", |r| {
r.method(http::Method::GET).f(|_| HttpResponse::Ok());
r.method(http::Method::HEAD).f(|_| HttpResponse::MethodNotAllowed());
})
.finish();
}
```
[sessionobj]: https://docs.rs/actix-session/0.1.1/actix_session/struct.Session.html
[requestsession]: https://docs.rs/actix-session/0.1.1/actix_session/struct.Session.html
[cookiesession]: https://docs.rs/actix-session/0.1.1/actix_session/struct.CookieSession.html
[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
[transformtrait]: https://docs.rs/actix-web/1.0.2/actix_web/dev/trait.Transform.html

View File

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

View File

@ -6,112 +6,62 @@ weight: 210
# Response
A builder-like pattern is used to construct an instance of `HttpResponse`.
`HttpResponse` provides several methods that return a `HttpResponseBuilder` instance,
which implements various convenience methods for building responses.
A builder-like pattern is used to construct an instance of `HttpResponse`. `HttpResponse`
provides several methods that return a `HttpResponseBuilder` instance, which implements
various convenience methods for building responses.
> Check the [documentation](../../actix-web/actix_web/dev/struct.HttpResponseBuilder.html)
> for type descriptions.
> Check the [documentation][responsebuilder] for type descriptions.
The methods `.body`, `.finish`, and `.json` finalize response creation and
return a constructed *HttpResponse* instance. If this methods is called on the same
builder instance multiple times, the builder will panic.
The methods `.body`, `.finish`, and `.json` finalize response creation and return a
constructed *HttpResponse* instance. If this methods is called on the same builder
instance multiple times, the builder will panic.
```rust
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")
}
```
{{< include-example example="responses" file="main.rs" section="builder" >}}
# 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
* Gzip
* Deflate
* Identity
Response payload is compressed based on the *content_encoding* parameter.
By default, `ContentEncoding::Auto` is used. If `ContentEncoding::Auto` is selected,
then the compression depends on the request's `Accept-Encoding` header.
{{< include-example example="responses" file="compress.rs" section="compress" >}}
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.
> 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
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
{{< include-example example="responses" file="brotli.rs" section="brotli" >}}
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.content_encoding(ContentEncoding::Br)
.body("data")
}
```
or for the entire application:
In this case we explicitly disable content compression
by setting content encoding to a `Identity` value:
{{< include-example example="responses" file="brotli_two.rs" section="brotli-two" >}}
```rust
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
In this case we explicitly disable content compression by setting content encoding to
an `Identity` value:
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
// v- disable compression
.content_encoding(ContentEncoding::Identity)
.body("data")
}
```
{{< include-example example="responses" file="identity.rs" section="identity" >}}
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:
```rust
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)
}
```
{{< include-example example="responses" file="identity_two.rs" section="identity-two" >}}
Also it is possible to set default content encoding on application level, by
default `ContentEncoding::Auto` is used, which implies automatic content compression
negotiation.
```rust
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));
}
```
{{< include-example example="responses" file="auto.rs" section="auto" >}}
# 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*.
The type `T` must implement the `Serialize` trait from *serde*.
```rust
# extern crate actix_web;
#[macro_use] extern crate serde_derive;
use actix_web::{App, HttpRequest, Json, Result, http::Method};
#[derive(Serialize)]
struct MyObj {
name: String,
}
fn index(req: &HttpRequest) -> Result<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();
}
```
{{< include-example example="responses" file="json_resp.rs" section="json-resp" >}}
# Chunked transfer encoding
@ -149,14 +80,7 @@ is enabled automatically.
> Enabling chunked encoding for *HTTP/2.0* responses is forbidden.
```rust
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
{{< include-example example="responses" file="chunked.rs" section="chunked" >}}
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.chunked()
.body(Body::Streaming(Box::new(once(Ok(Bytes::from_static(b"data"))))))
}
```
[responsebuilder]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.HttpResponseBuilder.html
[compressmidddleware]: https://docs.rs/actix-web/1.0.2/actix_web/middleware/struct.Compress.html

View File

@ -6,61 +6,38 @@ weight: 1020
# Sentry Crash Reporting
[Sentry](https://sentry.io/) is a crash reporting system that supports the
failure crate which is the base of the actix error reporting. With a
middleware it's possible to automatically report server errors to Sentry.
{{% alert %}}
NOTE: Sentry currently does not work with `actix-web` 1.0. Please checkout this
[issue](https://github.com/getsentry/sentry-rust/issues/143) for more details.
{{% /alert %}}
[Sentry][sentrysite] is a crash reporting system that supports the failure crate which
is the base of the actix error reporting. With a middleware it's possible to
automatically report server errors to Sentry.
# Middleware
This middleware captures any error in the server error range (500 - 599)
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
initialized and configured and the [sentry-actix middleware](https://crates.io/crates/sentry-actix)
needs to be added. Additionally it makes sense to also register the panic handler
to be informed about hard panics.
To use the middleware the [sentry crate][sentrycrate] needs to be initialized and configured
and the [sentry-actix middleware][sentrymiddleware] needs to be added. Additionally it
makes sense to also register the panic handler to be informed about hard panics.
```rust
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())
// ...
}
```
{{< include-example example="sentry" file="main.rs" section="middleware" >}}
# Reusing the Hub
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:
```rust
use sentry::{Hub, Level};
use sentry_actix::ActixWebHubExt;
let hub = Hub::from_request(req);
hub.capture_message("Something is not well", Level::Warning);
```
{{< include-example example="sentry" file="main.rs" section="hub" >}}
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.
```rust
use sentry::{Hub, Level};
use sentry_actix::ActixWebHubExt;
{{< include-example example="sentry" file="main.rs" section="hub2" >}}
let hub = Hub::from_request(req);
Hub::run(hub, || {
sentry::capture_message("Something is not well", Level::Warning);
});
```
[sentrysite]: https://sentry.io/
[sentrycrate]: https://crates.io/crates/sentry
[sentrymiddleware]: https://crates.io/crates/sentry-actix

View File

@ -6,25 +6,17 @@ weight: 150
# The HTTP Server
The [**HttpServer**](../../actix-web/actix_web/server/struct.HttpServer.html) type is responsible for
serving http requests.
The [**HttpServer**][httpserverstruct] type is responsible for serving http requests.
`HttpServer` accepts an application factory as a parameter, and the
application factory must have `Send` + `Sync` boundaries. More about that in the
*multi-threading* section.
`HttpServer` accepts an application factory as a parameter, and the application factory
must have `Send` + `Sync` boundaries. More about that in the *multi-threading* section.
To bind to a specific socket address,
[`bind()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.bind)
must be used, and it may be called multiple times. To bind ssl socket,
[`bind_ssl()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.bind_ssl)
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.
To bind to a specific socket address, [`bind()`][bindmethod] must be used, and it may be
called multiple times. To bind ssl socket, [`bind_ssl()`][bindsslmethod] or
[`bind_rustls()`][bindrusttls] 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)
for a server
`HttpServer` is an actix actor. It must be initialized within a properly
configured actix system:
- use [`start()`] for a server
{{< include-example example="server" section="main" >}}
@ -33,8 +25,8 @@ configured actix system:
> this server, send a `StopServer` message.
`HttpServer` is implemented as an actix actor. It is possible to communicate with the server
via a messaging system. Start method, e.g. `start()`, returns the
address of the started http server. It accepts several messages:
via a messaging system. Start method, e.g. `start()`, returns the address of the started
http server. It accepts several messages:
- `PauseServer` - Pause accepting incoming connections
- `ResumeServer` - Resume accepting incoming connections
@ -44,43 +36,39 @@ address of the started http server. It accepts several messages:
## Multi-threading
`HttpServer` automatically starts a number of http workers, by default
this number is equal to number of logical CPUs in the system. This number
can be overridden with the
[`HttpServer::workers()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.workers) method.
`HttpServer` automatically starts a number of http workers, by default this number is
equal to number of logical CPUs in the system. This number can be overridden with the
[`HttpServer::workers()`][workers] method.
{{< include-example example="server" file="workers.rs" section="workers" >}}
The server creates a separate application instance for each created worker. Application state
is not shared between threads. To share state, `Arc` could be used.
> Application state does not need to be `Send` and `Sync`,
> but factories must be `Send` + `Sync`.
> Application state does not need to be `Send` and `Sync`, but factories must be `Send` + `Sync`.
## SSL
There are two features for ssl server: `tls` and `alpn`. The `tls` feature is
for `native-tls` integration and `alpn` is for `openssl`.
There are two features for ssl server: `rust-tls` and `ssl`. The `tls` feature is
for `rust-tls` integration and `ssl` is for `openssl`.
```toml
[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" >}}
> **Note**: the *HTTP/2.0* protocol requires
> [tls alpn](https://tools.ietf.org/html/rfc7301).
> **Note**: the *HTTP/2.0* protocol requires [tls alpn][tlsalpn].
> At the moment, only `openssl` has `alpn` support.
> For a full example, check out
> [examples/tls](https://github.com/actix/examples/tree/master/tls).
> For a full example, check out [examples/tls][exampletls].
To create the key.pem and cert.pem use the command. **Fill in your own subject**
```bash
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
-days 365 -sha256 -subj "/C=CN/ST=Fujian/L=Xiamen/O=TVlinux/OU=Org/CN=muro.lxd"
```
To remove the password, then copy nopass.pem to key.pem
To remove the password, then copy nopass.pem to key.pem
```bash
$ openssl rsa -in key.pem -out nopass.pem
```
@ -95,39 +83,46 @@ Actix can wait for requests on a keep-alive connection.
- `None` or `KeepAlive::Disabled` - disable *keep alive*.
- `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
calculated based on the response's *connection-type*. By default
`HttpResponse::connection_type` is not defined. In that case *keep alive* is
defined by the request's http version.
If the first option is selected, then *keep alive* state is calculated based on the
response's *connection-type*. By default `HttpResponse::connection_type` is not
defined. In that case *keep alive* is defined by the request's http version.
> *keep alive* is **off** for *HTTP/1.0* and is **on** for *HTTP/1.1* and *HTTP/2.0*.
*Connection type* can be 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
`HttpServer` supports graceful shutdown. After receiving a stop signal, workers
have a specific amount of time to finish serving requests. Any workers still alive after the
timeout are force-dropped. By default the shutdown timeout is set to 30 seconds.
You can change this parameter with the
[`HttpServer::shutdown_timeout()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.shutdown_timeout) method.
timeout are force-dropped. By default the shutdown timeout is set to 30 seconds. You
can change this parameter with the [`HttpServer::shutdown_timeout()`][shutdowntimeout]
method.
You can send a stop message to the server with the server address and specify if you want
graceful shutdown or not. The
[`start()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.start)
method returns address of the server.
graceful shutdown or not. The [`start()`][startmethod] method returns address of the server.
`HttpServer` handles several OS signals. *CTRL-C* is available on all OSs,
other signals are available on unix systems.
`HttpServer` handles several OS signals. *CTRL-C* is available on all OSs, other signals
are available on unix systems.
- *SIGINT* - Force shutdown workers
- *SIGTERM* - Graceful shutdown workers
- *SIGQUIT* - Force shutdown workers
> It is possible to disable signal handling with
> [`HttpServer::disable_signals()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.disable_signals)
> method.
[`HttpServer::disable_signals()`][disablesignals] 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
match a path tail, we can use a `[.*]` regex.
```rust
extern crate actix_web;
use std::path::PathBuf;
use actix_web::{App, HttpRequest, Result, http::Method, fs::NamedFile};
fn index(req: &HttpRequest) -> Result<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();
}
```
{{< include-example example="static-files" file="main.rs" section="individual-file" >}}
# Directory
To serve files from specific directories and sub-directories, `StaticFiles` can be used.
`StaticFiles` must be registered with an `App::handler()` method, otherwise
To serve files from specific directories and sub-directories, `Files` can be used.
`Files` must be registered with an `App::service()` method, otherwise
it will be unable to serve sub-paths.
```rust
extern crate actix_web;
use actix_web::{App, fs};
{{< include-example example="static-files" file="directory.rs" section="directory" >}}
fn main() {
App::new()
.handler(
"/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)
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
[*Files::show_files_listing()*][showfileslisting]
method.
Instead of showing files listing for directory, it is possible to redirect
to a specific index file. Use the
[*StaticFiles::index_file()*](../../actix-web/actix_web/fs/struct.StaticFiles.html#method.index_file)
method to configure this redirect.
Instead of showing files listing for directory, it is possible to redirect to a specific
index file. Use the [*Files::index_file()*][indexfile] method to configure this redirect.
# Configuration
Generic trait `StaticFileConfig` can be used to specify various options
for serving files:
`NamedFiles` can specify various options for serving files:
- `content_disposition_map` - 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.
- `is_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.
- `set_content_dispostion` - function to be used for mapping file's mime to corresponding `Content-Disposition` type
- `use_etag` - specifies whether `ETag` shall be calculated and included in headers.
- `use_last_modifier` - specifies whether file modified timestamp should be used and added to `Last-Modified` header.
All of the above methods are optional and provided with the best defaults.
But it is possible to customize any of them by implementing the trait onto own struct.
All of the above methods are optional and provided with the best defaults, But it is
possible to customize any of them.
```rust
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;
{{< include-example example="static-files" file="configuration.rs" section="config-one" >}}
use std::path::PathBuf;
The Configuration can also be applied to directory service:
#[derive(Default)]
struct MyConfig;
{{< include-example example="static-files" file="configuration_two.rs" section="config-two" >}}
impl StaticFileConfig for MyConfig {
fn content_disposition_map(typ: mime::Name) -> DispositionType {
DispositionType::Attachment
}
}
fn index(req: &HttpRequest) -> Result<NamedFile> {
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();
}
```
[showfileslisting]: https://docs.rs/actix-files/0.1.2/actix_files/struct.Files.html
[indexfile]: https://docs.rs/actix-files/0.1.2/actix_files/struct.Files.html#method.index_file

View File

@ -6,231 +6,48 @@ weight: 210
# 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.
# Unit Tests
For unit testing, actix provides a request builder type and a simple handler runner.
[*TestRequest*](../../actix-web/actix_web/test/struct.TestRequest.html)
implements a builder-like pattern.
You can generate a `HttpRequest` instance with `finish()`, or you can
run your handler with `run()` or `run_async()`.
For unit testing, actix-web provides a request builder type and a simple handler runner.
[*TestRequest*][testrequest] implements a builder-like pattern. You can generate a
`HttpRequest` instance with `to_http_request()`, or you can
run your handler with `block_on()`.
```rust
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);
}
```
{{< include-example example="testing" file="main.rs" section="unit-tests" >}}
# Integration tests
There are several methods for testing your application. Actix provides
[*TestServer*](../../actix-web/actix_web/test/struct.TestServer.html), which can be used
There a few methods for testing your application. Actix-web can be used
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.
A simple form `TestServer` can be configured to use a handler.
`TestServer::new` method accepts a configuration function, and the only argument
for this function is a *test application* instance.
To create a `Service` for testing, use the `test::init_serivce` method which accepts a
regular `App` builder.
> Check the [api documentation](../../actix-web/actix_web/test/struct.TestApp.html)
> for more information.
> Check the [api documentation][actixdocs] for more information.
```rust
use actix_web::{HttpRequest, HttpMessage};
use actix_web::test::TestServer;
use std::str;
{{< include-example example="testing" file="integration_one.rs" section="integration-one" >}}
fn index(req: HttpRequest) -> &'static str {
"Hello world!"
}
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
);
```
If you need more complex application configuration testing should be very similar to creating
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.
{{< include-example example="testing" file="integration_two.rs" section="integration-two" >}}
# 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.
For example of testing [*Server Sent Events*](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events).
If you need to test stream it would be enough to convert a [*ClientResponse*][clientresponse]
to future and execute it.
For example of testing [*Server Sent Events*][serversentevents].
```rust
extern crate bytes;
extern crate futures;
extern crate actix_web;
{{< include-example example="testing" file="stream_response.rs" section="stream-response" >}}
use bytes::Bytes;
use futures::stream::poll_fn;
use futures::{Async, Poll, Stream};
use actix_web::{HttpRequest, HttpResponse, Error};
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::NotReady);
}
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 (bytes, response) = srv.execute(response.into_future()).unwrap();
assert_eq!(bytes.unwrap(), Bytes::from("data: 5\n\n"));
// next chunk
let (bytes, _) = srv.execute(response.into_future()).unwrap();
assert_eq!(bytes.unwrap(), Bytes::from("data: 4\n\n"));
}
```
# 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_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())));
}
```
[serversentevents]: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
[clientresponse]: https://docs.rs/actix-web/1.0.2/actix_web/client/struct.ClientResponse.html
[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

View File

@ -6,112 +6,95 @@ weight: 190
# 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,
a particular handler object is invoked.
> A handler is a specific object that implements the
> `Handler` trait, defined in your application, that receives the request and returns
> a response object. More information is available in the
> [handler section](sec-4-handler.html).
> A request handler is a function that accepts zero or more parameters that can be extracted
> from a request (ie, [*impl FromRequest*][implfromrequest]) and returns a type that can
> be converted into an HttpResponse (ie, [*impl Responder*][implresponder]). More information
> is available in the [handler section][handlersection].
# Resource configuration
Resource configuration is the act of adding a new resources to an application.
A resource has a name, which acts as an identifier to be used for URL generation.
The name also allows developers to add routes to existing resources.
A resource also has a pattern, meant to match against the *PATH* portion of a *URL* (the portion following the scheme and
port, e.g. */foo/bar* in the *URL* *http://localhost:8080/foo/bar?q=value*).
It does not match against the *QUERY* portion (the portion that follows *?*, e.g. *q=value* in *http://localhost:8080/foo/bar?q=value*).
Resource configuration is the act of adding a new resources to an application. A resource
has a name, which acts as an identifier to be used for URL generation. The name also
allows developers to add routes to existing resources. A resource also has a pattern,
meant to match against the *PATH* portion of a *URL* (the portion following the scheme and
port, e.g. */foo/bar* in the *URL* *http://localhost:8080/foo/bar?q=value*). It does not
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
simple way of registering routes. This method adds a single route to application
routing table. This method accepts a *path pattern*,
The [*App::route()*][approute] method provides simple way of registering routes. This
method adds a single route to application routing table. This method accepts a *path pattern*,
*http method* and a handler function. `route()` method could be called multiple times
for the same path, in that case, multiple routes register for the same resource path.
{{< include-example example="url-dispatch" section="main" >}}
While *App::route()* provides simple way of registering routes, to access
complete resource configuration, a different method has to be used.
The [*App::resource()*](../../actix-web/actix_web/struct.App.html#method.resource) method
adds a single resource to application routing table. This method accepts a *path pattern*
and a resource configuration function.
While *App::route()* provides simple way of registering routes, to access complete resource
configuration, a different method has to be used. The [*App::service()*][appservice] method
adds a single [resource][webresource] to application routing table. This method accepts a
*path pattern*, guards, and one or more routes.
{{< 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
returns *NOT FOUND* http response.
## Configuring a Route
Resource contains a set of routes. Each route in turn has a set of predicates and a handler.
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
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`.
The application routes incoming requests based on route criteria which are defined during
resource registration and route registration. Resource matches all routes it contains in
the order the routes were registered via `Resource::route()`.
> A *Route* can contain any number of *predicates* but only one handler.
> A *Route* can contain any number of *guards* but only one handler.
{{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}}
In this example, `HttpResponse::Ok()` is returned for *GET* requests.
If a request contains `Content-Type` header, the value of this header is *text/plain*,
and path equals to `/path`, Resource calls handle of the first matching route.
In this example, `HttpResponse::Ok()` is returned for *GET* requests if the request
contains `Content-Type` header, the value of this header is *text/plain*, and path
equals to `/path`.
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
[*Route*](../../actix-web/actix_web/dev/struct.Route.html) object. Route can be configured with a
builder-like pattern. Following configuration methods are available:
[*ResourceHandler::route()*][resourcehandler] returns a [*Route*][route] object. Route
can be configured with a builder-like pattern. Following configuration methods are available:
* [*Route::filter()*](../../actix-web/actix_web/dev/struct.Route.html#method.filter)
registers a new predicate. Any number of predicates can be registered for each route.
* [*Route::f()*](../../actix-web/actix_web/dev/struct.Route.html#method.f) registers
handler function for this route. Only one handler can be registered.
Usually handler registration
is the last config operation. Handler function can be a function or closure
and has the type
`Fn(&HttpRequest<S>) -> R + 'static`
* [*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::guard()*][routeguard] registers a new guard. Any number of guards can be
registered for each route.
* [*Route::method()*][routemethod] registers a method guard. Any number of guards can be
registered for each route.
* [*Route::to()*][routeto] registers handler function for this route. Only one handler
can be registered. Usually handler registration is the last config operation.
* [*Route::to_async()*][routetoasync] registers an async handler function for this route.
Only one handler can be registered. Handler registration is the last config operation.
# Route matching
The main purpose of route configuration is to match (or not match) the request's `path`
against a URL path pattern. `path` represents the path portion of the URL that was requested.
The way that *actix* does this is very simple. When a request enters the system,
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
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.
When a route configuration is declared, it may contain route predicate arguments. All route
predicates associated with a route declaration must be `true` for the route configuration to
be used for a given request during a check. If any predicate in the set of route predicate
When a route configuration is declared, it may contain route guard arguments. All route
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 guard in the set of route guard
arguments provided to a route configuration returns `false` during a check, that route is
skipped and route matching continues through the ordered set of routes.
If any route matches, the route matching process stops and the handler associated with
the route is invoked. If no route matches after all route patterns are exhausted, a *NOT FOUND* response get returned.
the route is invoked. If no route matches after all route patterns are exhausted, a
*NOT FOUND* response get returned.
# Resource pattern syntax
@ -269,18 +252,12 @@ A *scoped* path can contain variable path segments as resources. Consistent with
unscoped paths.
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
All values representing matched path segments are available in
[`HttpRequest::match_info`](../actix_web/struct.HttpRequest.html#method.match_info).
Specific values can be retrieved with
[`Params::get()`](../actix_web/dev/struct.Params.html#method.get).
Any matched parameter can be deserialized into a specific type if the type
implements the `FromParam` trait. For example most standard integer types
the trait, i.e.:
All values representing matched path segments are available in [`HttpRequest::match_info`][matchinfo].
Specific values can be retrieved with [`Path::get()`][pathget].
{{< 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" >}}
List of `FromParam` implementations can be found in
[api docs](../../actix-web/actix_web/dev/trait.FromParam.html#foreign-impls)
## Path information extractor
Actix provides functionality for type safe path information extraction.
[*Path*](../../actix-web/actix_web/struct.Path.html) extracts information, destination type
could be defined in several different forms. Simplest approach is to use
`tuple` type. Each element in tuple must correpond to one element from
Actix provides functionality for type safe path information extraction. [*Path*][pathstruct]
extracts information, destination type could be defined in several different forms. Simplest
approach is to use `tuple` type. Each element in tuple must correpond to one element from
path pattern. i.e. you can match path pattern `/{id}/{username}/` against
`Path<(u32, String)>` type, but `Path<(String, String, String)>` type will
always fail.
`Path<(u32, String)>` type, but `Path<(String, String, String)>` type will always fail.
{{< 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" >}}
[*Query*](../../actix-web/actix_web/struct.Query.html) provides similar
functionality for request query parameters.
[*Query*][query] provides similar functionality for request query parameters.
# Generating resource URLs
Use the [*HttpRequest.url_for()*](../../actix-web/actix_web/struct.HttpRequest.html#method.url_for)
method to generate URLs based on resource patterns. For example, if you've configured a
resource with the name "foo" and the pattern "{a}/{b}/{c}", you might do this:
Use the [*HttpRequest.url_for()*][urlfor] method to generate URLs based on resource
patterns. For example, if you've configured a resource with the name "foo" and the
pattern "{a}/{b}/{c}", you might do this:
{{< 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
the current protocol and hostname implied http://example.com).
`url_for()` method returns [*Url object*](https://docs.rs/url/1.7.0/url/struct.Url.html) so you
can modify this url (add query parameters, anchor, etc).
the current protocol and hostname implied http://example.com). `url_for()` method
returns [*Url object*][urlobj] so you can modify this url (add query parameters, anchor, etc).
`url_for()` could be called only for *named* resources otherwise error get returned.
# External resources
@ -355,22 +325,14 @@ By normalizing it means:
* To add a trailing slash to the path.
* To replace multiple slashes with one.
The handler returns as soon as it finds a path that resolves
correctly. The order of normalization conditions, if all are enabled, is 1) merge, 2) both merge and append
and 3) append. If the path resolves with
at least one of those conditions, it will redirect to the new path.
If *append* is *true*, append slash when needed. If a resource is
defined with trailing slash and the request doesn't have one, it will
be appended automatically.
If *merge* is *true*, merge multiple consecutive slashes in the path into one.
This handler designed to be used as a handler for application's *default resource*.
The handler returns as soon as it finds a path that resolves correctly. The order of
normalization conditions, if all are enabled, is 1) merge, 2) both merge and append and
3) append. If the path resolves with at least one of those conditions, it will redirect
to the new path.
{{< 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,
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
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.
The `web::scope()` method allows to set a specific application scope. This scope 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:
{{< 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
*/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*,
and when the `HttpRequest.url_for()` function is called with the route name show_users,
it will generate a URL with that same path.
# Custom route predicates
# Custom route guard
You can think of a predicate as a simple function that accepts a *request* object reference
and returns *true* or *false*. Formally, a predicate is any object that implements the
[`Predicate`](../actix_web/pred/trait.Predicate.html) trait. Actix provides
several predicates, you can check
[functions section](../../actix-web/actix_web/pred/index.html#functions) of api docs.
You can think of a guard as a simple function that accepts a *request* object reference
and returns *true* or *false*. Formally, a guard is any object that implements the
[`Guard`][guardtrait] trait. Actix provides several predicates, you can check
[functions section][guardfuncs] 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.
Predicates have access to the application's state via `HttpRequest::state()`.
Also predicates can store extra information in
[request extensions](../../actix-web/actix_web/struct.HttpRequest.html#method.extensions).
Guards can not access or modify the request object, but it is possible to store extra
information in [request extensions][requestextensions].
## Modifying predicate values
## Modifying guard values
You can invert the meaning of any predicate value by wrapping it in a `Not` predicate.
For example, if you want to return "METHOD NOT ALLOWED" response for all methods
except "GET":
{{< 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
predicates match. i.e:
The `Any` guard accepts a list of guards and matches if any of the supplied
guards match. i.e:
```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
predicates match. i.e:
The `All` guard accepts a list of guard and matches if all of the supplied
guards match. i.e:
```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
If the path pattern can not be found in the routing table or a resource can not find matching
route, the default resource is used. The default response is *NOT FOUND*.
It is possible to override the *NOT FOUND* response with `App::default_resource()`.
It is possible to override the *NOT FOUND* response with `App::default_service()`.
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" >}}
[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
---
Actix supports WebSockets out-of-the-box. It is possible to convert a request's `Payload`
to a stream of [*ws::Message*](../../actix-web/actix_web/ws/enum.Message.html) with
a [*ws::WsStream*](../../actix-web/actix_web/ws/struct.WsStream.html) and then use stream
combinators to handle actual messages, but it is simpler to handle websocket communications
with an http actor.
Actix-web supports WebSockets with the `actix-web-actors` crate. It is possible to convert a
request's `Payload` to a stream of [*ws::Message*][message] with a [*web::Payload*][payload]
and then use stream combinators to handle actual messages, but it is simpler to handle
websocket communications with an http actor.
The following is an example of a simple websocket echo server:
```rust
use actix::*;
use actix_web::*;
{{< include-example example="websockets" file="main.rs" section="websockets" >}}
/// Define http actor
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/).
> A simple websocket echo server example is available in the [examples directory][examples].
> 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 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
going to work with. What `actix-web` gives you is a fun and very fast web
top of which the `actix-web` system was originally built. This is what you are most
likely going to work with. What `actix-web` gives you is a fun and very fast web
development framework.
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
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
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
distribution.
Most importantly: `actix-web` runs on Rust 1.26 or later and it works with
stable releases.
Most importantly: `actix-web` runs on Rust {{< rust-version "actix-web" >}} or later
and it works with stable releases.

View File

@ -1,7 +1,30 @@
[workspace]
members = [
"application",
"easy-form-handling",
"flexible-responders",
"getting-started",
"main-example",
"powerful-extractors",
"request-routing",
"server",
"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]
name = "application"
version = "0.7.0"
version = "1.0.0"
edition = "2018"
workspace = "../"
[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)]
extern crate actix_web;
use actix_web::{http::Method, server, App, HttpRequest, HttpResponse, Responder};
use actix_web::{web, App, HttpResponse};
mod state;
mod vh;
fn make_app() {
// <make_app>
fn index(req: &HttpRequest) -> impl Responder {
"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>
}
pub mod app;
pub mod combine;
pub mod config;
pub mod scope;
pub mod state;
pub mod vh;
#[rustfmt::skip]
// <multi>
fn main() {
make_app();
run_server();
state::test();
App::new()
.service(
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,54 @@
// <setup>
use actix_web::{http, App, HttpRequest};
use std::cell::Cell;
use actix_web::{web, App, HttpServer};
use std::sync::{Mutex};
// This struct represents state
struct AppState {
counter: Cell<usize>,
app_name: String,
}
fn index(req: &HttpRequest<AppState>) -> String {
let count = req.state().counter.get() + 1; // <- get count
req.state().counter.set(count); // <- store new count in state
fn index(data: web::Data<AppState>) -> String {
let app_name = data.app_name; // <- get app_name
format!("Request number: {}", count) // <- response with count
format!("Hello {}!", app_name) // <- response with app_name
}
// </setup>
fn make_app() {
// <make_app>
App::with_state(AppState { counter: Cell::new(0) })
.resource("/", |r| r.method(http::Method::GET).f(index))
.finish()
// </make_app>
;
// <setup_mutable>
struct AppStateWithCounter {
counter: Mutex<i32>, // <- Mutex is necessary to mutate safely across threads
}
fn start_app() {
fn _index(data: web::Data<AppStateWithCounter>) -> String {
let mut counter = data.counter.lock().unwrap(); // <- get counter's MutexGuard
*counter += 1; // <- access counter inside MutexGuard
format!("Request number: {}", counter) // <- response with count
}
// </setup_mutable>
// <make_app_mutable>
fn _main() {
let counter = web::Data::new(AppStateWithCounter { counter : Mutex::new(0)});
App::new()
.register_data(counter.clone()) // <- register the created data
.route("/", web::get().to(index));
}
// </make_app_mutable>
// <start_app>
server::new(|| {
App::with_state(AppState { counter: Cell::new(0) })
.resource("/", |r| r.method(http::Method::GET).f(index))
}).bind("127.0.0.1:8080")
pub fn main() {
HttpServer::new(|| {
App::new()
.data(AppState {
app_name: String::from("Actix-web")
})
.route("/", web::get().to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </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::{http::Method, pred, server, App, HttpRequest, HttpResponse, Responder};
use actix_web::{guard, web, App, HttpResponse, HttpServer};
// <vh>
fn main() {
let server = server::new(|| {
vec![
App::new()
.filter(pred::Host("www.rust-lang.org"))
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
App::new()
.filter(pred::Host("users.rust-lang.org"))
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
App::new().resource("/", |r| r.f(|r| HttpResponse::Ok())),
]
});
server.run();
pub fn main() {
HttpServer::new(|| {
App::new()
.service(
web::scope("/")
.guard(guard::Header("Host", "www.rust-lang.org"))
.route("", web::to(|| HttpResponse::Ok().body("www"))),
)
.service(
web::scope("/")
.guard(guard::Header("Host", "users.rust-lang.org"))
.route("", web::to(|| HttpResponse::Ok().body("user"))),
)
.route("/", web::to(|| HttpResponse::Ok()))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </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]
name = "getting-started"
version = "0.7.0"
version = "1.0.0"
edition = "2018"
workspace = "../"
[dependencies]
actix-web = "0.7"
actix-web = "1.0"

View File

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

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