mirror of
https://github.com/actix/actix-website
synced 2024-11-28 02:22:57 +01:00
Merge branch 'master' into master
This commit is contained in:
commit
a4765b0577
@ -14,7 +14,7 @@ brew update && brew install hugo
|
|||||||
Then, get the website running locally:
|
Then, get the website running locally:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/mitsuhiko/actix-website
|
git clone https://github.com/actix/actix-website.git
|
||||||
cd actix-website
|
cd actix-website
|
||||||
hugo server
|
hugo server
|
||||||
```
|
```
|
||||||
|
@ -16,4 +16,6 @@ baseURL = "https://actix.rs"
|
|||||||
|
|
||||||
[params]
|
[params]
|
||||||
actixVersion = "0.7"
|
actixVersion = "0.7"
|
||||||
actixWebVersion = "0.7"
|
actixWebVersion = "1.0"
|
||||||
|
actixWebMinRustVersion = "1.34"
|
||||||
|
actixMinRustVersion = "1.31"
|
||||||
|
@ -12,7 +12,7 @@ Here are the most important projects and the link to their github repositories
|
|||||||
and related resources:
|
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](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)
|
* [example code](https://github.com/actix/examples)
|
||||||
* [this website](https://github.com/actix/actix-website)
|
* [this website](https://github.com/actix/actix-website)
|
||||||
|
|
||||||
|
@ -12,13 +12,16 @@ weight: 10
|
|||||||
Actix is your door to developing web services with Rust and this documentation
|
Actix is your door to developing web services with Rust and this documentation
|
||||||
is going to guide you.
|
is going to guide you.
|
||||||
|
|
||||||
This documentation currently covers mostly the `actix-web` part which is the
|
This documentation currently covers mostly the `actix-web` part which is the high level
|
||||||
high level web framework build on top of the `actix` actor framework and the
|
web framework previously built on top of the `actix` actor framework and the [Tokio][tokio]
|
||||||
[Tokio](https://tokio.rs/) async IO system. This is the part that is from an
|
async IO system. This is the part that is from an API stability point of view the most stable.
|
||||||
API stability point of view the most stable.
|
|
||||||
|
|
||||||
If you haven't used actix yet it's best to start with the [getting started
|
If you haven't used `actix-web` yet it's best to start with the [getting started
|
||||||
guide](getting-started/). If you already know your ways around and you need
|
guide][gettingstarted]. If you already know your ways around and you need
|
||||||
specific information you might want to read the [actix-web API
|
specific information you might want to read the [actix-web API docs][actixwebdocs]
|
||||||
docs](https://actix.rs/api/actix-web/stable/actix_web/) (or the lower level [actix API
|
(or the lower level [actix API docs][actixdocs]).
|
||||||
docs](https://docs.rs/actix)).
|
|
||||||
|
[gettingstarted]: ./getting-started
|
||||||
|
[actixwebdocs]: https://docs.rs/actix-web
|
||||||
|
[actixdocs]: https://docs.rs/actix
|
||||||
|
[tokio]: (https://tokio.rs/)
|
||||||
|
@ -7,33 +7,32 @@ weight: 140
|
|||||||
# Writing an Application
|
# Writing an Application
|
||||||
|
|
||||||
`actix-web` provides various primitives to build web servers and applications with Rust.
|
`actix-web` provides various primitives to build web servers and applications with Rust.
|
||||||
It provides routing, middlewares, pre-processing of requests, post-processing of responses,
|
It provides routing, middlewares, pre-processing of requests, post-processing of
|
||||||
websocket protocol handling, multipart streams, etc.
|
responses, etc.
|
||||||
|
|
||||||
All actix web servers are built around the `App` instance. It is used for
|
All `actix-web` servers are built around the `App` instance. It is used for
|
||||||
registering routes for resources and middlewares. It also stores application
|
registering routes for resources and middlewares. It also stores application
|
||||||
state shared across all handlers within same application.
|
state shared across all handlers within same scope.
|
||||||
|
|
||||||
Applications act as a namespace for all routes, i.e. all routes for a specific application
|
An application's `scope` acts as a namespace for all routes, i.e. all routes for a
|
||||||
have the same url path prefix. The application prefix always contains a leading "/" slash.
|
specific application scope have the same url path prefix. The application prefix always
|
||||||
If a supplied prefix does not contain leading slash, it is automatically inserted.
|
contains a leading "/" slash. If a supplied prefix does not contain leading slash,
|
||||||
The prefix should consist of value path segments.
|
it is automatically inserted. The prefix should consist of value path segments.
|
||||||
|
|
||||||
> For an application with prefix `/app`,
|
> For an application with scope `/app`,
|
||||||
> any request with the paths `/app`, `/app/`, or `/app/test` would match;
|
> any request with the paths `/app`, `/app/`, or `/app/test` would match;
|
||||||
> however, the path `/application` would not match.
|
> however, the path `/application` would not match.
|
||||||
|
|
||||||
{{< include-example example="application" section="make_app" >}}
|
{{< include-example example="application" file="app.rs" section="setup" >}}
|
||||||
|
|
||||||
In this example, an application with the `/app` prefix and a `index.html` resource
|
In this example, an application with the `/app` prefix and a `index.html` resource
|
||||||
are created. This resource is available through the `/app/index.html` url.
|
are created. This resource is available through the `/app/index.html` url.
|
||||||
|
|
||||||
> For more information, check the
|
> For more information, check the [URL Dispatch][usingappprefix] section.
|
||||||
> [URL Dispatch](/docs/url-dispatch/index.html#using-an-application-prefix-to-compose-applications) section.
|
|
||||||
|
|
||||||
Multiple applications can be served with one server:
|
Multiple application scopes can be served with one server:
|
||||||
|
|
||||||
{{< include-example example="application" section="run_server" >}}
|
{{< include-example example="application" file="main.rs" section="multi" >}}
|
||||||
|
|
||||||
All `/app1` requests route to the first application, `/app2` to the second, and all other to the third.
|
All `/app1` requests route to the first application, `/app2` to the second, and all other to the third.
|
||||||
**Applications get matched based on registration order**. If an application with a more generic
|
**Applications get matched based on registration order**. If an application with a more generic
|
||||||
@ -43,70 +42,90 @@ as the first application, it would match all incoming requests.
|
|||||||
|
|
||||||
## State
|
## State
|
||||||
|
|
||||||
Application state is shared with all routes and resources within the same application.
|
Application state is shared with all routes and resources within the same scope. State
|
||||||
When using an http actor, state can be accessed with the `HttpRequest::state()` as read-only,
|
can be accessed with the `web::Data<State>` extractor. State is also available for route matching guards and middlewares.
|
||||||
but interior mutability with `RefCell` can be used to achieve state mutability.
|
|
||||||
State is also available for route matching predicates and middlewares.
|
|
||||||
|
|
||||||
Let's write a simple application that uses shared state. We are going to store request count
|
Let's write a simple application and store the application name in the state:
|
||||||
in the state:
|
|
||||||
|
|
||||||
{{< include-example example="application" file="state.rs" section="setup" >}}
|
{{< include-example example="application" file="state.rs" section="setup" >}}
|
||||||
|
|
||||||
When the app is initialized it needs to be passed the initial state:
|
and pass in the state when initializing the App, and start the application:
|
||||||
|
|
||||||
{{< 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:
|
|
||||||
|
|
||||||
{{< include-example example="application" file="state.rs" section="start_app" >}}
|
{{< 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 applications with different state
|
||||||
|
|
||||||
Combining multiple applications with different state is possible as well.
|
Combining multiple applications with different state is possible as well.
|
||||||
|
|
||||||
[server::new](https://docs.rs/actix-web/*/actix_web/server/fn.new.html) requires the handler to have a single type.
|
{{< include-example example="application" file="combine.rs" section="combine" >}}
|
||||||
|
|
||||||
This limitation can easily be overcome with the [App::boxed](https://docs.rs/actix-web/*/actix_web/struct.App.html#method.boxed) method, which converts an App into a boxed trait object.
|
## Using an Application Scope to Compose Applications
|
||||||
|
|
||||||
{{< include-example example="application" file="state.rs" section="combine" >}}
|
The `web::scope()` method allows to set a specific application prefix. This scope represents
|
||||||
|
a resource prefix that will be prepended to all resource patterns added by the resource
|
||||||
## Using an Application Prefix to Compose Applications
|
configuration. This can be used to help mount a set of routes at a different location
|
||||||
|
than the included callable's author intended while still maintaining the same resource names.
|
||||||
The `App::prefix()` method allows to set a specific application prefix.
|
|
||||||
This prefix represents a resource prefix that will be prepended to all resource patterns added
|
|
||||||
by the resource configuration. This can be used to help mount a set of routes at a different
|
|
||||||
location than the included callable's author intended while still maintaining the same
|
|
||||||
resource names.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="prefix.rs" section="prefix" >}}
|
{{< include-example example="application" file="scope.rs" section="scope" >}}
|
||||||
|
|
||||||
In the above example, the *show_users* route will have an effective route pattern of
|
In the above example, the *show_users* route will have an effective route pattern of
|
||||||
*/users/show* instead of */show* because the application's prefix argument will be prepended
|
*/users/show* instead of */show* because the application's scope argument will be prepended
|
||||||
to the pattern. The route will then only match if the URL path is */users/show*,
|
to the pattern. The route will then only match if the URL path is */users/show*,
|
||||||
and when the `HttpRequest.url_for()` function is called with the route name show_users,
|
and when the `HttpRequest.url_for()` function is called with the route name show_users,
|
||||||
it will generate a URL with that same path.
|
it will generate a URL with that same path.
|
||||||
|
|
||||||
## Application predicates and virtual hosting
|
## Application guards and virtual hosting
|
||||||
|
|
||||||
You can think of a predicate as a simple function that accepts a *request* object reference
|
You can think of a guard as a simple function that accepts a *request* object reference
|
||||||
and returns *true* or *false*. Formally, a predicate is any object that implements the
|
and returns *true* or *false*. Formally, a guard is any object that implements the
|
||||||
[`Predicate`](../actix_web/pred/trait.Predicate.html) trait. Actix provides
|
[`Guard`][guardtrait] trait. Actix-web provides several guards, you can check
|
||||||
several predicates, you can check
|
[functions section][guardfuncs] of api docs.
|
||||||
[functions section](../../actix-web/actix_web/pred/index.html#functions) of api docs.
|
|
||||||
|
|
||||||
Any of this predicates could be used
|
One of the provided guards is [`Header`][guardheader], it can be used as application's
|
||||||
with [`App::filter()`](../actix_web/struct.App.html#method.filter) method. One of the
|
filter based on request's header information.
|
||||||
provided predicates is [`Host`](../actix_web/pred/fn.Host.html), it can be used
|
|
||||||
as application's filter based on request's host information.
|
|
||||||
|
|
||||||
{{< include-example example="application" file="vh.rs" section="vh" >}}
|
{{< include-example example="application" file="vh.rs" section="vh" >}}
|
||||||
|
|
||||||
|
# Configure
|
||||||
|
|
||||||
|
For simplicity and reusability both `App` and `web::scope` provide the `configure` method.
|
||||||
|
This function is useful for moving parts of configuration to a different module or even
|
||||||
|
library. For example, some of the resource's configuration could be moved to different
|
||||||
|
module.
|
||||||
|
|
||||||
|
{{< include-example example="application" file="config.rs" section="config" >}}
|
||||||
|
|
||||||
|
The result of the above example would be:
|
||||||
|
|
||||||
|
```
|
||||||
|
/ -> "/"
|
||||||
|
/app -> "app"
|
||||||
|
/api/test -> "test"
|
||||||
|
```
|
||||||
|
|
||||||
|
Each `ServiceConfig` can have it's own `data`, `routes`, and `services`
|
||||||
|
|
||||||
|
[usingappprefix]: /docs/url-dispatch/index.html#using-an-application-prefix-to-compose-applications
|
||||||
|
[stateexample]: https://github.com/actix/examples/blob/master/state/src/main.rs
|
||||||
|
[guardtrait]: https://docs.rs/actix-web/1.0.2/actix_web/guard/trait.Guard.html
|
||||||
|
[guardfuncs]: https://docs.rs/actix-web/1.0.2/actix_web/guard/index.html#functions
|
||||||
|
[guardheader]: ((https://docs.rs/actix-web/1.0.2/actix_web/guard/fn.Header.html
|
||||||
|
@ -6,13 +6,11 @@ weight: 1000
|
|||||||
|
|
||||||
# Auto-Reloading Development Server
|
# Auto-Reloading Development Server
|
||||||
|
|
||||||
During development it can be very handy to have cargo automatically recompile
|
During development it can be very handy to have cargo automatically recompile the code
|
||||||
the code on change. This can be accomplished by using
|
on change. This can be accomplished by using [cargo-watch][cargowatch]. Because an
|
||||||
[cargo-watch](https://github.com/passcod/cargo-watch). Because an actix app
|
actix app will typically bind to a port for listening for incoming HTTP requests it makes
|
||||||
will typically bind to a port for listening for incoming HTTP requests it makes
|
sense to combine this with the [listenfd][listenfd] crate and the [systemfd][systemfd]
|
||||||
sense to combine this with the [listenfd](https://crates.io/crates/listenfd)
|
utility to ensure the socket is kept open while the app is compiling and reloading.
|
||||||
crate and the [systemfd](https://github.com/mitsuhiko/systemfd) utility to
|
|
||||||
ensure the socket is kept open while the app is compiling and reloading.
|
|
||||||
|
|
||||||
`systemfd` will open a socket and pass it to `cargo-watch` which will watch for
|
`systemfd` will open a socket and pass it to `cargo-watch` which will watch for
|
||||||
changes and then invoke the compiler and run your actix app. The actix app
|
changes and then invoke the compiler and run your actix app. The actix app
|
||||||
@ -30,8 +28,7 @@ cargo install systemfd cargo-watch
|
|||||||
## Code Changes
|
## Code Changes
|
||||||
|
|
||||||
Additionally you need to slightly modify your actix app so that it can pick up
|
Additionally you need to slightly modify your actix app so that it can pick up
|
||||||
an external socket opened by `systemfd`. Add the listenfd dependency to your
|
an external socket opened by `systemfd`. Add the listenfd dependency to your app:
|
||||||
app:
|
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -40,33 +37,7 @@ listenfd = "0.3"
|
|||||||
|
|
||||||
Then modify your server code to only invoke `bind` as a fallback:
|
Then modify your server code to only invoke `bind` as a fallback:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="autoreload" file="main.rs" section="autoreload" >}}
|
||||||
extern crate actix_web;
|
|
||||||
extern crate listenfd;
|
|
||||||
|
|
||||||
use listenfd::ListenFd;
|
|
||||||
use actix_web::{server, App, HttpRequest, Responder};
|
|
||||||
|
|
||||||
fn index(_req: &HttpRequest) -> impl Responder {
|
|
||||||
"Hello World!"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut listenfd = ListenFd::from_env();
|
|
||||||
let mut server = server::new(|| {
|
|
||||||
App::new()
|
|
||||||
.resource("/", |r| r.f(index))
|
|
||||||
});
|
|
||||||
|
|
||||||
server = if let Some(listener) = listenfd.take_tcp_listener(0).unwrap() {
|
|
||||||
server.listen(listener)
|
|
||||||
} else {
|
|
||||||
server.bind("127.0.0.1:3000").unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
server.run();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running the Server
|
## Running the Server
|
||||||
|
|
||||||
@ -75,3 +46,7 @@ To now run the development server invoke this command:
|
|||||||
```
|
```
|
||||||
systemfd --no-pid -s http::3000 -- cargo watch -x run
|
systemfd --no-pid -s http::3000 -- cargo watch -x run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[cargowatch]: https://github.com/passcod/cargo-watch
|
||||||
|
[listenfd]: https://crates.io/crates/listenfd
|
||||||
|
[systemfd]: https://github.com/mitsuhiko/systemfd
|
||||||
|
@ -6,6 +6,14 @@ weight: 1010
|
|||||||
|
|
||||||
# Diesel
|
# 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,
|
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.
|
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
|
We must define a sync actor and a connection that this actor will use. The same approach
|
||||||
can be used for other databases.
|
can be used for other databases.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="og_databases" file="main.rs" section="actor" >}}
|
||||||
use actix::prelude::*;
|
|
||||||
|
|
||||||
struct DbExecutor(SqliteConnection);
|
|
||||||
|
|
||||||
impl Actor for DbExecutor {
|
|
||||||
type Context = SyncContext<Self>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is the definition of our actor. Now, we must define the *create user* message and response.
|
This is the definition of our actor. Now, we must define the *create user* message and response.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="og_databases" file="main.rs" section="message" >}}
|
||||||
struct CreateUser {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Message for CreateUser {
|
|
||||||
type Result = Result<User, Error>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We can send a `CreateUser` message to the `DbExecutor` actor, and as a result, we will receive a
|
We can send a `CreateUser` message to the `DbExecutor` actor, and as a result, we will receive a
|
||||||
`User` model instance. Next, we must define the handler implementation for this message.
|
`User` model instance. Next, we must define the handler implementation for this message.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="og_databases" file="main.rs" section="handler" >}}
|
||||||
impl Handler<CreateUser> for DbExecutor {
|
|
||||||
type Result = Result<User, Error>;
|
|
||||||
|
|
||||||
fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Self::Result
|
|
||||||
{
|
|
||||||
use self::schema::users::dsl::*;
|
|
||||||
|
|
||||||
// Create insertion model
|
|
||||||
let uuid = format!("{}", uuid::Uuid::new_v4());
|
|
||||||
let new_user = models::NewUser {
|
|
||||||
id: &uuid,
|
|
||||||
name: &msg.name,
|
|
||||||
};
|
|
||||||
|
|
||||||
// normal diesel operations
|
|
||||||
diesel::insert_into(users)
|
|
||||||
.values(&new_user)
|
|
||||||
.execute(&self.0)
|
|
||||||
.expect("Error inserting person");
|
|
||||||
|
|
||||||
let mut items = users
|
|
||||||
.filter(id.eq(&uuid))
|
|
||||||
.load::<models::User>(&self.0)
|
|
||||||
.expect("Error loading person");
|
|
||||||
|
|
||||||
Ok(items.pop().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it! Now, we can use the *DbExecutor* actor from any http handler or middleware.
|
That's it! Now, we can use the *DbExecutor* actor from any http handler or middleware.
|
||||||
All we need is to start *DbExecutor* actors and store the address in a state where http handler
|
All we need is to start *DbExecutor* actors and store the address in a state where http handler
|
||||||
can access it.
|
can access it.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="og_databases" file="main.rs" section="main" >}}
|
||||||
/// This is state where we will store *DbExecutor* address.
|
|
||||||
struct State {
|
|
||||||
db: Addr<DbExecutor>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let sys = actix::System::new("diesel-example");
|
|
||||||
|
|
||||||
// Start 3 parallel db executors
|
|
||||||
let addr = SyncArbiter::start(3, || {
|
|
||||||
DbExecutor(SqliteConnection::establish("test.db").unwrap())
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start http server
|
|
||||||
HttpServer::new(move || {
|
|
||||||
App::with_state(State{db: addr.clone()})
|
|
||||||
.resource("/{name}", |r| r.method(Method::GET).a(index))})
|
|
||||||
.bind("127.0.0.1:8080").unwrap()
|
|
||||||
.start().unwrap();
|
|
||||||
|
|
||||||
println!("Started http server: 127.0.0.1:8080");
|
|
||||||
let _ = sys.run();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We will use the address in a request handler. The handle returns a future object;
|
We will use the address in a request handler. The handle returns a future object;
|
||||||
thus, we receive the message response asynchronously.
|
thus, we receive the message response asynchronously.
|
||||||
`Route::a()` must be used for async handler registration.
|
`Route::a()` must be used for async handler registration.
|
||||||
|
|
||||||
|
{{< include-example example="og_databases" file="main.rs" section="index" >}}
|
||||||
|
|
||||||
```rust
|
> A full example is available in the [examples directory][examples].
|
||||||
/// Async handler
|
|
||||||
fn index(req: &HttpRequest<State>) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
|
||||||
let name = &req.match_info()["name"];
|
|
||||||
|
|
||||||
// Send message to `DbExecutor` actor
|
|
||||||
req.state().db.send(CreateUser{name: name.to_owned()})
|
|
||||||
.from_err()
|
|
||||||
.and_then(|res| {
|
|
||||||
match res {
|
|
||||||
Ok(user) => Ok(HttpResponse::Ok().json(user)),
|
|
||||||
Err(_) => Ok(HttpResponse::InternalServerError().into())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.responder()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> A full example is available in the
|
|
||||||
> [examples directory](https://github.com/actix/examples/tree/master/diesel/).
|
|
||||||
|
|
||||||
> More information on sync actors can be found in the
|
> More information on sync actors can be found in the
|
||||||
> [actix documentation](https://docs.rs/actix/0.7.0/actix/sync/index.html).
|
> [actix documentation][actixdocs].
|
||||||
|
|
||||||
|
[examples]: https://github.com/actix/examples/tree/master/diesel/
|
||||||
|
[actixdocs]: https://docs.rs/actix/0.7.0/actix/sync/index.html
|
||||||
|
@ -6,13 +6,13 @@ weight: 180
|
|||||||
|
|
||||||
# Errors
|
# Errors
|
||||||
|
|
||||||
Actix uses its own [`actix_web::error::Error`][actixerror] type and
|
Actix-web uses its own [`actix_web::error::Error`][actixerror] type and
|
||||||
[`actix_web::error::ResponseError`][responseerror] trait for error handling
|
[`actix_web::error::ResponseError`][responseerror] trait for error handling
|
||||||
from web handlers.
|
from web handlers.
|
||||||
|
|
||||||
If a handler returns an `Error` (referring to the [general Rust trait
|
If a handler returns an `Error` (referring to the [general Rust trait
|
||||||
`std::error::Error`][stderror]) in a `Result` that also implements the
|
`std::error::Error`][stderror]) in a `Result` that also implements the
|
||||||
`ResponseError` trait, actix will render that error as an HTTP response.
|
`ResponseError` trait, actix-web will render that error as an HTTP response.
|
||||||
`ResponseError` has a single function called `error_response()` that returns
|
`ResponseError` has a single function called `error_response()` that returns
|
||||||
`HttpResponse`:
|
`HttpResponse`:
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ A `Responder` coerces compatible `Result`s into HTTP responses:
|
|||||||
impl<T: Responder, E: Into<Error>> Responder for Result<T, E>
|
impl<T: Responder, E: Into<Error>> Responder for Result<T, E>
|
||||||
```
|
```
|
||||||
|
|
||||||
`Error` in the code above is actix's error definition, and any errors that
|
`Error` in the code above is actix-web's error definition, and any errors that
|
||||||
implement `ResponseError` can be converted to one automatically.
|
implement `ResponseError` can be converted to one automatically.
|
||||||
|
|
||||||
Actix-web provides `ResponseError` implementations for some common non-actix
|
Actix-web provides `ResponseError` implementations for some common non-actix
|
||||||
@ -40,34 +40,19 @@ converted into an `HttpInternalServerError`:
|
|||||||
```rust
|
```rust
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> io::Result<fs::NamedFile> {
|
fn index(_req: HttpRequest) -> io::Result<fs::NamedFile> {
|
||||||
Ok(fs::NamedFile::open("static/index.html")?)
|
Ok(fs::NamedFile::open("static/index.html")?)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
See [the actix-web API documentation][responseerrorimpls] for a full list of
|
See [the actix-web API documentation][responseerrorimpls] for a full list of foreign
|
||||||
foreign implementations for `ResponseError`.
|
implementations for `ResponseError`.
|
||||||
|
|
||||||
## An example of a custom error response
|
## An example of a custom error response
|
||||||
|
|
||||||
Here's an example implementation for `ResponseError`:
|
Here's an example implementation for `ResponseError`:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="errors" file="main.rs" section="response-error" >}}
|
||||||
use actix_web::*;
|
|
||||||
|
|
||||||
#[derive(Fail, Debug)]
|
|
||||||
#[fail(display="my error")]
|
|
||||||
struct MyError {
|
|
||||||
name: &'static str
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use default implementation for `error_response()` method
|
|
||||||
impl error::ResponseError for MyError {}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Result<&'static str, MyError> {
|
|
||||||
Err(MyError{name: "test"})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`ResponseError` has a default implementation for `error_response()` that will
|
`ResponseError` has a default implementation for `error_response()` that will
|
||||||
render a *500* (internal server error), and that's what will happen when the
|
render a *500* (internal server error), and that's what will happen when the
|
||||||
@ -75,63 +60,19 @@ render a *500* (internal server error), and that's what will happen when the
|
|||||||
|
|
||||||
Override `error_response()` to produce more useful results:
|
Override `error_response()` to produce more useful results:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="errors" file="override_error.rs" section="override" >}}
|
||||||
#[macro_use] extern crate failure;
|
|
||||||
use actix_web::{App, HttpRequest, HttpResponse, http, error};
|
|
||||||
|
|
||||||
#[derive(Fail, Debug)]
|
|
||||||
enum MyError {
|
|
||||||
#[fail(display="internal error")]
|
|
||||||
InternalError,
|
|
||||||
#[fail(display="bad request")]
|
|
||||||
BadClientData,
|
|
||||||
#[fail(display="timeout")]
|
|
||||||
Timeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::ResponseError for MyError {
|
|
||||||
fn error_response(&self) -> HttpResponse {
|
|
||||||
match *self {
|
|
||||||
MyError::InternalError => HttpResponse::new(
|
|
||||||
http::StatusCode::INTERNAL_SERVER_ERROR),
|
|
||||||
MyError::BadClientData => HttpResponse::new(
|
|
||||||
http::StatusCode::BAD_REQUEST),
|
|
||||||
MyError::Timeout => HttpResponse::new(
|
|
||||||
http::StatusCode::GATEWAY_TIMEOUT),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Result<&'static str, MyError> {
|
|
||||||
Err(MyError::BadClientData)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Error helpers
|
# Error helpers
|
||||||
|
|
||||||
Actix provides a set of error helper functions that are useful for generating
|
Actix-web provides a set of error helper functions that are useful for generating
|
||||||
specific HTTP error codes from other errors. Here we convert `MyError`, which
|
specific HTTP error codes from other errors. Here we convert `MyError`, which
|
||||||
doesn't implement the `ResponseError` trait, to a *400* (bad request) using
|
doesn't implement the `ResponseError` trait, to a *400* (bad request) using
|
||||||
`map_err`:
|
`map_err`:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="errors" file="helpers.rs" section="helpers" >}}
|
||||||
# extern crate actix_web;
|
|
||||||
use actix_web::*;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
See the [API documentation for actix-web's `error` module][actixerror]
|
||||||
struct MyError {
|
for a full list of available error helpers.
|
||||||
name: &'static str
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Result<&'static str> {
|
|
||||||
let result: Result<&'static str, MyError> = Err(MyError{name: "test"});
|
|
||||||
|
|
||||||
Ok(result.map_err(|e| error::ErrorBadRequest(e.name))?)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
See the [API documentation for actix-web's `error` module][errorhelpers] for a
|
|
||||||
full list of available error helpers.
|
|
||||||
|
|
||||||
# Compatibility with failure
|
# Compatibility with failure
|
||||||
|
|
||||||
@ -165,27 +106,7 @@ An example of the former is that I might use failure to specify a `UserError`
|
|||||||
enum which encapsulates a `ValidationError` to return whenever a user sends bad
|
enum which encapsulates a `ValidationError` to return whenever a user sends bad
|
||||||
input:
|
input:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="errors" file="recommend_one.rs" section="recommend-one" >}}
|
||||||
#[macro_use] extern crate failure;
|
|
||||||
use actix_web::{HttpResponse, http, error};
|
|
||||||
|
|
||||||
#[derive(Fail, Debug)]
|
|
||||||
enum UserError {
|
|
||||||
#[fail(display="Validation error on field: {}", field)]
|
|
||||||
ValidationError {
|
|
||||||
field: String,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::ResponseError for UserError {
|
|
||||||
fn error_response(&self) -> HttpResponse {
|
|
||||||
match *self {
|
|
||||||
UserError::ValidationError { .. } => HttpResponse::new(
|
|
||||||
http::StatusCode::BAD_REQUEST),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This will behave exactly as intended because the error message defined with
|
This will behave exactly as intended because the error message defined with
|
||||||
`display` is written with the explicit intent to be read by a user.
|
`display` is written with the explicit intent to be read by a user.
|
||||||
@ -201,38 +122,21 @@ consumption.
|
|||||||
Here's an example that maps an internal error to a user-facing `InternalError`
|
Here's an example that maps an internal error to a user-facing `InternalError`
|
||||||
with a custom message:
|
with a custom message:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="errors" file="recommend_two.rs" section="recommend-two" >}}
|
||||||
#[macro_use] extern crate failure;
|
|
||||||
use actix_web::{App, HttpRequest, HttpResponse, http, error, fs};
|
|
||||||
|
|
||||||
#[derive(Fail, Debug)]
|
|
||||||
enum UserError {
|
|
||||||
#[fail(display="An internal error occurred. Please try again later.")]
|
|
||||||
InternalError,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::ResponseError for UserError {
|
|
||||||
fn error_response(&self) -> HttpResponse {
|
|
||||||
match *self {
|
|
||||||
UserError::InternalError => HttpResponse::new(
|
|
||||||
http::StatusCode::INTERNAL_SERVER_ERROR),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(_: &HttpRequest) -> Result<&'static str, UserError> {
|
|
||||||
fs::NamedFile::open("static/index.html").map_err(|_e| UserError::InternalError)?;
|
|
||||||
Ok("success!")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
By dividing errors into those which are user facing and those which are not, we
|
By dividing errors into those which are user facing and those which are not, we
|
||||||
can ensure that we don't accidentally expose users to errors thrown by
|
can ensure that we don't accidentally expose users to errors thrown by
|
||||||
application internals which they weren't meant to see.
|
application internals which they weren't meant to see.
|
||||||
|
|
||||||
[actixerror]: ../../actix-web/actix_web/error/struct.Error.html
|
# Error Logging
|
||||||
[errorhelpers]: ../../actix-web/actix_web/error/index.html#functions
|
|
||||||
|
This is a basic example using `middleware::Logger`:
|
||||||
|
|
||||||
|
{{< include-example example="errors" file="logging.rs" section="logging" >}}
|
||||||
|
|
||||||
|
[actixerror]: https://docs.rs/actix-web/1.0.2/actix_web/error/struct.Error.html
|
||||||
|
[errorhelpers]: https://docs.rs/actix-web/1.0.2/actix_web/trait.ResponseError.html
|
||||||
[failure]: https://github.com/rust-lang-nursery/failure
|
[failure]: https://github.com/rust-lang-nursery/failure
|
||||||
[responseerror]: ../../actix-web/actix_web/error/trait.ResponseError.html
|
[responseerror]: https://docs.rs/actix-web/1.0.2/actix_web/error/trait.ResponseError.html
|
||||||
[responseerrorimpls]: ../../actix-web/actix_web/error/trait.ResponseError.html#foreign-impls
|
[responseerrorimpls]: https://docs.rs/actix-web/1.0.2/actix_web/error/trait.ResponseError.html#foreign-impls
|
||||||
[stderror]: https://doc.rust-lang.org/std/error/trait.Error.html
|
[stderror]: https://doc.rust-lang.org/std/error/trait.Error.html
|
||||||
|
@ -6,280 +6,134 @@ weight: 170
|
|||||||
|
|
||||||
# Type-safe information extraction
|
# Type-safe information extraction
|
||||||
|
|
||||||
Actix provides facility for type-safe request information extraction. By default,
|
Actix-web provides a facility for type-safe request information access called *extractors*
|
||||||
actix provides several extractor implementations.
|
(ie, `impl FromRequest`). By default, actix-web provides several extractor implementations.
|
||||||
|
|
||||||
# Accessing Extractors
|
## Extractors Within Handler Functions
|
||||||
|
|
||||||
How you access an Extractor depends on whether you are using a handler function
|
An extractor can be accessed in a few different ways.
|
||||||
or a custom Handler type.
|
|
||||||
|
|
||||||
## Within Handler Functions
|
Option 1 - passed as a parameter to a handler function:
|
||||||
|
|
||||||
An Extractor can be passed to a handler function as a function parameter
|
{{< include-example example="extractors" file="main.rs" section="option-one" >}}
|
||||||
*or* accessed within the function by calling the ExtractorType::<...>::extract(req)
|
|
||||||
function.
|
|
||||||
```rust
|
|
||||||
|
|
||||||
// Option 1: passed as a parameter to a handler function
|
Option 2 - accessed by calling `extract()` on the Extractor
|
||||||
fn index((params, info): (Path<(String, String,)>, Json<MyInfo>)) -> HttpResponse {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
|
{{< include-example example="extractors" file="main.rs" section="option-two" >}}
|
||||||
// Option 2: accessed by calling extract() on the Extractor
|
|
||||||
|
|
||||||
use actix_web::FromRequest;
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> HttpResponse {
|
|
||||||
let params = Path::<(String, String)>::extract(req);
|
|
||||||
let info = Json::<MyInfo>::extract(req);
|
|
||||||
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Within Custom Handler Types
|
|
||||||
|
|
||||||
Like a handler function, a custom Handler type can *access* an Extractor by
|
|
||||||
calling the ExtractorType::<...>::extract(&req) function. An Extractor
|
|
||||||
*cannot* be passed as a parameter to a custom Handler type because a custom
|
|
||||||
Handler type must follow the ``handle`` function signature specified by the
|
|
||||||
Handler trait it implements.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
|
|
||||||
struct MyHandler(String);
|
|
||||||
|
|
||||||
impl<S> Handler<S> for MyHandler {
|
|
||||||
type Result = HttpResponse;
|
|
||||||
|
|
||||||
/// Handle request
|
|
||||||
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
|
|
||||||
let params = Path::<(String, String)>::extract(req);
|
|
||||||
let info = Json::<MyInfo>::extract(req);
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
HttpResponse::Ok().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
# Path
|
# Path
|
||||||
|
|
||||||
[*Path*](../../actix-web/actix_web/struct.Path.html) provides information that can
|
[*Path*][pathstruct] provides information that can be extracted from the Request's
|
||||||
be extracted from the Request's path. You can deserialize any variable
|
path. You can deserialize any variable segment from the path.
|
||||||
segment from the path.
|
|
||||||
|
|
||||||
For instance, for resource that registered for the `/users/{userid}/{friend}` path
|
For instance, for resource that registered for the `/users/{userid}/{friend}` path
|
||||||
two segments could be deserialized, `userid` and `friend`. These segments
|
two segments could be deserialized, `userid` and `friend`. These segments could be
|
||||||
could be extracted into a `tuple`, i.e. `Path<(u32, String)>` or any structure
|
extracted into a `tuple`, i.e. `Path<(u32, String)>` or any structure that implements
|
||||||
that implements the `Deserialize` trait from the *serde* crate.
|
the `Deserialize` trait from the *serde* crate.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="extractors" file="path_one.rs" section="path-one" >}}
|
||||||
use actix_web::{App, Path, Result, http};
|
|
||||||
|
|
||||||
/// extract path info from "/users/{userid}/{friend}" url
|
It is also possible to extract path information to a specific type that implements the
|
||||||
/// {userid} - - deserializes to a u32
|
`Deserialize` trait from *serde*. Here is an equivalent example that uses *serde*
|
||||||
/// {friend} - deserializes to a String
|
|
||||||
fn index(info: Path<(u32, String)>) -> Result<String> {
|
|
||||||
Ok(format!("Welcome {}! {}", info.1, info.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new().resource(
|
|
||||||
"/users/{userid}/{friend}", // <- define path parameters
|
|
||||||
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Remember! A handler function that uses extractors has to be registered using the
|
|
||||||
[*Route::with()*](../../actix-web/actix_web/dev/struct.Route.html#method.with) method.
|
|
||||||
|
|
||||||
It is also possible to extract path information to a specific type that
|
|
||||||
implements the `Deserialize` trait from *serde*. Here is an equivalent example that uses *serde*
|
|
||||||
instead of a *tuple* type.
|
instead of a *tuple* type.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="extractors" file="path_two.rs" section="path-two" >}}
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{App, Path, Result, http};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
It is also possible to `get` or `query` the request for path parameters by name:
|
||||||
struct Info {
|
|
||||||
userid: u32,
|
|
||||||
friend: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// extract path info using serde
|
{{< include-example example="extractors" file="path_three.rs" section="path-three" >}}
|
||||||
fn index(info: Path<Info>) -> Result<String> {
|
|
||||||
Ok(format!("Welcome {}!", info.friend))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new().resource(
|
|
||||||
"/users/{userid}/{friend}", // <- define path parameters
|
|
||||||
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Query
|
# Query
|
||||||
|
|
||||||
Same can be done with the request's query.
|
The [*Query*][querystruct] type provides extraction functionality for the request's
|
||||||
The [*Query*](../../actix-web/actix_web/struct.Query.html)
|
query parameters. Underneath it uses *serde_urlencoded* crate.
|
||||||
type provides extraction functionality. Underneath it uses *serde_urlencoded* crate.
|
|
||||||
|
|
||||||
```rust
|
{{< include-example example="extractors" file="query.rs" section="query" >}}
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{App, Query, http};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Info {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
// this handler get called only if the request's query contains `username` field
|
|
||||||
fn index(info: Query<Info>) -> String {
|
|
||||||
format!("Welcome {}!", info.username)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new().resource(
|
|
||||||
"/index.html",
|
|
||||||
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Json
|
# Json
|
||||||
|
|
||||||
[*Json*](../../actix-web/actix_web/struct.Json.html) allows to deserialize
|
[*Json*][jsonstruct] allows to deserialize a request body into a struct. To extract
|
||||||
a request body into a struct. To extract typed information from a request's body,
|
typed information from a request's body, the type `T` must implement the `Deserialize`
|
||||||
the type `T` must implement the `Deserialize` trait from *serde*.
|
trait from *serde*.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="extractors" file="json_one.rs" section="json-one" >}}
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{App, Json, Result, http};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Info {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// deserialize `Info` from request's body
|
|
||||||
fn index(info: Json<Info>) -> Result<String> {
|
|
||||||
Ok(format!("Welcome {}!", info.username))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new().resource(
|
|
||||||
"/index.html",
|
|
||||||
|r| r.method(http::Method::POST).with(index)); // <- use `with` extractor
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Some extractors provide a way to configure the extraction process. Json extractor
|
Some extractors provide a way to configure the extraction process. Json extractor
|
||||||
[*JsonConfig*](../../actix-web/actix_web/dev/struct.JsonConfig.html) type for configuration.
|
[*JsonConfig*][jsonconfig] type for configuration. To configure an extractor, pass it's
|
||||||
When you register a handler using `Route::with()`, it returns a configuration instance. In case of
|
configuration object to the resource's `.data()` method. In case of a *Json* extractor
|
||||||
a *Json* extractor it returns a *JsonConfig*. You can configure the maximum size of the json
|
it returns a *JsonConfig*. You can configure the maximum size of the json payload as
|
||||||
payload as well as a custom error handler function.
|
well as a custom error handler function.
|
||||||
|
|
||||||
The following example limits the size of the payload to 4kb and uses a custom error handler.
|
The following example limits the size of the payload to 4kb and uses a custom error handler.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="extractors" file="json_two.rs" section="json-two" >}}
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{App, Json, HttpResponse, Result, http, error};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Info {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// deserialize `Info` from request's body, max payload size is 4kb
|
|
||||||
fn index(info: Json<Info>) -> Result<String> {
|
|
||||||
Ok(format!("Welcome {}!", info.username))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new().resource(
|
|
||||||
"/index.html", |r| {
|
|
||||||
r.method(http::Method::POST)
|
|
||||||
.with_config(index, |cfg| {
|
|
||||||
cfg.limit(4096) // <- change json extractor configuration
|
|
||||||
cfg.error_handler(|err, req| { // <- create custom error response
|
|
||||||
error::InternalError::from_response(
|
|
||||||
err, HttpResponse::Conflict().finish()).into()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Form
|
# Form
|
||||||
|
|
||||||
At the moment only url-encoded forms are supported. The url-encoded body
|
At the moment only url-encoded forms are supported. The url-encoded body could be
|
||||||
could be extracted to a specific type. This type must implement
|
extracted to a specific type. This type must implement the `Deserialize` trait from
|
||||||
the `Deserialize` trait from the *serde* crate.
|
the *serde* crate.
|
||||||
|
|
||||||
[*FormConfig*](../../actix-web/actix_web/dev/struct.FormConfig.html) allows
|
[*FormConfig*][formconfig] allows configuring the extraction process.
|
||||||
configuring the extraction process.
|
|
||||||
|
|
||||||
```rust
|
{{< include-example example="extractors" file="form.rs" section="form" >}}
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{App, Form, Result};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct FormData {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// extract form data using serde
|
|
||||||
/// this handler gets called only if the content type is *x-www-form-urlencoded*
|
|
||||||
/// and the content of the request could be deserialized to a `FormData` struct
|
|
||||||
fn index(form: Form<FormData>) -> Result<String> {
|
|
||||||
Ok(format!("Welcome {}!", form.username))
|
|
||||||
}
|
|
||||||
# fn main() {}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Multiple extractors
|
# Multiple extractors
|
||||||
|
|
||||||
Actix provides extractor implementations for tuples (up to 10 elements)
|
Actix-web provides extractor implementations for tuples (up to 10 elements) whose
|
||||||
whose elements implement `FromRequest`.
|
elements implement `FromRequest`.
|
||||||
|
|
||||||
For example we can use a path extractor and a query extractor at the same time.
|
For example we can use a path extractor and a query extractor at the same time.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="extractors" file="multiple.rs" section="multi" >}}
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{App, Query, Path, http};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Info {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index((path, query): (Path<(u32, String)>, Query<Info>)) -> String {
|
|
||||||
format!("Welcome {}!", query.username)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new().resource(
|
|
||||||
"/users/{userid}/{friend}", // <- define path parameters
|
|
||||||
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Other
|
# Other
|
||||||
|
|
||||||
Actix also provides several other extractors:
|
Actix-web also provides several other extractors:
|
||||||
|
|
||||||
* [*State*](../../actix-web/actix_web/struct.State.html) - If you need
|
* [*Data*][datastruct] - If you need access to an application state.
|
||||||
access to an application state. This is similar to a `HttpRequest::state()`.
|
* *HttpRequest* - *HttpRequest* itself is an extractor which returns self, in case you
|
||||||
* *HttpRequest* - *HttpRequest* itself is an extractor which returns self,
|
need access to the request.
|
||||||
in case you need access to the request.
|
* *String* - You can convert a request's payload to a *String*. [*Example*][stringexample]
|
||||||
* *String* - You can convert a request's payload to a *String*.
|
|
||||||
[*Example*](../../actix-web/actix_web/trait.FromRequest.html#example-1)
|
|
||||||
is available in doc strings.
|
is available in doc strings.
|
||||||
* *bytes::Bytes* - You can convert a request's payload into *Bytes*.
|
* *bytes::Bytes* - You can convert a request's payload into *Bytes*.
|
||||||
[*Example*](../../actix-web/actix_web/trait.FromRequest.html#example)
|
[*Example*][bytesexample]
|
||||||
is available in doc strings.
|
is available in doc strings.
|
||||||
|
* *Payload* - You can access a request's payload.
|
||||||
|
[*Example*][payloadexample]
|
||||||
|
|
||||||
|
# Async Data Access
|
||||||
|
|
||||||
|
Application state is accessible from the handler with the `web::Data` extractor;
|
||||||
|
however, state is accessible as a read-only reference. If you need mutable access to state,
|
||||||
|
it must be implemented.
|
||||||
|
|
||||||
|
> **Beware**, actix creates multiple copies of the application state and the handlers,
|
||||||
|
> unique for each thread. If you run your application in several threads, actix will
|
||||||
|
> create the same amount as number of threads of application state objects and handler
|
||||||
|
> objects.
|
||||||
|
|
||||||
|
Here is an example of a handler that stores the number of processed requests:
|
||||||
|
|
||||||
|
{{< include-example example="request-handlers" file="main.rs" section="data" >}}
|
||||||
|
|
||||||
|
Although this handler will work, `self.0` will be different depending on the number of threads and
|
||||||
|
number of requests processed per thread. A proper implementation would use `Arc` and `AtomicUsize`.
|
||||||
|
|
||||||
|
{{< include-example example="request-handlers" file="handlers_arc.rs" section="arc" >}}
|
||||||
|
|
||||||
|
> Be careful with synchronization primitives like `Mutex` or `RwLock`. The `actix-web` framework
|
||||||
|
> handles requests asynchronously. By blocking thread execution, all concurrent
|
||||||
|
> request handling processes would block. If you need to share or update some state
|
||||||
|
> from multiple threads, consider using the [actix][actix] actor system.
|
||||||
|
|
||||||
|
[pathstruct]: (https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html
|
||||||
|
[querystruct]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Query.html
|
||||||
|
[jsonstruct]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Json.html
|
||||||
|
[jsonconfig]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.JsonConfig.html
|
||||||
|
[formconfig]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.FormConfig.html
|
||||||
|
[datastruct]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Data.html
|
||||||
|
[stringexample]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html#example-2
|
||||||
|
[bytesexample]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html#example-4
|
||||||
|
[payloadexample]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Payload.html
|
||||||
|
[actix]: https://actix.github.io/actix/actix/
|
||||||
|
@ -6,7 +6,7 @@ weight: 130
|
|||||||
|
|
||||||
# Getting Started
|
# Getting Started
|
||||||
|
|
||||||
Let’s write our first actix web application!
|
Let’s write our first `actix-web` application!
|
||||||
|
|
||||||
## Hello, world!
|
## 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.
|
In order to implement a web server, we first need to create a request handler.
|
||||||
|
|
||||||
A request handler is a function that accepts an `HttpRequest` instance as its only parameter
|
A request handler is a function that accepts zero or more parameters that can be
|
||||||
and returns a type that can be converted into `HttpResponse`:
|
extracted from a request (ie, `impl FromRequest`) and returns a type that can be
|
||||||
|
converted into an `HttpResponse` (ie, `impl Responder`):
|
||||||
Filename: `src/main.rs`
|
|
||||||
|
|
||||||
{{< include-example example="getting-started" section="setup" >}}
|
{{< include-example example="getting-started" section="setup" >}}
|
||||||
|
|
||||||
Next, create an `Application` instance and register the request handler with
|
Next, create an `App` instance and register the request handler with the application's
|
||||||
the application's `resource` on a particular *HTTP method* and *path* and
|
`route` on a *path* and with a particular *HTTP method*. After that, the application
|
||||||
after that, the application instance can be used with `HttpServer` to listen
|
instance can be used with `HttpServer` to listen for incoming connections. The server
|
||||||
for incoming connections. The server accepts a function that should return an
|
accepts a function that should return an application factory.
|
||||||
`HttpHandler` instance. For simplicity `server::new` could be used, this
|
|
||||||
function is shortcut for `HttpServer::new`:
|
|
||||||
|
|
||||||
{{< include-example example="getting-started" section="main" >}}
|
{{< include-example example="getting-started" section="main" >}}
|
||||||
|
|
||||||
That's it! Now, compile and run the program with `cargo run`.
|
That's it! Now, compile and run the program with `cargo run`.
|
||||||
Head over to ``http://localhost:8088/`` to see the results.
|
Head over to ``http://localhost:8088/`` to see the results.
|
||||||
|
|
||||||
If you want you can have an automatic reloading server during development
|
If you want, you can have an automatic reloading server during development
|
||||||
that recompiles on demand. To see how this can be accomplished have a look
|
that recompiles on demand. To see how this can be accomplished have a look
|
||||||
at the [autoreload pattern](../autoreload/).
|
at the [autoreload pattern][autoload].
|
||||||
|
|
||||||
|
[autoload]: ../autoreload/
|
||||||
|
@ -6,30 +6,29 @@ weight: 160
|
|||||||
|
|
||||||
# Request Handlers
|
# Request Handlers
|
||||||
|
|
||||||
A request handler can be any object that implements
|
A request handler is a function that accepts zero or more parameters that can be extracted
|
||||||
[*Handler*](../../actix-web/actix_web/dev/trait.Handler.html) trait.
|
from a request (ie, [*impl FromRequest*][implfromrequest]) and returns a type that can
|
||||||
|
be converted into an HttpResponse (ie, [*impl Responder*][implresponder]).
|
||||||
|
|
||||||
Request handling happens in two stages. First the handler object is called,
|
Request handling happens in two stages. First the handler object is called, returning any
|
||||||
returning any object that implements the
|
object that implements the [*Responder*][respondertrait] trait. Then, `respond_to()` is
|
||||||
[*Responder*](../../actix-web/actix_web/trait.Responder.html#foreign-impls) trait.
|
called on the returned object, converting itself to a `HttpResponse` or `Error`.
|
||||||
Then, `respond_to()` is called on the returned object, converting itself to a `AsyncResult` or `Error`.
|
|
||||||
|
|
||||||
By default actix provides `Responder` implementations for some standard types,
|
By default actix-web provides `Responder` implementations for some standard types,
|
||||||
such as `&'static str`, `String`, etc.
|
such as `&'static str`, `String`, etc.
|
||||||
|
|
||||||
> For a complete list of implementations, check
|
> For a complete list of implementations, check [*Responder documentation*][responderimpls].
|
||||||
> [*Responder documentation*](../../actix-web/actix_web/trait.Responder.html#foreign-impls).
|
|
||||||
|
|
||||||
Examples of valid handlers:
|
Examples of valid handlers:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn index(req: &HttpRequest) -> &'static str {
|
fn index(_req: HttpRequest) -> &'static str {
|
||||||
"Hello world!"
|
"Hello world!"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn index(req: &HttpRequest) -> String {
|
fn index(_req: HttpRequest) -> String {
|
||||||
"Hello world!".to_owned()
|
"Hello world!".to_owned()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -38,255 +37,59 @@ You can also change the signature to return `impl Responder` which works well if
|
|||||||
complex types are involved.
|
complex types are involved.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn index(req: &HttpRequest) -> impl Responder {
|
fn index(_req: HttpRequest) -> impl Responder {
|
||||||
Bytes::from_static("Hello world!")
|
Bytes::from_static(b"Hello world!")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust,ignore
|
```rust
|
||||||
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
*Handler* trait is generic over *S*, which defines the application state's type.
|
|
||||||
Application state is accessible from the handler with the `HttpRequest::state()` method;
|
|
||||||
however, state is accessible as a read-only reference. If you need mutable access to state,
|
|
||||||
it must be implemented.
|
|
||||||
|
|
||||||
> **Note**: Alternatively, the handler can use interior mutably to access its own
|
|
||||||
> state. **Beware**, actix creates multiple copies
|
|
||||||
> of the application state and the handlers, unique for each thread. If you run your
|
|
||||||
> application in several threads, actix will create the same amount as number of threads
|
|
||||||
> of application state objects and handler objects.
|
|
||||||
|
|
||||||
Here is an example of a handler that stores the number of processed requests:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use actix_web::{App, HttpRequest, HttpResponse, dev::Handler};
|
|
||||||
|
|
||||||
struct MyHandler(Cell<usize>);
|
|
||||||
|
|
||||||
impl<S> Handler<S> for MyHandler {
|
|
||||||
type Result = HttpResponse;
|
|
||||||
|
|
||||||
/// Handle request
|
|
||||||
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
|
|
||||||
let i = self.0.get();
|
|
||||||
self.0.set(i + 1);
|
|
||||||
HttpResponse::Ok().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main(){
|
|
||||||
server::new(|| App::new()
|
|
||||||
.resource("/", |r| r.h(MyHandler(Cell::new(0))))) //use r.h() to bind handler, not the r.f()
|
|
||||||
.bind("127.0.0.1:8080")
|
|
||||||
.unwrap()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Although this handler will work, `self.0` will be different depending on the number of threads and
|
|
||||||
number of requests processed per thread. A proper implementation would use `Arc` and `AtomicUsize`.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use actix_web::{server, App, HttpRequest, HttpResponse, dev::Handler};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
struct MyHandler(Arc<AtomicUsize>);
|
|
||||||
|
|
||||||
impl<S> Handler<S> for MyHandler {
|
|
||||||
type Result = HttpResponse;
|
|
||||||
|
|
||||||
/// Handle request
|
|
||||||
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
|
|
||||||
self.0.fetch_add(1, Ordering::Relaxed);
|
|
||||||
HttpResponse::Ok().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let sys = actix::System::new("example");
|
|
||||||
|
|
||||||
let inc = Arc::new(AtomicUsize::new(0));
|
|
||||||
|
|
||||||
server::new(
|
|
||||||
move || {
|
|
||||||
let cloned = inc.clone();
|
|
||||||
App::new()
|
|
||||||
.resource("/", move |r| r.h(MyHandler(cloned)))
|
|
||||||
})
|
|
||||||
.bind("127.0.0.1:8088").unwrap()
|
|
||||||
.start();
|
|
||||||
|
|
||||||
println!("Started http server: 127.0.0.1:8088");
|
|
||||||
let _ = sys.run();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Be careful with synchronization primitives like `Mutex` or `RwLock`. The `actix-web` framework
|
|
||||||
> handles requests asynchronously. By blocking thread execution, all concurrent
|
|
||||||
> request handling processes would block. If you need to share or update some state
|
|
||||||
> from multiple threads, consider using the [actix](https://actix.github.io/actix/actix/) actor system.
|
|
||||||
|
|
||||||
## Response with custom type
|
## Response with custom type
|
||||||
|
|
||||||
To return a custom type directly from a handler function, the type needs to implement the `Responder` trait.
|
To return a custom type directly from a handler function, the type needs to implement the `Responder` trait.
|
||||||
|
|
||||||
Let's create a response for a custom type that serializes to an `application/json` response:
|
Let's create a response for a custom type that serializes to an `application/json` response:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="responder-trait" file="main.rs" section="responder-trait" >}}
|
||||||
# extern crate actix;
|
|
||||||
# extern crate actix_web;
|
|
||||||
extern crate serde;
|
|
||||||
extern crate serde_json;
|
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{server, App, HttpRequest, HttpResponse, Error, Responder, http};
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct MyObj {
|
|
||||||
name: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Responder
|
|
||||||
impl Responder for MyObj {
|
|
||||||
type Item = HttpResponse;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn respond_to<S>(self, req: &HttpRequest<S>) -> Result<HttpResponse, Error> {
|
|
||||||
let body = serde_json::to_string(&self)?;
|
|
||||||
|
|
||||||
// Create response and set content type
|
|
||||||
Ok(HttpResponse::Ok()
|
|
||||||
.content_type("application/json")
|
|
||||||
.body(body))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> impl Responder {
|
|
||||||
MyObj { name: "user" }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let sys = actix::System::new("example");
|
|
||||||
|
|
||||||
server::new(
|
|
||||||
|| App::new()
|
|
||||||
.resource("/", |r| r.method(http::Method::GET).f(index)))
|
|
||||||
.bind("127.0.0.1:8088").unwrap()
|
|
||||||
.start();
|
|
||||||
|
|
||||||
println!("Started http server: 127.0.0.1:8088");
|
|
||||||
let _ = sys.run();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Async handlers
|
## Async handlers
|
||||||
|
|
||||||
There are two different types of async handlers. Response objects can be generated asynchronously
|
There are two different types of async handlers. Response objects can be generated asynchronously
|
||||||
or more precisely, any type that implements the [*Responder*](../../actix-web/actix_web/trait.Responder.html) trait.
|
or more precisely, any type that implements the [*Responder*][respondertrait] trait.
|
||||||
|
|
||||||
In this case, the handler must return a `Future` object that resolves to the *Responder* type, i.e:
|
In this case, the handler must return a `Future` object that resolves to the *Responder* type, i.e:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="async-handlers" file="main.rs" section="async-responder" >}}
|
||||||
use actix_web::*;
|
|
||||||
use bytes::Bytes;
|
|
||||||
use futures::stream::once;
|
|
||||||
use futures::future::{Future, result};
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
Or the response body can be generated asynchronously. In this case, body must implement
|
||||||
|
the stream trait `Stream<Item=Bytes, Error=Error>`, i.e:
|
||||||
|
|
||||||
result(Ok(HttpResponse::Ok()
|
{{< include-example example="async-handlers" file="stream.rs" section="stream" >}}
|
||||||
.content_type("text/html")
|
|
||||||
.body(format!("Hello!"))))
|
|
||||||
.responder()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index2(req: &HttpRequest) -> Box<Future<Item=&'static str, Error=Error>> {
|
|
||||||
result(Ok("Welcome!"))
|
|
||||||
.responder()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
.resource("/async", |r| r.route().a(index))
|
|
||||||
.resource("/", |r| r.route().a(index2))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Or the response body can be generated asynchronously. In this case, body
|
|
||||||
must implement the stream trait `Stream<Item=Bytes, Error=Error>`, i.e:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use actix_web::*;
|
|
||||||
use bytes::Bytes;
|
|
||||||
use futures::stream::once;
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> HttpResponse {
|
|
||||||
let body = once(Ok(Bytes::from_static(b"test")));
|
|
||||||
|
|
||||||
HttpResponse::Ok()
|
|
||||||
.content_type("application/json")
|
|
||||||
.body(Body::Streaming(Box::new(body)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
.resource("/async", |r| r.f(index))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Both methods can be combined. (i.e Async response with streaming body)
|
Both methods can be combined. (i.e Async response with streaming body)
|
||||||
|
|
||||||
It is possible to return a `Result` where the `Result::Item` type can be `Future`.
|
It is possible to return a `Result` where the `Result::Item` type can be `Future`. In
|
||||||
In this example, the `index` handler can return an error immediately or return a
|
this example, the `index` handler can return an error immediately or return a future
|
||||||
future that resolves to a `HttpResponse`.
|
that resolves to a `HttpResponse`.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="async-handlers" file="async_stream.rs" section="async-stream" >}}
|
||||||
use actix_web::*;
|
|
||||||
use bytes::Bytes;
|
|
||||||
use futures::stream::once;
|
|
||||||
use futures::future::{Future, result};
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Result<Box<Future<Item=HttpResponse, Error=Error>>, Error> {
|
|
||||||
if is_error() {
|
|
||||||
Err(error::ErrorBadRequest("bad request"))
|
|
||||||
} else {
|
|
||||||
Ok(Box::new(
|
|
||||||
result(Ok(HttpResponse::Ok()
|
|
||||||
.content_type("text/html")
|
|
||||||
.body(format!("Hello!"))))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Different return types (Either)
|
## Different return types (Either)
|
||||||
|
|
||||||
Sometimes, you need to return different types of responses. For example,
|
Sometimes, you need to return different types of responses. For example, you can error
|
||||||
you can error check and return errors, return async responses, or any result that requires two different types.
|
check and return errors, return async responses, or any result that requires two different types.
|
||||||
|
|
||||||
For this case, the [*Either*](../../actix-web/actix_web/enum.Either.html) type can be used.
|
For this case, the [*Either*][either] type can be used. `Either` allows combining two
|
||||||
`Either` allows combining two different responder types into a single type.
|
different responder types into a single type.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="either" file="main.rs" section="either" >}}
|
||||||
use futures::future::{Future, result};
|
|
||||||
use actix_web::{Either, Error, HttpResponse};
|
|
||||||
|
|
||||||
type RegisterResult = Either<HttpResponse, Box<Future<Item=HttpResponse, Error=Error>>>;
|
[implfromrequest]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html
|
||||||
|
[implresponder]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html
|
||||||
fn index(req: &HttpRequest) -> RegisterResult {
|
[respondertrait]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html
|
||||||
if is_a_variant() { // <- choose variant A
|
[responderimpls]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html#foreign-impls
|
||||||
Either::A(
|
[either]: https://docs.rs/actix-web/1.0.2/actix_web/enum.Either.html
|
||||||
HttpResponse::BadRequest().body("Bad data"))
|
|
||||||
} else {
|
|
||||||
Either::B( // <- variant B
|
|
||||||
result(Ok(HttpResponse::Ok()
|
|
||||||
.content_type("text/html")
|
|
||||||
.body(format!("Hello!")))).responder())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -8,43 +8,28 @@ weight: 250
|
|||||||
|
|
||||||
# Negotiation
|
# Negotiation
|
||||||
|
|
||||||
*HTTP/2.0* protocol over tls without prior knowledge requires
|
*HTTP/2.0* protocol over tls without prior knowledge requires [tls alpn][tlsalpn].
|
||||||
[tls alpn](https://tools.ietf.org/html/rfc7301).
|
|
||||||
|
|
||||||
> Currently, only `rust-openssl` has support.
|
> Currently, only `rust-openssl` has support.
|
||||||
|
|
||||||
`alpn` negotiation requires enabling the feature. When enabled, `HttpServer` provides the
|
`alpn` negotiation requires enabling the feature. When enabled, `HttpServer` provides the
|
||||||
[serve_tls](../../actix-web/actix_web/server/struct.HttpServer.html#method.serve_tls) method.
|
[bind_ssl][bindssl] method.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["alpn"] }
|
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["ssl"] }
|
||||||
openssl = { version = "0.10", features = ["v110"] }
|
openssl = { version = "0.10", features = ["v110"] }
|
||||||
```
|
```
|
||||||
|
{{< include-example example="http2" file="main.rs" section="main" >}}
|
||||||
|
|
||||||
```rust
|
Upgrades to *HTTP/2.0* schema described in [rfc section 3.2][rfcsection32] is not
|
||||||
use std::fs::File;
|
supported. Starting *HTTP/2* with prior knowledge is supported for both clear text
|
||||||
use actix_web::*;
|
connection and tls connection. [rfc section 3.4][rfcsection34].
|
||||||
use openssl::ssl::{SslMethod, SslAcceptor, SslFiletype};
|
|
||||||
|
|
||||||
fn main() {
|
> Check out [examples/tls][examples] for a concrete example.
|
||||||
// load ssl keys
|
|
||||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
|
||||||
builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
|
|
||||||
builder.set_certificate_chain_file("cert.pem").unwrap();
|
|
||||||
|
|
||||||
HttpServer::new(
|
[rfcsection32]: https://http2.github.io/http2-spec/#rfc.section.3.2
|
||||||
|| App::new()
|
[rfcsection34]: https://http2.github.io/http2-spec/#rfc.section.3.4
|
||||||
.resource("/index.html", |r| r.f(index)))
|
[bindssl]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.bind_ssl
|
||||||
.bind("127.0.0.1:8080").unwrap();
|
[tlsalpn]: https://tools.ietf.org/html/rfc7301
|
||||||
.serve_ssl(builder).unwrap();
|
[examples]: https://github.com/actix/examples/tree/master/tls
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Upgrades to *HTTP/2.0* schema described in
|
|
||||||
[rfc section 3.2](https://http2.github.io/http2-spec/#rfc.section.3.2) is not supported.
|
|
||||||
Starting *HTTP/2* with prior knowledge is supported for both clear text connection
|
|
||||||
and tls connection. [rfc section 3.4](https://http2.github.io/http2-spec/#rfc.section.3.4)
|
|
||||||
|
|
||||||
> Check out [examples/tls](https://github.com/actix/examples/tree/master/tls)
|
|
||||||
> for a concrete example.
|
|
||||||
|
@ -6,21 +6,19 @@ weight: 110
|
|||||||
|
|
||||||
# Installing Rust
|
# Installing Rust
|
||||||
|
|
||||||
Since `actix-web` is a Rust framework you will need Rust to get started with it.
|
Since `actix-web` is a Rust framework you will need Rust to get started with it. If you
|
||||||
If you don't have it yet we recommend you use `rustup` to manage your Rust
|
don't have it yet we recommend you use `rustup` to manage your Rust installation. The
|
||||||
installation. The [official rust
|
[official rust guide][rustguide] has a wonderful section on getting started.
|
||||||
guide](https://doc.rust-lang.org/book/second-edition/ch01-01-installation.html)
|
|
||||||
has a wonderful section on getting started.
|
|
||||||
|
|
||||||
We currently require at least Rust 1.24 so make sure you run `rustup update`
|
We currently require at least Rust {{< rust-version "actix-web" >}} so make sure you run
|
||||||
to have the latest and greatest Rust version available. In particular this
|
`rustup update` to have the latest and greatest Rust version available. In particular
|
||||||
guide will assume that you actually run Rust 1.26 or later.
|
this guide will assume that you actually run Rust {{< rust-version "actix-web" >}} or later.
|
||||||
|
|
||||||
# Installing `actix-web`
|
# Installing `actix-web`
|
||||||
|
|
||||||
Thank's to Rust's `cargo` package manager you won't need to explicitly install
|
Thanks to Rust's `cargo` package manager you won't need to explicitly install
|
||||||
`actix-web`. Just depend on it and you're ready to go. For the unlikely
|
`actix-web`. Just depend on it and you're ready to go. For the unlikely
|
||||||
case that you want to use the development version of actix-web you can
|
case that you want to use the development version of `actix-web` you can
|
||||||
depend on the git repository directly.
|
depend on the git repository directly.
|
||||||
|
|
||||||
Release version:
|
Release version:
|
||||||
@ -39,10 +37,9 @@ actix-web = { git = "https://github.com/actix/actix-web" }
|
|||||||
|
|
||||||
# Diving In
|
# Diving In
|
||||||
|
|
||||||
There are two paths you can take here. You can follow the guide along or if
|
There are two paths you can take here. You can follow the guide along or if you are very
|
||||||
you are very impatient you might want to have a look at our
|
impatient you might want to have a look at our [extensive example repository][examples]
|
||||||
[extensive example repository](https://github.com/actix/examples) and run the
|
and run the included examples. Here for instance is how you run the included `basics`
|
||||||
included examples. Here for instance is how you run the included `basics`
|
|
||||||
example:
|
example:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -50,3 +47,6 @@ git clone https://github.com/actix/examples
|
|||||||
cd examples/basics
|
cd examples/basics
|
||||||
cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[rustguide]: https://doc.rust-lang.org/book/ch01-01-installation.html
|
||||||
|
[examples]: https://github.com/actix/examples
|
||||||
|
@ -6,9 +6,9 @@ weight: 220
|
|||||||
|
|
||||||
# Middleware
|
# Middleware
|
||||||
|
|
||||||
Actix's middleware system allows us to add additional behavior to request/response processing.
|
Actix-web's middleware system allows us to add additional behavior to request/response
|
||||||
Middleware can hook into an incoming request process, enabling us to modify requests
|
processing. Middleware can hook into an incoming request process, enabling us to modify
|
||||||
as well as halt request processing to return a response early.
|
requests as well as halt request processing to return a response early.
|
||||||
|
|
||||||
Middleware can also hook into response processing.
|
Middleware can also hook into response processing.
|
||||||
|
|
||||||
@ -19,87 +19,38 @@ Typically, middleware is involved in the following actions:
|
|||||||
* Modify application state
|
* Modify application state
|
||||||
* Access external services (redis, logging, sessions)
|
* Access external services (redis, logging, sessions)
|
||||||
|
|
||||||
Middleware is registered for each application and executed in same order as
|
Middleware is registered for each `App`, `scope`, or `Resource` and executed in opposite
|
||||||
registration. In general, a *middleware* is a type that implements the
|
order as registration. In general, a *middleware* is a type that implements the
|
||||||
[*Middleware trait*](../../actix-web/actix_web/middleware/trait.Middleware.html).
|
[*Service trait*][servicetrait] and [*Transform trait*][transformtrait]. Each method in
|
||||||
Each method in this trait has a default implementation. Each method can return
|
the traits has a default implementation. Each method can return a result immediately
|
||||||
a result immediately or a *future* object.
|
or a *future* object.
|
||||||
|
|
||||||
The following demonstrates using middleware to add request and response headers:
|
The following demonstrates creating a simple middleware:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="middleware" file="main.rs" section="simple" >}}
|
||||||
use http::{header, HttpTryFrom};
|
|
||||||
use actix_web::{App, HttpRequest, HttpResponse, Result};
|
|
||||||
use actix_web::middleware::{Middleware, Started, Response};
|
|
||||||
|
|
||||||
struct Headers; // <- Our middleware
|
> Actix-web provides several useful middlewares, such as *logging*, *user sessions*,
|
||||||
|
> *compress*, etc.
|
||||||
/// Middleware implementation, middlewares are generic over application state,
|
|
||||||
/// so you can access state with `HttpRequest::state()` method.
|
|
||||||
impl<S> Middleware<S> for Headers {
|
|
||||||
|
|
||||||
/// Method is called when request is ready. It may return
|
|
||||||
/// future, which should resolve before next middleware get called.
|
|
||||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
|
||||||
Ok(Started::Done)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method is called when handler returns response,
|
|
||||||
/// but before sending http message to peer.
|
|
||||||
fn response(&self, req: &HttpRequest<S>, mut resp: HttpResponse)
|
|
||||||
-> Result<Response>
|
|
||||||
{
|
|
||||||
resp.headers_mut().insert(
|
|
||||||
header::HeaderName::try_from("X-VERSION").unwrap(),
|
|
||||||
header::HeaderValue::from_static("0.2"));
|
|
||||||
Ok(Response::Done(resp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
// Register middleware, this method can be called multiple times
|
|
||||||
.middleware(Headers)
|
|
||||||
.resource("/", |r| r.f(|_| HttpResponse::Ok()));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Actix provides several useful middlewares, such as *logging*, *user sessions*, etc.
|
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
|
|
||||||
Logging is implemented as a middleware.
|
Logging is implemented as a middleware. It is common to register a logging middleware
|
||||||
It is common to register a logging middleware as the first middleware for the application.
|
as the first middleware for the application. Logging middleware must be registered for
|
||||||
Logging middleware must be registered for each application.
|
each application.
|
||||||
|
|
||||||
The `Logger` middleware uses the standard log crate to log information. You should enable logger
|
The `Logger` middleware uses the standard log crate to log information. You should enable logger
|
||||||
for *actix_web* package to see access log ([env_logger](https://docs.rs/env_logger/*/env_logger/)
|
for *actix_web* package to see access log ([env_logger][envlogger] or similar).
|
||||||
or similar).
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Create `Logger` middleware with the specified `format`.
|
Create `Logger` middleware with the specified `format`. Default `Logger` can be created
|
||||||
Default `Logger` can be created with `default` method, it uses the default format:
|
with `default` method, it uses the default format:
|
||||||
|
|
||||||
```ignore
|
```ignore
|
||||||
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
|
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust
|
{{< include-example example="middleware" file="logger.rs" section="logger" >}}
|
||||||
extern crate env_logger;
|
|
||||||
use actix_web::App;
|
|
||||||
use actix_web::middleware::Logger;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
std::env::set_var("RUST_LOG", "actix_web=info");
|
|
||||||
env_logger::init();
|
|
||||||
|
|
||||||
App::new()
|
|
||||||
.middleware(Logger::default())
|
|
||||||
.middleware(Logger::new("%a %{User-Agent}i"))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The following is an example of the default logging format:
|
The following is an example of the default logging format:
|
||||||
|
|
||||||
@ -110,29 +61,18 @@ INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800]
|
|||||||
|
|
||||||
## Format
|
## Format
|
||||||
|
|
||||||
`%%` The percent sign
|
- `%%` The percent sign
|
||||||
|
- `%a` Remote IP-address (IP-address of proxy if using reverse proxy)
|
||||||
`%a` Remote IP-address (IP-address of proxy if using reverse proxy)
|
- `%t` Time when the request was started to process
|
||||||
|
- `%P` The process ID of the child that serviced the request
|
||||||
`%t` Time when the request was started to process
|
- `%r` First line of request
|
||||||
|
- `%s` Response status code
|
||||||
`%P` The process ID of the child that serviced the request
|
- `%b` Size of response in bytes, including HTTP headers
|
||||||
|
- `%T` Time taken to serve the request, in seconds with floating fraction in .06f format
|
||||||
`%r` First line of request
|
- `%D` Time taken to serve the request, in milliseconds
|
||||||
|
- `%{FOO}i` request.headers['FOO']
|
||||||
`%s` Response status code
|
- `%{FOO}o` response.headers['FOO']
|
||||||
|
- `%{FOO}e` os.environ['FOO']
|
||||||
`%b` Size of response in bytes, including HTTP headers
|
|
||||||
|
|
||||||
`%T` Time taken to serve the request, in seconds with floating fraction in .06f format
|
|
||||||
|
|
||||||
`%D` Time taken to serve the request, in milliseconds
|
|
||||||
|
|
||||||
`%{FOO}i` request.headers['FOO']
|
|
||||||
|
|
||||||
`%{FOO}o` response.headers['FOO']
|
|
||||||
|
|
||||||
`%{FOO}e` os.environ['FOO']
|
|
||||||
|
|
||||||
## Default headers
|
## Default headers
|
||||||
|
|
||||||
@ -140,79 +80,37 @@ To set default response headers, the `DefaultHeaders` middleware can be used. Th
|
|||||||
*DefaultHeaders* middleware does not set the header if response headers already contain
|
*DefaultHeaders* middleware does not set the header if response headers already contain
|
||||||
a specified header.
|
a specified header.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="middleware" file="default_headers.rs" section="default-headers" >}}
|
||||||
use actix_web::{http, middleware, App, HttpResponse};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new()
|
|
||||||
.middleware(
|
|
||||||
middleware::DefaultHeaders::new()
|
|
||||||
.header("X-Version", "0.2"))
|
|
||||||
.resource("/test", |r| {
|
|
||||||
r.method(http::Method::GET).f(|req| HttpResponse::Ok());
|
|
||||||
r.method(http::Method::HEAD).f(|req| HttpResponse::MethodNotAllowed());
|
|
||||||
})
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## User sessions
|
## User sessions
|
||||||
|
|
||||||
Actix provides a general solution for session management. The
|
Actix-web provides a general solution for session management. The
|
||||||
[**SessionStorage**](../../actix-web/actix_web/middleware/session/struct.SessionStorage.html) middleware can be
|
[**actix-session**][actixsession] middleware can be used with different backend types
|
||||||
used with different backend types to store session data in different backends.
|
to store session data in different backends.
|
||||||
|
|
||||||
> By default, only cookie session backend is implemented. Other backend implementations
|
> By default, only cookie session backend is implemented. Other backend implementations
|
||||||
> can be added.
|
> can be added.
|
||||||
|
|
||||||
[**CookieSessionBackend**](../../actix-web/actix_web/middleware/session/struct.CookieSessionBackend.html)
|
[**CookieSession**][cookiesession] uses cookies as session storage. `CookieSessionBackend`
|
||||||
uses cookies as session storage. `CookieSessionBackend` creates sessions which
|
creates sessions which are limited to storing fewer than 4000 bytes of data, as the payload
|
||||||
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a
|
must fit into a single cookie. An internal server error is generated if a session
|
||||||
single cookie. An internal server error is generated if a session contains more than 4000 bytes.
|
contains more than 4000 bytes.
|
||||||
|
|
||||||
A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor.
|
A cookie may have a security policy of *signed* or *private*. Each has a respective
|
||||||
|
`CookieSession` constructor.
|
||||||
|
|
||||||
A *signed* cookie may be viewed but not modified by the client. A *private* cookie may neither be viewed nor modified by the client.
|
A *signed* cookie may be viewed but not modified by the client. A *private* cookie may
|
||||||
|
neither be viewed nor modified by the client.
|
||||||
|
|
||||||
The constructors take a key as an argument. This is the private key for cookie session - when this value is changed, all session data is lost.
|
The constructors take a key as an argument. This is the private key for cookie session -
|
||||||
|
when this value is changed, all session data is lost.
|
||||||
|
|
||||||
In general, you create a
|
In general, you create a `SessionStorage` middleware and initialize it with specific
|
||||||
`SessionStorage` middleware and initialize it with specific backend implementation,
|
backend implementation, such as a `CookieSession`. To access session data the
|
||||||
such as a `CookieSessionBackend`. To access session data,
|
[`Session`][requestsession] extractor must be used. This method returns a
|
||||||
[*HttpRequest::session()*](../../actix-web/actix_web/middleware/session/trait.RequestSession.html#tymethod.session)
|
[*Session*][sessionobj] object, which allows us to get or set session data.
|
||||||
must be used. This method returns a
|
|
||||||
[*Session*](../../actix-web/actix_web/middleware/session/struct.Session.html) object, which allows us to get or set
|
|
||||||
session data.
|
|
||||||
|
|
||||||
```rust
|
{{< include-example example="middleware" file="user_sessions.rs" section="user-session" >}}
|
||||||
use actix_web::{server, App, HttpRequest, Result};
|
|
||||||
use actix_web::middleware::session::{RequestSession, SessionStorage, CookieSessionBackend};
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Result<&'static str> {
|
|
||||||
// access session data
|
|
||||||
if let Some(count) = req.session().get::<i32>("counter")? {
|
|
||||||
println!("SESSION value: {}", count);
|
|
||||||
req.session().set("counter", count+1)?;
|
|
||||||
} else {
|
|
||||||
req.session().set("counter", 1)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok("Welcome!")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let sys = actix::System::new("basic-example");
|
|
||||||
server::new(
|
|
||||||
|| App::new().middleware(
|
|
||||||
SessionStorage::new(
|
|
||||||
CookieSessionBackend::signed(&[0; 32])
|
|
||||||
.secure(false)
|
|
||||||
)))
|
|
||||||
.bind("127.0.0.1:59880").unwrap()
|
|
||||||
.start();
|
|
||||||
let _ = sys.run();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Error handlers
|
# Error handlers
|
||||||
|
|
||||||
@ -223,26 +121,12 @@ for a specific status code. You can modify an existing response or create a comp
|
|||||||
one. The error handler can return a response immediately or return a future that resolves
|
one. The error handler can return a response immediately or return a future that resolves
|
||||||
into a response.
|
into a response.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="middleware" file="errorhandler.rs" section="error-handler" >}}
|
||||||
use actix_web::{
|
|
||||||
App, HttpRequest, HttpResponse, Result,
|
|
||||||
http, middleware::Response, middleware::ErrorHandlers};
|
|
||||||
|
|
||||||
fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
[sessionobj]: https://docs.rs/actix-session/0.1.1/actix_session/struct.Session.html
|
||||||
let mut builder = resp.into_builder();
|
[requestsession]: https://docs.rs/actix-session/0.1.1/actix_session/struct.Session.html
|
||||||
builder.header(http::header::CONTENT_TYPE, "application/json");
|
[cookiesession]: https://docs.rs/actix-session/0.1.1/actix_session/struct.CookieSession.html
|
||||||
Ok(Response::Done(builder.into()))
|
[actixsession]: https://docs.rs/actix-session/0.1.1/actix_session/
|
||||||
}
|
[envlogger]: https://docs.rs/env_logger/*/env_logger/
|
||||||
|
[servicetrait]: https://docs.rs/actix-web/1.0.2/actix_web/dev/trait.Service.html
|
||||||
fn main() {
|
[transformtrait]: https://docs.rs/actix-web/1.0.2/actix_web/dev/trait.Transform.html
|
||||||
let app = App::new()
|
|
||||||
.middleware(
|
|
||||||
ErrorHandlers::new()
|
|
||||||
.handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500))
|
|
||||||
.resource("/test", |r| {
|
|
||||||
r.method(http::Method::GET).f(|_| HttpResponse::Ok());
|
|
||||||
r.method(http::Method::HEAD).f(|_| HttpResponse::MethodNotAllowed());
|
|
||||||
})
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -6,153 +6,58 @@ weight: 200
|
|||||||
|
|
||||||
# Content Encoding
|
# Content Encoding
|
||||||
|
|
||||||
Actix automatically *decompresses* payloads. The following codecs are supported:
|
Actix-web automatically *decompresses* payloads. The following codecs are supported:
|
||||||
|
|
||||||
* Brotli
|
* Brotli
|
||||||
|
* Chunked
|
||||||
|
* Compress
|
||||||
* Gzip
|
* Gzip
|
||||||
* Deflate
|
* Deflate
|
||||||
* Identity
|
* Identity
|
||||||
|
* Trailers
|
||||||
|
* EncodingExt
|
||||||
|
|
||||||
If request headers contain a `Content-Encoding` header, the request payload is decompressed
|
If request headers contain a `Content-Encoding` header, the request payload is decompressed
|
||||||
according to the header value. Multiple codecs are not supported,
|
according to the header value. Multiple codecs are not supported, i.e: `Content-Encoding: br, gzip`.
|
||||||
i.e: `Content-Encoding: br, gzip`.
|
|
||||||
|
|
||||||
# JSON Request
|
# JSON Request
|
||||||
|
|
||||||
There are several options for json body deserialization.
|
There are several options for json body deserialization.
|
||||||
|
|
||||||
The first option is to use *Json* extractor. First, you define a handler function
|
The first option is to use *Json* extractor. First, you define a handler function
|
||||||
that accepts `Json<T>` as a parameter, then, you use the `.with()` method for registering
|
that accepts `Json<T>` as a parameter, then, you use the `.to()` method for registering
|
||||||
this handler. It is also possible to accept arbitrary valid json object by
|
this handler. It is also possible to accept arbitrary valid json object by
|
||||||
using `serde_json::Value` as a type `T`.
|
using `serde_json::Value` as a type `T`.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="requests" file="main.rs" section="json-request" >}}
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{App, Json, Result, http};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Info {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// extract `Info` using serde
|
|
||||||
fn index(info: Json<Info>) -> Result<String> {
|
|
||||||
Ok(format!("Welcome {}!", info.username))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new().resource(
|
|
||||||
"/index.html",
|
|
||||||
|r| r.method(http::Method::POST).with(index)); // <- use `with` extractor
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Another option is to use *HttpRequest::json()*. This method returns a
|
|
||||||
[*JsonBody*](../../actix-web/actix_web/dev/struct.JsonBody.html) object which resolves into
|
|
||||||
the deserialized value.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct MyObj {
|
|
||||||
name: String,
|
|
||||||
number: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
|
||||||
req.json().from_err()
|
|
||||||
.and_then(|val: MyObj| {
|
|
||||||
println!("model: {:?}", val);
|
|
||||||
Ok(HttpResponse::Ok().json(val)) // <- send response
|
|
||||||
})
|
|
||||||
.responder()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You may also manually load the payload into memory and then deserialize it.
|
You may also manually load the payload into memory and then deserialize it.
|
||||||
|
|
||||||
In the following example, we will deserialize a *MyObj* struct. We need to load the request
|
In the following example, we will deserialize a *MyObj* struct. We need to load the request
|
||||||
body first and then deserialize the json into an object.
|
body first and then deserialize the json into an object.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="requests" file="manual.rs" section="json-manual" >}}
|
||||||
extern crate serde_json;
|
|
||||||
use futures::{Future, Stream};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
> A complete example for both options is available in [examples directory][examples].
|
||||||
struct MyObj {name: String, number: i32}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
|
||||||
// `concat2` will asynchronously read each chunk of the request body and
|
|
||||||
// return a single, concatenated, chunk
|
|
||||||
req.concat2()
|
|
||||||
// `Future::from_err` acts like `?` in that it coerces the error type from
|
|
||||||
// the future into the final error type
|
|
||||||
.from_err()
|
|
||||||
// `Future::and_then` can be used to merge an asynchronous workflow with a
|
|
||||||
// synchronous workflow
|
|
||||||
.and_then(|body| {
|
|
||||||
let obj = serde_json::from_slice::<MyObj>(&body)?;
|
|
||||||
Ok(HttpResponse::Ok().json(obj))
|
|
||||||
})
|
|
||||||
.responder()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> A complete example for both options is available in
|
|
||||||
> [examples directory](https://github.com/actix/examples/tree/master/json/).
|
|
||||||
|
|
||||||
# Chunked transfer encoding
|
# Chunked transfer encoding
|
||||||
|
|
||||||
Actix automatically decodes *chunked* encoding. `HttpRequest::payload()` already contains
|
Actix automatically decodes *chunked* encoding. The [`web::Payload`][payloadextractor]
|
||||||
the decoded byte stream. If the request payload is compressed with one of the supported
|
extractor already contains the decoded byte stream. If the request payload is compressed
|
||||||
compression codecs (br, gzip, deflate), then the byte stream is decompressed.
|
with one of the supported compression codecs (br, gzip, deflate), then the byte stream
|
||||||
|
is decompressed.
|
||||||
|
|
||||||
# Multipart body
|
# Multipart body
|
||||||
|
|
||||||
Actix provides multipart stream support.
|
Actix-web provides multipart stream support with an external crate, [`actix-multipart`][multipartcrate].
|
||||||
[*Multipart*](../../actix-web/actix_web/multipart/struct.Multipart.html) is implemented as
|
|
||||||
a stream of multipart items. Each item can be a
|
|
||||||
[*Field*](../../actix-web/actix_web/multipart/struct.Field.html) or a nested
|
|
||||||
*Multipart* stream.`HttpResponse::multipart()` returns the *Multipart* stream
|
|
||||||
for the current request.
|
|
||||||
|
|
||||||
The following demonstrates multipart stream handling for a simple form:
|
> A full example is available in the [examples directory][multipartexample].
|
||||||
|
|
||||||
```rust
|
|
||||||
use actix_web::*;
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Box<Future<...>> {
|
|
||||||
// get multipart and iterate over multipart items
|
|
||||||
req.multipart()
|
|
||||||
.and_then(|item| {
|
|
||||||
match item {
|
|
||||||
multipart::MultipartItem::Field(field) => {
|
|
||||||
println!("==== FIELD ==== {:?} {:?}",
|
|
||||||
field.headers(),
|
|
||||||
field.content_type());
|
|
||||||
Either::A(
|
|
||||||
field.map(|chunk| {
|
|
||||||
println!("-- CHUNK: \n{}",
|
|
||||||
std::str::from_utf8(&chunk).unwrap());})
|
|
||||||
.fold((), |_, _| result(Ok(()))))
|
|
||||||
},
|
|
||||||
multipart::MultipartItem::Nested(mp) => {
|
|
||||||
Either::B(result(Ok(())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> A full example is available in the
|
|
||||||
> [examples directory](https://github.com/actix/examples/tree/master/multipart/).
|
|
||||||
|
|
||||||
# Urlencoded body
|
# Urlencoded body
|
||||||
|
|
||||||
Actix provides support for *application/x-www-form-urlencoded* encoded bodies.
|
Actix-web provides support for *application/x-www-form-urlencoded* encoded bodies with
|
||||||
`HttpResponse::urlencoded()` returns a
|
the [`web::Form`][formencoded] extractor which resolves to the deserialized instance. The
|
||||||
[*UrlEncoded*](../../actix-web/actix_web/dev/struct.UrlEncoded.html) future, which resolves
|
type of the instance must implement the `Deserialize` trait from *serde*.
|
||||||
to the deserialized instance. The type of the instance must implement the
|
|
||||||
`Deserialize` trait from *serde*.
|
|
||||||
|
|
||||||
The *UrlEncoded* future can resolve into an error in several cases:
|
The *UrlEncoded* future can resolve into an error in several cases:
|
||||||
|
|
||||||
@ -161,27 +66,7 @@ The *UrlEncoded* future can resolve into an error in several cases:
|
|||||||
* content-length is greater than 256k
|
* content-length is greater than 256k
|
||||||
* payload terminates with error.
|
* payload terminates with error.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="requests" file="urlencoded.rs" section="urlencoded" >}}
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::*;
|
|
||||||
use futures::future::{Future, ok};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct FormData {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
|
||||||
req.urlencoded::<FormData>() // <- get UrlEncoded future
|
|
||||||
.from_err()
|
|
||||||
.and_then(|data| { // <- deserialized instance
|
|
||||||
println!("USERNAME: {:?}", data.username);
|
|
||||||
ok(HttpResponse::Ok().into())
|
|
||||||
})
|
|
||||||
.responder()
|
|
||||||
}
|
|
||||||
# fn main() {}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Streaming request
|
# Streaming request
|
||||||
|
|
||||||
@ -190,20 +75,13 @@ body payload.
|
|||||||
|
|
||||||
In the following example, we read and print the request payload chunk by chunk:
|
In the following example, we read and print the request payload chunk by chunk:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="requests" file="streaming.rs" section="streaming" >}}
|
||||||
use actix_web::*;
|
|
||||||
use futures::{Future, Stream};
|
|
||||||
|
|
||||||
|
[examples]: https://github.com/actix/examples/tree/master/json/
|
||||||
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
[multipartstruct]: https://docs.rs/actix-multipart/0.1.2/actix_multipart/struct.Multipart.html
|
||||||
req
|
[fieldstruct]: https://docs.rs/actix-multipart/0.1.2/actix_multipart/struct.Field.html
|
||||||
.payload()
|
[multipartexample]: https://github.com/actix/examples/tree/master/multipart/
|
||||||
.from_err()
|
[urlencoded]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.UrlEncoded.html
|
||||||
.fold((), |_, chunk| {
|
[payloadextractor]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Payload.html
|
||||||
println!("Chunk: {:?}", chunk);
|
[multipartcrate]: https://crates.io/crates/actix-multipart
|
||||||
result::<_, error::PayloadError>(Ok(()))
|
[formencoded]:Jhttps://docs.rs/actix-web/1.0.2/actix_web/web/struct.Form.html
|
||||||
})
|
|
||||||
.map(|_| HttpResponse::Ok().finish())
|
|
||||||
.responder()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -6,112 +6,62 @@ weight: 210
|
|||||||
|
|
||||||
# Response
|
# Response
|
||||||
|
|
||||||
A builder-like pattern is used to construct an instance of `HttpResponse`.
|
A builder-like pattern is used to construct an instance of `HttpResponse`. `HttpResponse`
|
||||||
`HttpResponse` provides several methods that return a `HttpResponseBuilder` instance,
|
provides several methods that return a `HttpResponseBuilder` instance, which implements
|
||||||
which implements various convenience methods for building responses.
|
various convenience methods for building responses.
|
||||||
|
|
||||||
> Check the [documentation](../../actix-web/actix_web/dev/struct.HttpResponseBuilder.html)
|
> Check the [documentation][responsebuilder] for type descriptions.
|
||||||
> for type descriptions.
|
|
||||||
|
|
||||||
The methods `.body`, `.finish`, and `.json` finalize response creation and
|
The methods `.body`, `.finish`, and `.json` finalize response creation and return a
|
||||||
return a constructed *HttpResponse* instance. If this methods is called on the same
|
constructed *HttpResponse* instance. If this methods is called on the same builder
|
||||||
builder instance multiple times, the builder will panic.
|
instance multiple times, the builder will panic.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="responses" file="main.rs" section="builder" >}}
|
||||||
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> HttpResponse {
|
|
||||||
HttpResponse::Ok()
|
|
||||||
.content_encoding(ContentEncoding::Br)
|
|
||||||
.content_type("plain/text")
|
|
||||||
.header("X-Hdr", "sample")
|
|
||||||
.body("data")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Content encoding
|
# Content encoding
|
||||||
|
|
||||||
Actix automatically *compresses* payloads. The following codecs are supported:
|
Actix-web can automatically *compresses* payloads with the [*Compress middleware*][compressmidddleware].
|
||||||
|
The following codecs are supported:
|
||||||
|
|
||||||
* Brotli
|
* Brotli
|
||||||
* Gzip
|
* Gzip
|
||||||
* Deflate
|
* Deflate
|
||||||
* Identity
|
* Identity
|
||||||
|
|
||||||
Response payload is compressed based on the *content_encoding* parameter.
|
{{< include-example example="responses" file="compress.rs" section="compress" >}}
|
||||||
By default, `ContentEncoding::Auto` is used. If `ContentEncoding::Auto` is selected,
|
|
||||||
then the compression depends on the request's `Accept-Encoding` header.
|
Response payload is compressed based on the *encoding* parameter from the
|
||||||
|
`middleware::BodyEncoding` trait. By default, `ContentEncoding::Auto` is used. If
|
||||||
|
`ContentEncoding::Auto` is selected, then the compression depends on the request's
|
||||||
|
`Accept-Encoding` header.
|
||||||
|
|
||||||
> `ContentEncoding::Identity` can be used to disable compression.
|
> `ContentEncoding::Identity` can be used to disable compression.
|
||||||
> If another content encoding is selected, the compression is enforced for that codec.
|
> If another content encoding is selected, the compression is enforced for that codec.
|
||||||
|
|
||||||
For example, to enable `brotli` use `ContentEncoding::Br`:
|
For example, to enable `brotli` for a single handler use `ContentEncoding::Br`:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="responses" file="brotli.rs" section="brotli" >}}
|
||||||
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
|
|
||||||
|
|
||||||
fn index(req: HttpRequest) -> HttpResponse {
|
or for the entire application:
|
||||||
HttpResponse::Ok()
|
|
||||||
.content_encoding(ContentEncoding::Br)
|
|
||||||
.body("data")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In this case we explicitly disable content compression
|
{{< include-example example="responses" file="brotli_two.rs" section="brotli-two" >}}
|
||||||
by setting content encoding to a `Identity` value:
|
|
||||||
|
|
||||||
```rust
|
In this case we explicitly disable content compression by setting content encoding to
|
||||||
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
|
an `Identity` value:
|
||||||
|
|
||||||
fn index(req: HttpRequest) -> HttpResponse {
|
{{< include-example example="responses" file="identity.rs" section="identity" >}}
|
||||||
HttpResponse::Ok()
|
|
||||||
// v- disable compression
|
|
||||||
.content_encoding(ContentEncoding::Identity)
|
|
||||||
.body("data")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When dealing with an already compressed body (for example when serving assets),
|
When dealing with an already compressed body (for example when serving assets),
|
||||||
set the content encoding to `Identity` to avoid compressing the already compressed
|
set the content encoding to `Identity` to avoid compressing the already compressed
|
||||||
data and set the `content-encoding` header manually:
|
data and set the `content-encoding` header manually:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="responses" file="identity_two.rs" section="identity-two" >}}
|
||||||
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
|
|
||||||
|
|
||||||
static HELLO_WORLD: &[u8] = &[
|
|
||||||
0x1f,0x8b,0x08,0x00,0xa2,0x30,0x10,0x5c,
|
|
||||||
0x00,0x03,0xcb,0x48,0xcd,0xc9,0xc9,0x57,
|
|
||||||
0x28,0xcf,0x2f,0xca,0x49,0xe1,0x02,0x00,
|
|
||||||
0x2d,0x3b,0x08,0xaf,0x0c,0x00,0x00,0x00
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn index(req: HttpRequest) -> HttpResponse {
|
|
||||||
HttpResponse::Ok()
|
|
||||||
.content_encoding(ContentEncoding::Identity)
|
|
||||||
.header("content-encoding", "gzip")
|
|
||||||
.body(HELLO_WORLD)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Also it is possible to set default content encoding on application level, by
|
Also it is possible to set default content encoding on application level, by
|
||||||
default `ContentEncoding::Auto` is used, which implies automatic content compression
|
default `ContentEncoding::Auto` is used, which implies automatic content compression
|
||||||
negotiation.
|
negotiation.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="responses" file="auto.rs" section="auto" >}}
|
||||||
use actix_web::{App, HttpRequest, HttpResponse, http::ContentEncoding};
|
|
||||||
|
|
||||||
fn index(req: HttpRequest) -> HttpResponse {
|
|
||||||
HttpResponse::Ok()
|
|
||||||
.body("data")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let app = App::new()
|
|
||||||
// v- disable compression for all routes
|
|
||||||
.default_encoding(ContentEncoding::Identity)
|
|
||||||
.resource("/index.html", |r| r.with(index));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# JSON Response
|
# JSON Response
|
||||||
|
|
||||||
@ -119,26 +69,7 @@ The `Json` type allows to respond with well-formed JSON data: simply return a va
|
|||||||
type Json<T> where `T` is the type of a structure to serialize into *JSON*.
|
type Json<T> where `T` is the type of a structure to serialize into *JSON*.
|
||||||
The type `T` must implement the `Serialize` trait from *serde*.
|
The type `T` must implement the `Serialize` trait from *serde*.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="responses" file="json_resp.rs" section="json-resp" >}}
|
||||||
# extern crate actix_web;
|
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
use actix_web::{App, HttpRequest, Json, Result, http::Method};
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct MyObj {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Result<Json<MyObj>> {
|
|
||||||
Ok(Json(MyObj{name: req.match_info().query("name")?}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
.resource(r"/a/{name}", |r| r.method(Method::GET).f(index))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Chunked transfer encoding
|
# Chunked transfer encoding
|
||||||
|
|
||||||
@ -149,14 +80,7 @@ is enabled automatically.
|
|||||||
|
|
||||||
> Enabling chunked encoding for *HTTP/2.0* responses is forbidden.
|
> Enabling chunked encoding for *HTTP/2.0* responses is forbidden.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="responses" file="chunked.rs" section="chunked" >}}
|
||||||
use actix_web::*;
|
|
||||||
use bytes::Bytes;
|
|
||||||
use futures::stream::once;
|
|
||||||
|
|
||||||
fn index(req: HttpRequest) -> HttpResponse {
|
[responsebuilder]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.HttpResponseBuilder.html
|
||||||
HttpResponse::Ok()
|
[compressmidddleware]: https://docs.rs/actix-web/1.0.2/actix_web/middleware/struct.Compress.html
|
||||||
.chunked()
|
|
||||||
.body(Body::Streaming(Box::new(once(Ok(Bytes::from_static(b"data"))))))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -6,61 +6,38 @@ weight: 1020
|
|||||||
|
|
||||||
# Sentry Crash Reporting
|
# Sentry Crash Reporting
|
||||||
|
|
||||||
[Sentry](https://sentry.io/) is a crash reporting system that supports the
|
{{% alert %}}
|
||||||
failure crate which is the base of the actix error reporting. With a
|
NOTE: Sentry currently does not work with `actix-web` 1.0. Please checkout this
|
||||||
middleware it's possible to automatically report server errors to Sentry.
|
[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
|
# Middleware
|
||||||
|
|
||||||
This middleware captures any error in the server error range (500 - 599)
|
This middleware captures any error in the server error range (500 - 599)
|
||||||
and sends the attached error to sentry with its stacktrace.
|
and sends the attached error to sentry with its stacktrace.
|
||||||
|
|
||||||
To use the middleware the [sentry crate](https://crates.io/crates/sentry) needs to be
|
To use the middleware the [sentry crate][sentrycrate] needs to be initialized and configured
|
||||||
initialized and configured and the [sentry-actix middleware](https://crates.io/crates/sentry-actix)
|
and the [sentry-actix middleware][sentrymiddleware] needs to be added. Additionally it
|
||||||
needs to be added. Additionally it makes sense to also register the panic handler
|
makes sense to also register the panic handler to be informed about hard panics.
|
||||||
to be informed about hard panics.
|
|
||||||
|
|
||||||
```rust
|
{{< include-example example="sentry" file="main.rs" section="middleware" >}}
|
||||||
extern crate sentry;
|
|
||||||
extern crate sentry_actix;
|
|
||||||
|
|
||||||
use sentry_actix::SentryMiddleware;
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
sentry::init("SENTRY_DSN_GOES_HERE");
|
|
||||||
env::set_var("RUST_BACKTRACE", "1");
|
|
||||||
sentry::integrations::panic::register_panic_handler();
|
|
||||||
|
|
||||||
let mut app = App::with_state(state)
|
|
||||||
.middleware(SentryMiddleware::new())
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Reusing the Hub
|
# Reusing the Hub
|
||||||
|
|
||||||
If you use this integration the default sentry hub (`Hub::current()`) is typically the wrong one.
|
If you use this integration the default sentry hub (`Hub::current()`) is typically the wrong one.
|
||||||
To get the request specific one you need to use the `ActixWebHubExt` trait:
|
To get the request specific one you need to use the `ActixWebHubExt` trait:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="sentry" file="main.rs" section="hub" >}}
|
||||||
use sentry::{Hub, Level};
|
|
||||||
use sentry_actix::ActixWebHubExt;
|
|
||||||
|
|
||||||
let hub = Hub::from_request(req);
|
|
||||||
hub.capture_message("Something is not well", Level::Warning);
|
|
||||||
```
|
|
||||||
|
|
||||||
The hub can also be made current for the duration of a call. Then `Hub::current()` works correctly
|
The hub can also be made current for the duration of a call. Then `Hub::current()` works correctly
|
||||||
until the end of the `run` block.
|
until the end of the `run` block.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="sentry" file="main.rs" section="hub2" >}}
|
||||||
use sentry::{Hub, Level};
|
|
||||||
use sentry_actix::ActixWebHubExt;
|
|
||||||
|
|
||||||
let hub = Hub::from_request(req);
|
[sentrysite]: https://sentry.io/
|
||||||
Hub::run(hub, || {
|
[sentrycrate]: https://crates.io/crates/sentry
|
||||||
sentry::capture_message("Something is not well", Level::Warning);
|
[sentrymiddleware]: https://crates.io/crates/sentry-actix
|
||||||
});
|
|
||||||
```
|
|
||||||
|
@ -6,25 +6,17 @@ weight: 150
|
|||||||
|
|
||||||
# The HTTP Server
|
# The HTTP Server
|
||||||
|
|
||||||
The [**HttpServer**](../../actix-web/actix_web/server/struct.HttpServer.html) type is responsible for
|
The [**HttpServer**][httpserverstruct] type is responsible for serving http requests.
|
||||||
serving http requests.
|
|
||||||
|
|
||||||
`HttpServer` accepts an application factory as a parameter, and the
|
`HttpServer` accepts an application factory as a parameter, and the application factory
|
||||||
application factory must have `Send` + `Sync` boundaries. More about that in the
|
must have `Send` + `Sync` boundaries. More about that in the *multi-threading* section.
|
||||||
*multi-threading* section.
|
|
||||||
|
|
||||||
To bind to a specific socket address,
|
To bind to a specific socket address, [`bind()`][bindmethod] must be used, and it may be
|
||||||
[`bind()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.bind)
|
called multiple times. To bind ssl socket, [`bind_ssl()`][bindsslmethod] or
|
||||||
must be used, and it may be called multiple times. To bind ssl socket,
|
[`bind_rustls()`][bindrusttls] should be used. To start the http server, use one of the
|
||||||
[`bind_ssl()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.bind_ssl)
|
start methods.
|
||||||
or [`bind_tls()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.bind_tls)
|
|
||||||
should be used. To start the http server, use one of the start methods.
|
|
||||||
|
|
||||||
- use [`start()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.start)
|
- use [`start()`] for a server
|
||||||
for a server
|
|
||||||
|
|
||||||
`HttpServer` is an actix actor. It must be initialized within a properly
|
|
||||||
configured actix system:
|
|
||||||
|
|
||||||
{{< include-example example="server" section="main" >}}
|
{{< include-example example="server" section="main" >}}
|
||||||
|
|
||||||
@ -33,8 +25,8 @@ configured actix system:
|
|||||||
> this server, send a `StopServer` message.
|
> this server, send a `StopServer` message.
|
||||||
|
|
||||||
`HttpServer` is implemented as an actix actor. It is possible to communicate with the server
|
`HttpServer` is implemented as an actix actor. It is possible to communicate with the server
|
||||||
via a messaging system. Start method, e.g. `start()`, returns the
|
via a messaging system. Start method, e.g. `start()`, returns the address of the started
|
||||||
address of the started http server. It accepts several messages:
|
http server. It accepts several messages:
|
||||||
|
|
||||||
- `PauseServer` - Pause accepting incoming connections
|
- `PauseServer` - Pause accepting incoming connections
|
||||||
- `ResumeServer` - Resume accepting incoming connections
|
- `ResumeServer` - Resume accepting incoming connections
|
||||||
@ -44,36 +36,32 @@ address of the started http server. It accepts several messages:
|
|||||||
|
|
||||||
## Multi-threading
|
## Multi-threading
|
||||||
|
|
||||||
`HttpServer` automatically starts a number of http workers, by default
|
`HttpServer` automatically starts a number of http workers, by default this number is
|
||||||
this number is equal to number of logical CPUs in the system. This number
|
equal to number of logical CPUs in the system. This number can be overridden with the
|
||||||
can be overridden with the
|
[`HttpServer::workers()`][workers] method.
|
||||||
[`HttpServer::workers()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.workers) method.
|
|
||||||
|
|
||||||
{{< include-example example="server" file="workers.rs" section="workers" >}}
|
{{< include-example example="server" file="workers.rs" section="workers" >}}
|
||||||
|
|
||||||
The server creates a separate application instance for each created worker. Application state
|
The server creates a separate application instance for each created worker. Application state
|
||||||
is not shared between threads. To share state, `Arc` could be used.
|
is not shared between threads. To share state, `Arc` could be used.
|
||||||
|
|
||||||
> Application state does not need to be `Send` and `Sync`,
|
> Application state does not need to be `Send` and `Sync`, but factories must be `Send` + `Sync`.
|
||||||
> but factories must be `Send` + `Sync`.
|
|
||||||
|
|
||||||
## SSL
|
## SSL
|
||||||
|
|
||||||
There are two features for ssl server: `tls` and `alpn`. The `tls` feature is
|
There are two features for ssl server: `rust-tls` and `ssl`. The `tls` feature is
|
||||||
for `native-tls` integration and `alpn` is for `openssl`.
|
for `rust-tls` integration and `ssl` is for `openssl`.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["alpn"] }
|
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["ssl"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
{{< include-example example="server" file="ssl.rs" section="ssl" >}}
|
{{< include-example example="server" file="ssl.rs" section="ssl" >}}
|
||||||
|
|
||||||
> **Note**: the *HTTP/2.0* protocol requires
|
> **Note**: the *HTTP/2.0* protocol requires [tls alpn][tlsalpn].
|
||||||
> [tls alpn](https://tools.ietf.org/html/rfc7301).
|
|
||||||
> At the moment, only `openssl` has `alpn` support.
|
> At the moment, only `openssl` has `alpn` support.
|
||||||
> For a full example, check out
|
> For a full example, check out [examples/tls][exampletls].
|
||||||
> [examples/tls](https://github.com/actix/examples/tree/master/tls).
|
|
||||||
|
|
||||||
To create the key.pem and cert.pem use the command. **Fill in your own subject**
|
To create the key.pem and cert.pem use the command. **Fill in your own subject**
|
||||||
```bash
|
```bash
|
||||||
@ -95,39 +83,46 @@ Actix can wait for requests on a keep-alive connection.
|
|||||||
- `None` or `KeepAlive::Disabled` - disable *keep alive*.
|
- `None` or `KeepAlive::Disabled` - disable *keep alive*.
|
||||||
- `KeepAlive::Tcp(75)` - use `SO_KEEPALIVE` socket option.
|
- `KeepAlive::Tcp(75)` - use `SO_KEEPALIVE` socket option.
|
||||||
|
|
||||||
{{< include-example example="server" file="ka.rs" section="ka" >}}
|
{{< include-example example="server" file="keep_alive.rs" section="keep-alive" >}}
|
||||||
|
|
||||||
If the first option is selected, then *keep alive* state is
|
If the first option is selected, then *keep alive* state is calculated based on the
|
||||||
calculated based on the response's *connection-type*. By default
|
response's *connection-type*. By default `HttpResponse::connection_type` is not
|
||||||
`HttpResponse::connection_type` is not defined. In that case *keep alive* is
|
defined. In that case *keep alive* is defined by the request's http version.
|
||||||
defined by the request's http version.
|
|
||||||
|
|
||||||
> *keep alive* is **off** for *HTTP/1.0* and is **on** for *HTTP/1.1* and *HTTP/2.0*.
|
> *keep alive* is **off** for *HTTP/1.0* and is **on** for *HTTP/1.1* and *HTTP/2.0*.
|
||||||
|
|
||||||
*Connection type* can be changed with `HttpResponseBuilder::connection_type()` method.
|
*Connection type* can be changed with `HttpResponseBuilder::connection_type()` method.
|
||||||
|
|
||||||
{{< include-example example="server" file="ka_tp.rs" section="example" >}}
|
{{< include-example example="server" file="keep_alive_tp.rs" section="example" >}}
|
||||||
|
|
||||||
## Graceful shutdown
|
## Graceful shutdown
|
||||||
|
|
||||||
`HttpServer` supports graceful shutdown. After receiving a stop signal, workers
|
`HttpServer` supports graceful shutdown. After receiving a stop signal, workers
|
||||||
have a specific amount of time to finish serving requests. Any workers still alive after the
|
have a specific amount of time to finish serving requests. Any workers still alive after the
|
||||||
timeout are force-dropped. By default the shutdown timeout is set to 30 seconds.
|
timeout are force-dropped. By default the shutdown timeout is set to 30 seconds. You
|
||||||
You can change this parameter with the
|
can change this parameter with the [`HttpServer::shutdown_timeout()`][shutdowntimeout]
|
||||||
[`HttpServer::shutdown_timeout()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.shutdown_timeout) method.
|
method.
|
||||||
|
|
||||||
You can send a stop message to the server with the server address and specify if you want
|
You can send a stop message to the server with the server address and specify if you want
|
||||||
graceful shutdown or not. The
|
graceful shutdown or not. The [`start()`][startmethod] method returns address of the server.
|
||||||
[`start()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.start)
|
|
||||||
method returns address of the server.
|
|
||||||
|
|
||||||
`HttpServer` handles several OS signals. *CTRL-C* is available on all OSs,
|
`HttpServer` handles several OS signals. *CTRL-C* is available on all OSs, other signals
|
||||||
other signals are available on unix systems.
|
are available on unix systems.
|
||||||
|
|
||||||
- *SIGINT* - Force shutdown workers
|
- *SIGINT* - Force shutdown workers
|
||||||
- *SIGTERM* - Graceful shutdown workers
|
- *SIGTERM* - Graceful shutdown workers
|
||||||
- *SIGQUIT* - Force shutdown workers
|
- *SIGQUIT* - Force shutdown workers
|
||||||
|
|
||||||
> It is possible to disable signal handling with
|
> It is possible to disable signal handling with
|
||||||
> [`HttpServer::disable_signals()`](../../actix-web/actix_web/server/struct.HttpServer.html#method.disable_signals)
|
[`HttpServer::disable_signals()`][disablesignals] method.
|
||||||
> method.
|
|
||||||
|
[httpserverstruct]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html
|
||||||
|
[bindmethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.bind
|
||||||
|
[bindsslmethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.bind_ssl
|
||||||
|
[bindrusttls]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.bind_rustls
|
||||||
|
[startmethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.start
|
||||||
|
[workers]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.workers
|
||||||
|
[tlsalpn]: https://tools.ietf.org/html/rfc7301
|
||||||
|
[exampletls]: https://github.com/actix/examples/tree/master/tls
|
||||||
|
[shutdowntimeout]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.shutdown_timeout
|
||||||
|
[disablesignals]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpServer.html#method.disable_signals
|
||||||
|
@ -9,125 +9,40 @@ weight: 230
|
|||||||
It is possible to serve static files with a custom path pattern and `NamedFile`. To
|
It is possible to serve static files with a custom path pattern and `NamedFile`. To
|
||||||
match a path tail, we can use a `[.*]` regex.
|
match a path tail, we can use a `[.*]` regex.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="static-files" file="main.rs" section="individual-file" >}}
|
||||||
extern crate actix_web;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use actix_web::{App, HttpRequest, Result, http::Method, fs::NamedFile};
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Result<NamedFile> {
|
|
||||||
let path: PathBuf = req.match_info().query("tail")?;
|
|
||||||
Ok(NamedFile::open(path)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
.resource(r"/a/{tail:.*}", |r| r.method(Method::GET).f(index))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Directory
|
# Directory
|
||||||
|
|
||||||
To serve files from specific directories and sub-directories, `StaticFiles` can be used.
|
To serve files from specific directories and sub-directories, `Files` can be used.
|
||||||
`StaticFiles` must be registered with an `App::handler()` method, otherwise
|
`Files` must be registered with an `App::service()` method, otherwise
|
||||||
it will be unable to serve sub-paths.
|
it will be unable to serve sub-paths.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="static-files" file="directory.rs" section="directory" >}}
|
||||||
extern crate actix_web;
|
|
||||||
use actix_web::{App, fs};
|
|
||||||
|
|
||||||
fn main() {
|
By default files listing for sub-directories is disabled. Attempt to load directory
|
||||||
App::new()
|
listing will return *404 Not Found* response. To enable files listing, use
|
||||||
.handler(
|
[*Files::show_files_listing()*][showfileslisting]
|
||||||
"/static",
|
|
||||||
fs::StaticFiles::new(".")
|
|
||||||
.unwrap()
|
|
||||||
.show_files_listing())
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The parameter is the base directory. By default files listing for sub-directories
|
|
||||||
is disabled. Attempt to load directory listing will return *404 Not Found* response.
|
|
||||||
To enable files listing, use
|
|
||||||
[*StaticFiles::show_files_listing()*](../../actix-web/actix_web/fs/struct.StaticFiles.html#method.show_files_listing)
|
|
||||||
method.
|
method.
|
||||||
|
|
||||||
Instead of showing files listing for directory, it is possible to redirect
|
Instead of showing files listing for directory, it is possible to redirect to a specific
|
||||||
to a specific index file. Use the
|
index file. Use the [*Files::index_file()*][indexfile] method to configure this redirect.
|
||||||
[*StaticFiles::index_file()*](../../actix-web/actix_web/fs/struct.StaticFiles.html#method.index_file)
|
|
||||||
method to configure this redirect.
|
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
Generic trait `StaticFileConfig` can be used to specify various options
|
`NamedFiles` can specify various options for serving files:
|
||||||
for serving files:
|
|
||||||
|
|
||||||
- `content_disposition_map` - function to be used for mapping file's mime to corresponding `Content-Disposition` type
|
- `set_content_dispostion` - function to be used for mapping file's mime to corresponding `Content-Disposition` type
|
||||||
- `is_use_etag` - specifies whether `ETag` shall be calculated and included in headers.
|
- `use_etag` - specifies whether `ETag` shall be calculated and included in headers.
|
||||||
- `is_use_last_modifier` - specifies whether file modified timestamp should be used and added to `Last-Modified` header.
|
- `use_last_modifier` - specifies whether file modified timestamp should be used and added to `Last-Modified` header.
|
||||||
- `is_method_allowed` - allows to control which HTTP methods are allowed to be used when accessing file.
|
|
||||||
|
|
||||||
All of the above methods are optional and provided with the best defaults.
|
All of the above methods are optional and provided with the best defaults, But it is
|
||||||
But it is possible to customize any of them by implementing the trait onto own struct.
|
possible to customize any of them.
|
||||||
|
|
||||||
```rust
|
{{< include-example example="static-files" file="configuration.rs" section="config-one" >}}
|
||||||
extern crate mime;
|
|
||||||
extern crate actix_web;
|
|
||||||
use actix_web::{App, HttpRequest, Result, http::Method};
|
|
||||||
use actix_web::fs::{StaticFileConfig, NamedFile};
|
|
||||||
use actix_web::http::header::DispositionType;
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
The Configuration can also be applied to directory service:
|
||||||
|
|
||||||
#[derive(Default)]
|
{{< include-example example="static-files" file="configuration_two.rs" section="config-two" >}}
|
||||||
struct MyConfig;
|
|
||||||
|
|
||||||
impl StaticFileConfig for MyConfig {
|
[showfileslisting]: https://docs.rs/actix-files/0.1.2/actix_files/struct.Files.html
|
||||||
fn content_disposition_map(typ: mime::Name) -> DispositionType {
|
[indexfile]: https://docs.rs/actix-files/0.1.2/actix_files/struct.Files.html#method.index_file
|
||||||
DispositionType::Attachment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> Result<NamedFile> {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -6,231 +6,48 @@ weight: 210
|
|||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|
||||||
Every application should be well tested. Actix provides tools to perform unit and
|
Every application should be well tested. Actix-web provides tools to perform unit and
|
||||||
integration tests.
|
integration tests.
|
||||||
|
|
||||||
# Unit Tests
|
# Unit Tests
|
||||||
|
|
||||||
For unit testing, actix provides a request builder type and a simple handler runner.
|
For unit testing, actix-web provides a request builder type and a simple handler runner.
|
||||||
[*TestRequest*](../../actix-web/actix_web/test/struct.TestRequest.html)
|
[*TestRequest*][testrequest] implements a builder-like pattern. You can generate a
|
||||||
implements a builder-like pattern.
|
`HttpRequest` instance with `to_http_request()`, or you can
|
||||||
You can generate a `HttpRequest` instance with `finish()`, or you can
|
run your handler with `block_on()`.
|
||||||
run your handler with `run()` or `run_async()`.
|
|
||||||
|
|
||||||
```rust
|
{{< include-example example="testing" file="main.rs" section="unit-tests" >}}
|
||||||
use actix_web::{http, test, HttpRequest, HttpResponse, HttpMessage};
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> HttpResponse {
|
|
||||||
if let Some(hdr) = req.headers().get(http::header::CONTENT_TYPE) {
|
|
||||||
if let Ok(s) = hdr.to_str() {
|
|
||||||
return HttpResponse::Ok().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HttpResponse::BadRequest().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let resp = test::TestRequest::with_header("content-type", "text/plain")
|
|
||||||
.run(&index)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(resp.status(), http::StatusCode::OK);
|
|
||||||
|
|
||||||
let resp = test::TestRequest::default()
|
|
||||||
.run(&index)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(resp.status(), http::StatusCode::BAD_REQUEST);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Integration tests
|
# Integration tests
|
||||||
|
|
||||||
There are several methods for testing your application. Actix provides
|
There a few methods for testing your application. Actix-web can be used
|
||||||
[*TestServer*](../../actix-web/actix_web/test/struct.TestServer.html), which can be used
|
|
||||||
to run the application with specific handlers in a real http server.
|
to run the application with specific handlers in a real http server.
|
||||||
|
|
||||||
`TestServer::get()`, `TestServer::post()`, and `TestServer::client()`
|
`TestRequest::get()`, `TestRequest::post()`, and `TestRequest::client()`
|
||||||
methods can be used to send requests to the test server.
|
methods can be used to send requests to the test server.
|
||||||
|
|
||||||
A simple form `TestServer` can be configured to use a handler.
|
To create a `Service` for testing, use the `test::init_serivce` method which accepts a
|
||||||
`TestServer::new` method accepts a configuration function, and the only argument
|
regular `App` builder.
|
||||||
for this function is a *test application* instance.
|
|
||||||
|
|
||||||
> Check the [api documentation](../../actix-web/actix_web/test/struct.TestApp.html)
|
> Check the [api documentation][actixdocs] for more information.
|
||||||
> for more information.
|
|
||||||
|
|
||||||
```rust
|
{{< include-example example="testing" file="integration_one.rs" section="integration-one" >}}
|
||||||
use actix_web::{HttpRequest, HttpMessage};
|
|
||||||
use actix_web::test::TestServer;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
fn index(req: HttpRequest) -> &'static str {
|
If you need more complex application configuration testing should be very similar to creating
|
||||||
"Hello world!"
|
the normal application. For example, you may need to initialize application state. Create an
|
||||||
}
|
`App` with a `data` method and attach state just like you would from a normal application.
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// start new test server
|
|
||||||
let mut srv = TestServer::new(|app| app.handler(index));
|
|
||||||
|
|
||||||
let request = srv.get().finish().unwrap();
|
|
||||||
let response = srv.execute(request.send()).unwrap();
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
|
|
||||||
let bytes = srv.execute(response.body()).unwrap();
|
|
||||||
let body = str::from_utf8(&bytes).unwrap();
|
|
||||||
assert_eq!(body, "Hello world!");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The other option is to use an application factory. In this case, you need to pass the factory
|
|
||||||
function the same way as you would for real http server configuration.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use actix_web::{http, test, App, HttpRequest, HttpResponse};
|
|
||||||
|
|
||||||
fn index(req: &HttpRequest) -> HttpResponse {
|
|
||||||
HttpResponse::Ok().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function get called by http server.
|
|
||||||
fn create_app() -> App {
|
|
||||||
App::new()
|
|
||||||
.resource("/test", |r| r.h(index))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut srv = test::TestServer::with_factory(create_app);
|
|
||||||
|
|
||||||
let request = srv.client(
|
|
||||||
http::Method::GET, "/test").finish().unwrap();
|
|
||||||
let response = srv.execute(request.send()).unwrap();
|
|
||||||
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you need more complex application configuration, use the `TestServer::build_with_state()`
|
|
||||||
method. For example, you may need to initialize application state or start `SyncActor`'s for diesel
|
|
||||||
interation. This method accepts a closure that constructs the application state,
|
|
||||||
and it runs when the actix system is configured. Thus, you can initialize any additional actors.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[test]
|
|
||||||
fn test() {
|
|
||||||
let srv = TestServer::build_with_state(|| {
|
|
||||||
// we can start diesel actors
|
|
||||||
let addr = SyncArbiter::start(3, || {
|
|
||||||
DbExecutor(SqliteConnection::establish("test.db").unwrap())
|
|
||||||
});
|
|
||||||
// then we can construct custom state, or it could be `()`
|
|
||||||
MyState{addr: addr}
|
|
||||||
})
|
|
||||||
|
|
||||||
// register server handlers and start test server
|
|
||||||
.start(|app| {
|
|
||||||
app.resource(
|
|
||||||
"/{username}/index.html", |r| r.with(
|
|
||||||
|p: Path<PParam>| format!("Welcome {}!", p.username)));
|
|
||||||
});
|
|
||||||
|
|
||||||
// now we can run our test code
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
{{< include-example example="testing" file="integration_two.rs" section="integration-two" >}}
|
||||||
|
|
||||||
# Stream response tests
|
# Stream response tests
|
||||||
|
|
||||||
If you need to test stream it would be enough to convert a [*ClientResponse*](../../actix-web/actix_web/client/struct.ClientResponse.html) to future and execute it.
|
If you need to test stream it would be enough to convert a [*ClientResponse*][clientresponse]
|
||||||
For example of testing [*Server Sent Events*](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events).
|
to future and execute it.
|
||||||
|
For example of testing [*Server Sent Events*][serversentevents].
|
||||||
|
|
||||||
```rust
|
{{< include-example example="testing" file="stream_response.rs" section="stream-response" >}}
|
||||||
extern crate bytes;
|
|
||||||
extern crate futures;
|
|
||||||
extern crate actix_web;
|
|
||||||
|
|
||||||
use bytes::Bytes;
|
[serversentevents]: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
|
||||||
use futures::stream::poll_fn;
|
[clientresponse]: https://docs.rs/actix-web/1.0.2/actix_web/client/struct.ClientResponse.html
|
||||||
use futures::{Async, Poll, Stream};
|
[actixdocs]: (https://docs.rs/actix-web/1.0.2/actix_web/test/index.html)
|
||||||
|
[testrequest]: https://docs.rs/actix-web/1.0.2/actix_web/error/trait.ResponseError.html#foreign-impls
|
||||||
use actix_web::{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())));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -6,112 +6,95 @@ weight: 190
|
|||||||
|
|
||||||
# URL Dispatch
|
# URL Dispatch
|
||||||
|
|
||||||
URL dispatch provides a simple way for mapping URLs to `Handler` code using a simple pattern
|
URL dispatch provides a simple way for mapping URLs to handler code using a simple pattern
|
||||||
matching language. If one of the patterns matches the path information associated with a request,
|
matching language. If one of the patterns matches the path information associated with a request,
|
||||||
a particular handler object is invoked.
|
a particular handler object is invoked.
|
||||||
|
|
||||||
> A handler is a specific object that implements the
|
> A request handler is a function that accepts zero or more parameters that can be extracted
|
||||||
> `Handler` trait, defined in your application, that receives the request and returns
|
> from a request (ie, [*impl FromRequest*][implfromrequest]) and returns a type that can
|
||||||
> a response object. More information is available in the
|
> be converted into an HttpResponse (ie, [*impl Responder*][implresponder]). More information
|
||||||
> [handler section](sec-4-handler.html).
|
> is available in the [handler section][handlersection].
|
||||||
|
|
||||||
# Resource configuration
|
# Resource configuration
|
||||||
|
|
||||||
Resource configuration is the act of adding a new resources to an application.
|
Resource configuration is the act of adding a new resources to an application. A resource
|
||||||
A resource has a name, which acts as an identifier to be used for URL generation.
|
has a name, which acts as an identifier to be used for URL generation. The name also
|
||||||
The name also allows developers to add routes to existing resources.
|
allows developers to add routes to existing resources. A resource also has a pattern,
|
||||||
A resource also has a pattern, meant to match against the *PATH* portion of a *URL* (the portion following the scheme and
|
meant to match against the *PATH* portion of a *URL* (the portion following the scheme and
|
||||||
port, e.g. */foo/bar* in the *URL* *http://localhost:8080/foo/bar?q=value*).
|
port, e.g. */foo/bar* in the *URL* *http://localhost:8080/foo/bar?q=value*). It does not
|
||||||
It does not match against the *QUERY* portion (the portion that follows *?*, e.g. *q=value* in *http://localhost:8080/foo/bar?q=value*).
|
match against the *QUERY* portion (the portion that follows *?*, e.g. *q=value*
|
||||||
|
in *http://localhost:8080/foo/bar?q=value*).
|
||||||
|
|
||||||
The [*App::route()*](../../actix-web/actix_web/struct.App.html#method.route) method provides
|
The [*App::route()*][approute] method provides simple way of registering routes. This
|
||||||
simple way of registering routes. This method adds a single route to application
|
method adds a single route to application routing table. This method accepts a *path pattern*,
|
||||||
routing table. This method accepts a *path pattern*,
|
|
||||||
*http method* and a handler function. `route()` method could be called multiple times
|
*http method* and a handler function. `route()` method could be called multiple times
|
||||||
for the same path, in that case, multiple routes register for the same resource path.
|
for the same path, in that case, multiple routes register for the same resource path.
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" section="main" >}}
|
{{< include-example example="url-dispatch" section="main" >}}
|
||||||
|
|
||||||
While *App::route()* provides simple way of registering routes, to access
|
While *App::route()* provides simple way of registering routes, to access complete resource
|
||||||
complete resource configuration, a different method has to be used.
|
configuration, a different method has to be used. The [*App::service()*][appservice] method
|
||||||
The [*App::resource()*](../../actix-web/actix_web/struct.App.html#method.resource) method
|
adds a single [resource][webresource] to application routing table. This method accepts a
|
||||||
adds a single resource to application routing table. This method accepts a *path pattern*
|
*path pattern*, guards, and one or more routes.
|
||||||
and a resource configuration function.
|
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="resource.rs" section="resource" >}}
|
{{< include-example example="url-dispatch" file="resource.rs" section="resource" >}}
|
||||||
|
|
||||||
The *Configuration function* has the following type:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
FnOnce(&mut Resource<_>) -> ()
|
|
||||||
```
|
|
||||||
|
|
||||||
The *Configuration function* can set a name and register specific routes.
|
|
||||||
If a resource does not contain any route or does not have any matching routes, it
|
If a resource does not contain any route or does not have any matching routes, it
|
||||||
returns *NOT FOUND* http response.
|
returns *NOT FOUND* http response.
|
||||||
|
|
||||||
## Configuring a Route
|
## Configuring a Route
|
||||||
|
|
||||||
Resource contains a set of routes. Each route in turn has a set of predicates and a handler.
|
Resource contains a set of routes. Each route in turn has a set of `guards` and a handler.
|
||||||
New routes can be created with `Resource::route()` method which returns a reference
|
New routes can be created with `Resource::route()` method which returns a reference
|
||||||
to new *Route* instance. By default the *route* does not contain any predicates, so matches
|
to new *Route* instance. By default the *route* does not contain any guards, so matches
|
||||||
all requests and the default handler is `HttpNotFound`.
|
all requests and the default handler is `HttpNotFound`.
|
||||||
|
|
||||||
The application routes incoming requests based on route criteria which are defined during
|
The application routes incoming requests based on route criteria which are defined during
|
||||||
resource registration and route registration. Resource matches all routes it contains in
|
resource registration and route registration. Resource matches all routes it contains in
|
||||||
the order the routes were registered via `Resource::route()`.
|
the order the routes were registered via `Resource::route()`.
|
||||||
|
|
||||||
> A *Route* can contain any number of *predicates* but only one handler.
|
> A *Route* can contain any number of *guards* but only one handler.
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}}
|
{{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}}
|
||||||
|
|
||||||
In this example, `HttpResponse::Ok()` is returned for *GET* requests.
|
In this example, `HttpResponse::Ok()` is returned for *GET* requests if the request
|
||||||
If a request contains `Content-Type` header, the value of this header is *text/plain*,
|
contains `Content-Type` header, the value of this header is *text/plain*, and path
|
||||||
and path equals to `/path`, Resource calls handle of the first matching route.
|
equals to `/path`.
|
||||||
|
|
||||||
If a resource can not match any route, a "NOT FOUND" response is returned.
|
If a resource can not match any route, a "NOT FOUND" response is returned.
|
||||||
|
|
||||||
[*ResourceHandler::route()*](../../actix-web/actix_web/dev/struct.ResourceHandler.html#method.route) returns a
|
[*ResourceHandler::route()*][resourcehandler] returns a [*Route*][route] object. Route
|
||||||
[*Route*](../../actix-web/actix_web/dev/struct.Route.html) object. Route can be configured with a
|
can be configured with a builder-like pattern. Following configuration methods are available:
|
||||||
builder-like pattern. Following configuration methods are available:
|
|
||||||
|
|
||||||
* [*Route::filter()*](../../actix-web/actix_web/dev/struct.Route.html#method.filter)
|
* [*Route::guard()*][routeguard] registers a new guard. Any number of guards can be
|
||||||
registers a new predicate. Any number of predicates can be registered for each route.
|
registered for each route.
|
||||||
* [*Route::f()*](../../actix-web/actix_web/dev/struct.Route.html#method.f) registers
|
* [*Route::method()*][routemethod] registers a method guard. Any number of guards can be
|
||||||
handler function for this route. Only one handler can be registered.
|
registered for each route.
|
||||||
Usually handler registration
|
* [*Route::to()*][routeto] registers handler function for this route. Only one handler
|
||||||
is the last config operation. Handler function can be a function or closure
|
can be registered. Usually handler registration is the last config operation.
|
||||||
and has the type
|
* [*Route::to_async()*][routetoasync] registers an async handler function for this route.
|
||||||
`Fn(&HttpRequest<S>) -> R + 'static`
|
Only one handler can be registered. Handler registration is the last config operation.
|
||||||
* [*Route::h()*](../../actix-web/actix_web/dev/struct.Route.html#method.h) registers
|
|
||||||
a handler object that implements the `Handler` trait. This is
|
|
||||||
similar to `f()` method - only one handler can
|
|
||||||
be registered. Handler registration is the last config operation.
|
|
||||||
* [*Route::a()*](../../actix-web/actix_web/dev/struct.Route.html#method.a) registers
|
|
||||||
an async handler function for this route. Only one handler can be registered.
|
|
||||||
Handler registration is the last config operation. Handler function can
|
|
||||||
be a function or closure and has the type
|
|
||||||
`Fn(&HttpRequest<S>) -> Future<Item = HttpResponse, Error = Error> + 'static`
|
|
||||||
|
|
||||||
# Route matching
|
# Route matching
|
||||||
|
|
||||||
The main purpose of route configuration is to match (or not match) the request's `path`
|
The main purpose of route configuration is to match (or not match) the request's `path`
|
||||||
against a URL path pattern. `path` represents the path portion of the URL that was requested.
|
against a URL path pattern. `path` represents the path portion of the URL that was requested.
|
||||||
|
|
||||||
The way that *actix* does this is very simple. When a request enters the system,
|
The way that *actix-web* does this is very simple. When a request enters the system,
|
||||||
for each resource configuration declaration present in the system, actix checks
|
for each resource configuration declaration present in the system, actix checks
|
||||||
the request's path against the pattern declared. This checking happens in the order that
|
the request's path against the pattern declared. This checking happens in the order that
|
||||||
the routes were declared via `App::resource()` method. If resource can not be found,
|
the routes were declared via `App::service()` method. If resource can not be found,
|
||||||
the *default resource* is used as the matched resource.
|
the *default resource* is used as the matched resource.
|
||||||
|
|
||||||
When a route configuration is declared, it may contain route predicate arguments. All route
|
When a route configuration is declared, it may contain route guard arguments. All route
|
||||||
predicates associated with a route declaration must be `true` for the route configuration to
|
guards associated with a route declaration must be `true` for the route configuration to
|
||||||
be used for a given request during a check. If any predicate in the set of route predicate
|
be used for a given request during a check. If any guard in the set of route guard
|
||||||
arguments provided to a route configuration returns `false` during a check, that route is
|
arguments provided to a route configuration returns `false` during a check, that route is
|
||||||
skipped and route matching continues through the ordered set of routes.
|
skipped and route matching continues through the ordered set of routes.
|
||||||
|
|
||||||
If any route matches, the route matching process stops and the handler associated with
|
If any route matches, the route matching process stops and the handler associated with
|
||||||
the route is invoked. If no route matches after all route patterns are exhausted, a *NOT FOUND* response get returned.
|
the route is invoked. If no route matches after all route patterns are exhausted, a
|
||||||
|
*NOT FOUND* response get returned.
|
||||||
|
|
||||||
# Resource pattern syntax
|
# Resource pattern syntax
|
||||||
|
|
||||||
@ -269,18 +252,12 @@ A *scoped* path can contain variable path segments as resources. Consistent with
|
|||||||
unscoped paths.
|
unscoped paths.
|
||||||
|
|
||||||
You can get variable path segments from `HttpRequest::match_info()`.
|
You can get variable path segments from `HttpRequest::match_info()`.
|
||||||
`Path` extractor also is able to extract scope level variable segments.
|
[`Path` extractor][pathextractor] also is able to extract scope level variable segments.
|
||||||
|
|
||||||
# Match information
|
# Match information
|
||||||
|
|
||||||
All values representing matched path segments are available in
|
All values representing matched path segments are available in [`HttpRequest::match_info`][matchinfo].
|
||||||
[`HttpRequest::match_info`](../actix_web/struct.HttpRequest.html#method.match_info).
|
Specific values can be retrieved with [`Path::get()`][pathget].
|
||||||
Specific values can be retrieved with
|
|
||||||
[`Params::get()`](../actix_web/dev/struct.Params.html#method.get).
|
|
||||||
|
|
||||||
Any matched parameter can be deserialized into a specific type if the type
|
|
||||||
implements the `FromParam` trait. For example most standard integer types
|
|
||||||
the trait, i.e.:
|
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="minfo.rs" section="minfo" >}}
|
{{< include-example example="url-dispatch" file="minfo.rs" section="minfo" >}}
|
||||||
|
|
||||||
@ -304,18 +281,13 @@ safe to interpolate within, or use as a suffix of, a path without additional che
|
|||||||
|
|
||||||
{{< include-example example="url-dispatch" file="pbuf.rs" section="pbuf" >}}
|
{{< include-example example="url-dispatch" file="pbuf.rs" section="pbuf" >}}
|
||||||
|
|
||||||
List of `FromParam` implementations can be found in
|
|
||||||
[api docs](../../actix-web/actix_web/dev/trait.FromParam.html#foreign-impls)
|
|
||||||
|
|
||||||
## Path information extractor
|
## Path information extractor
|
||||||
|
|
||||||
Actix provides functionality for type safe path information extraction.
|
Actix provides functionality for type safe path information extraction. [*Path*][pathstruct]
|
||||||
[*Path*](../../actix-web/actix_web/struct.Path.html) extracts information, destination type
|
extracts information, destination type could be defined in several different forms. Simplest
|
||||||
could be defined in several different forms. Simplest approach is to use
|
approach is to use `tuple` type. Each element in tuple must correpond to one element from
|
||||||
`tuple` type. Each element in tuple must correpond to one element from
|
|
||||||
path pattern. i.e. you can match path pattern `/{id}/{username}/` against
|
path pattern. i.e. you can match path pattern `/{id}/{username}/` against
|
||||||
`Path<(u32, String)>` type, but `Path<(String, String, String)>` type will
|
`Path<(u32, String)>` type, but `Path<(String, String, String)>` type will always fail.
|
||||||
always fail.
|
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="path.rs" section="path" >}}
|
{{< include-example example="url-dispatch" file="path.rs" section="path" >}}
|
||||||
|
|
||||||
@ -324,21 +296,19 @@ this struct must implement *serde's *`Deserialize` trait.
|
|||||||
|
|
||||||
{{< include-example example="url-dispatch" file="path2.rs" section="path" >}}
|
{{< include-example example="url-dispatch" file="path2.rs" section="path" >}}
|
||||||
|
|
||||||
[*Query*](../../actix-web/actix_web/struct.Query.html) provides similar
|
[*Query*][query] provides similar functionality for request query parameters.
|
||||||
functionality for request query parameters.
|
|
||||||
|
|
||||||
# Generating resource URLs
|
# Generating resource URLs
|
||||||
|
|
||||||
Use the [*HttpRequest.url_for()*](../../actix-web/actix_web/struct.HttpRequest.html#method.url_for)
|
Use the [*HttpRequest.url_for()*][urlfor] method to generate URLs based on resource
|
||||||
method to generate URLs based on resource patterns. For example, if you've configured a
|
patterns. For example, if you've configured a resource with the name "foo" and the
|
||||||
resource with the name "foo" and the pattern "{a}/{b}/{c}", you might do this:
|
pattern "{a}/{b}/{c}", you might do this:
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="urls.rs" section="url" >}}
|
{{< include-example example="url-dispatch" file="urls.rs" section="url" >}}
|
||||||
|
|
||||||
This would return something like the string *http://example.com/test/1/2/3* (at least if
|
This would return something like the string *http://example.com/test/1/2/3* (at least if
|
||||||
the current protocol and hostname implied http://example.com).
|
the current protocol and hostname implied http://example.com). `url_for()` method
|
||||||
`url_for()` method returns [*Url object*](https://docs.rs/url/1.7.0/url/struct.Url.html) so you
|
returns [*Url object*][urlobj] so you can modify this url (add query parameters, anchor, etc).
|
||||||
can modify this url (add query parameters, anchor, etc).
|
|
||||||
`url_for()` could be called only for *named* resources otherwise error get returned.
|
`url_for()` could be called only for *named* resources otherwise error get returned.
|
||||||
|
|
||||||
# External resources
|
# External resources
|
||||||
@ -355,22 +325,14 @@ By normalizing it means:
|
|||||||
* To add a trailing slash to the path.
|
* To add a trailing slash to the path.
|
||||||
* To replace multiple slashes with one.
|
* To replace multiple slashes with one.
|
||||||
|
|
||||||
The handler returns as soon as it finds a path that resolves
|
The handler returns as soon as it finds a path that resolves correctly. The order of
|
||||||
correctly. The order of normalization conditions, if all are enabled, is 1) merge, 2) both merge and append
|
normalization conditions, if all are enabled, is 1) merge, 2) both merge and append and
|
||||||
and 3) append. If the path resolves with
|
3) append. If the path resolves with at least one of those conditions, it will redirect
|
||||||
at least one of those conditions, it will redirect to the new path.
|
to the new path.
|
||||||
|
|
||||||
If *append* is *true*, append slash when needed. If a resource is
|
|
||||||
defined with trailing slash and the request doesn't have one, it will
|
|
||||||
be appended automatically.
|
|
||||||
|
|
||||||
If *merge* is *true*, merge multiple consecutive slashes in the path into one.
|
|
||||||
|
|
||||||
This handler designed to be used as a handler for application's *default resource*.
|
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="norm.rs" section="norm" >}}
|
{{< include-example example="url-dispatch" file="norm.rs" section="norm" >}}
|
||||||
|
|
||||||
In this example `/resource`, `//resource///` will be redirected to `/resource/`.
|
In this example `//resource///` will be redirected to `/resource/`.
|
||||||
|
|
||||||
In this example, the path normalization handler is registered for all methods,
|
In this example, the path normalization handler is registered for all methods,
|
||||||
but you should not rely on this mechanism to redirect *POST* requests. The redirect of the
|
but you should not rely on this mechanism to redirect *POST* requests. The redirect of the
|
||||||
@ -383,68 +345,88 @@ It is possible to register path normalization only for *GET* requests only:
|
|||||||
|
|
||||||
## Using an Application Prefix to Compose Applications
|
## Using an Application Prefix to Compose Applications
|
||||||
|
|
||||||
The `App::prefix()` method allows to set a specific application prefix.
|
The `web::scope()` method allows to set a specific application scope. This scope represents
|
||||||
This prefix represents a resource prefix that will be prepended to all resource patterns added
|
a resource prefix that will be prepended to all resource patterns added by the resource
|
||||||
by the resource configuration. This can be used to help mount a set of routes at a different
|
configuration. This can be used to help mount a set of routes at a different location
|
||||||
location than the included callable's author intended while still maintaining the same
|
than the included callable's author intended while still maintaining the same resource names.
|
||||||
resource names.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="prefix.rs" section="prefix" >}}
|
{{< include-example example="url-dispatch" file="scope.rs" section="scope" >}}
|
||||||
|
|
||||||
In the above example, the *show_users* route will have an effective route pattern of
|
In the above example, the *show_users* route will have an effective route pattern of
|
||||||
*/users/show* instead of */show* because the application's prefix argument will be prepended
|
*/users/show* instead of */show* because the application's scope will be prepended
|
||||||
to the pattern. The route will then only match if the URL path is */users/show*,
|
to the pattern. The route will then only match if the URL path is */users/show*,
|
||||||
and when the `HttpRequest.url_for()` function is called with the route name show_users,
|
and when the `HttpRequest.url_for()` function is called with the route name show_users,
|
||||||
it will generate a URL with that same path.
|
it will generate a URL with that same path.
|
||||||
|
|
||||||
# Custom route predicates
|
# Custom route guard
|
||||||
|
|
||||||
You can think of a predicate as a simple function that accepts a *request* object reference
|
You can think of a guard as a simple function that accepts a *request* object reference
|
||||||
and returns *true* or *false*. Formally, a predicate is any object that implements the
|
and returns *true* or *false*. Formally, a guard is any object that implements the
|
||||||
[`Predicate`](../actix_web/pred/trait.Predicate.html) trait. Actix provides
|
[`Guard`][guardtrait] trait. Actix provides several predicates, you can check
|
||||||
several predicates, you can check
|
[functions section][guardfuncs] of api docs.
|
||||||
[functions section](../../actix-web/actix_web/pred/index.html#functions) of api docs.
|
|
||||||
|
|
||||||
Here is a simple predicate that check that a request contains a specific *header*:
|
Here is a simple guard that check that a request contains a specific *header*:
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="pred.rs" section="pred" >}}
|
{{< include-example example="url-dispatch" file="guard.rs" section="guard" >}}
|
||||||
|
|
||||||
In this example, *index* handler will be called only if request contains *CONTENT-TYPE* header.
|
In this example, *index* handler will be called only if request contains *CONTENT-TYPE* header.
|
||||||
|
|
||||||
Predicates have access to the application's state via `HttpRequest::state()`.
|
Guards can not access or modify the request object, but it is possible to store extra
|
||||||
Also predicates can store extra information in
|
information in [request extensions][requestextensions].
|
||||||
[request extensions](../../actix-web/actix_web/struct.HttpRequest.html#method.extensions).
|
|
||||||
|
|
||||||
## Modifying predicate values
|
## Modifying guard values
|
||||||
|
|
||||||
You can invert the meaning of any predicate value by wrapping it in a `Not` predicate.
|
You can invert the meaning of any predicate value by wrapping it in a `Not` predicate.
|
||||||
For example, if you want to return "METHOD NOT ALLOWED" response for all methods
|
For example, if you want to return "METHOD NOT ALLOWED" response for all methods
|
||||||
except "GET":
|
except "GET":
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="pred2.rs" section="pred" >}}
|
{{< include-example example="url-dispatch" file="guard2.rs" section="guard2" >}}
|
||||||
|
|
||||||
The `Any` predicate accepts a list of predicates and matches if any of the supplied
|
The `Any` guard accepts a list of guards and matches if any of the supplied
|
||||||
predicates match. i.e:
|
guards match. i.e:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pred::Any(pred::Get()).or(pred::Post())
|
guard::Any(guard::Get()).or(guard::Post())
|
||||||
```
|
```
|
||||||
|
|
||||||
The `All` predicate accepts a list of predicates and matches if all of the supplied
|
The `All` guard accepts a list of guard and matches if all of the supplied
|
||||||
predicates match. i.e:
|
guards match. i.e:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pred::All(pred::Get()).and(pred::Header("content-type", "plain/text"))
|
guard::All(guard::Get()).and(guard::Header("content-type", "plain/text"))
|
||||||
```
|
```
|
||||||
|
|
||||||
# Changing the default Not Found response
|
# Changing the default Not Found response
|
||||||
|
|
||||||
If the path pattern can not be found in the routing table or a resource can not find matching
|
If the path pattern can not be found in the routing table or a resource can not find matching
|
||||||
route, the default resource is used. The default response is *NOT FOUND*.
|
route, the default resource is used. The default response is *NOT FOUND*.
|
||||||
It is possible to override the *NOT FOUND* response with `App::default_resource()`.
|
It is possible to override the *NOT FOUND* response with `App::default_service()`.
|
||||||
This method accepts a *configuration function* same as normal resource configuration
|
This method accepts a *configuration function* same as normal resource configuration
|
||||||
with `App::resource()` method.
|
with `App::service()` method.
|
||||||
|
|
||||||
{{< include-example example="url-dispatch" file="dhandler.rs" section="default" >}}
|
{{< include-example example="url-dispatch" file="dhandler.rs" section="default" >}}
|
||||||
|
|
||||||
|
[handlersection]: ../handlers/
|
||||||
|
[approute]: https://docs.rs/actix-web/1.0.2/actix_web/struct.App.html#method.route
|
||||||
|
[appservice]: https://docs.rs/actix-web/1.0.2/actix_web/struct.App.html?search=#method.service
|
||||||
|
[webresource]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Resource.html
|
||||||
|
[resourcehandler]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Resource.html#method.route
|
||||||
|
[route]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html
|
||||||
|
[routeguard]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.guard
|
||||||
|
[routemethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.method
|
||||||
|
[routeto]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.to
|
||||||
|
[routetoasync]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.to_async
|
||||||
|
[matchinfo]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.match_info
|
||||||
|
[pathget]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html#method.get
|
||||||
|
[pathstruct]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html
|
||||||
|
[query]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Query.html
|
||||||
|
[urlfor]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.url_for
|
||||||
|
[urlobj]: https://docs.rs/url/1.7.2/url/struct.Url.html
|
||||||
|
[guardtrait]: https://docs.rs/actix-web/1.0.2/actix_web/guard/trait.Guard.html
|
||||||
|
[guardfuncs]: https://docs.rs/actix-web/1.0.2/actix_web/guard/index.html#functions
|
||||||
|
[requestextensions]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.extensions
|
||||||
|
[implfromrequest]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html
|
||||||
|
[implresponder]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html
|
||||||
|
[pathextractor]: ../extractors
|
||||||
|
@ -4,47 +4,21 @@ menu: docs_proto
|
|||||||
weight: 240
|
weight: 240
|
||||||
---
|
---
|
||||||
|
|
||||||
Actix supports WebSockets out-of-the-box. It is possible to convert a request's `Payload`
|
Actix-web supports WebSockets with the `actix-web-actors` crate. It is possible to convert a
|
||||||
to a stream of [*ws::Message*](../../actix-web/actix_web/ws/enum.Message.html) with
|
request's `Payload` to a stream of [*ws::Message*][message] with a [*web::Payload*][payload]
|
||||||
a [*ws::WsStream*](../../actix-web/actix_web/ws/struct.WsStream.html) and then use stream
|
and then use stream combinators to handle actual messages, but it is simpler to handle
|
||||||
combinators to handle actual messages, but it is simpler to handle websocket communications
|
websocket communications with an http actor.
|
||||||
with an http actor.
|
|
||||||
|
|
||||||
The following is an example of a simple websocket echo server:
|
The following is an example of a simple websocket echo server:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="websockets" file="main.rs" section="websockets" >}}
|
||||||
use actix::*;
|
|
||||||
use actix_web::*;
|
|
||||||
|
|
||||||
/// Define http actor
|
> A simple websocket echo server example is available in the [examples directory][examples].
|
||||||
struct Ws;
|
|
||||||
|
|
||||||
impl Actor for Ws {
|
|
||||||
type Context = ws::WebsocketContext<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler for ws::Message message
|
|
||||||
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
|
|
||||||
|
|
||||||
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
|
|
||||||
match msg {
|
|
||||||
ws::Message::Ping(msg) => ctx.pong(&msg),
|
|
||||||
ws::Message::Text(text) => ctx.text(text),
|
|
||||||
ws::Message::Binary(bin) => ctx.binary(bin),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
.resource("/ws/", |r| r.f(|req| ws::start(req, Ws)))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> A simple websocket echo server example is available in the
|
|
||||||
> [examples directory](https://github.com/actix/examples/tree/master/websocket/).
|
|
||||||
|
|
||||||
> An example chat server with the ability to chat over a websocket or tcp connection
|
> An example chat server with the ability to chat over a websocket or tcp connection
|
||||||
> is available in [websocket-chat directory](https://github.com/actix/examples/tree/master/websocket-chat/)
|
> is available in [websocket-chat directory][chat]
|
||||||
|
|
||||||
|
[message]: https://docs.rs/actix-web-actors/1.0.0/actix_web_actors/ws/enum.Message.html
|
||||||
|
[payload]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Payload.html
|
||||||
|
[examples]: https://github.com/actix/examples/tree/master/websocket/
|
||||||
|
[chat]: https://github.com/actix/examples/tree/master/websocket-chat/
|
||||||
|
@ -7,14 +7,14 @@ weight: 100
|
|||||||
# Actix is Multiple Things
|
# Actix is Multiple Things
|
||||||
|
|
||||||
Actix is a few things. The base of it is a powerful actor system for Rust on
|
Actix is a few things. The base of it is a powerful actor system for Rust on
|
||||||
top of which the `actix-web` system is built. This is what you are most likely
|
top of which the `actix-web` system was originally built. This is what you are most
|
||||||
going to work with. What `actix-web` gives you is a fun and very fast web
|
likely going to work with. What `actix-web` gives you is a fun and very fast web
|
||||||
development framework.
|
development framework.
|
||||||
|
|
||||||
We call `actix-web` a small and pragmatic framework. For all intents and purposes
|
We call `actix-web` a small and pragmatic framework. For all intents and purposes
|
||||||
it's a microframework with a few twists. If you are already a Rust programmer
|
it's a microframework with a few twists. If you are already a Rust programmer
|
||||||
you will probably find yourself at home quickly, but even if you are coming from
|
you will probably find yourself at home quickly, but even if you are coming from
|
||||||
another programming language you should find actix-web easy to pick up.
|
another programming language you should find `actix-web` easy to pick up.
|
||||||
|
|
||||||
An application developed with `actix-web` will expose an HTTP server contained
|
An application developed with `actix-web` will expose an HTTP server contained
|
||||||
within a native executable. You can either put this behind another HTTP server like
|
within a native executable. You can either put this behind another HTTP server like
|
||||||
@ -23,5 +23,5 @@ server `actix-web` is powerful enough to provide HTTP 1 and HTTP 2 support as
|
|||||||
well as SSL/TLS. This makes it useful for building small services ready for
|
well as SSL/TLS. This makes it useful for building small services ready for
|
||||||
distribution.
|
distribution.
|
||||||
|
|
||||||
Most importantly: `actix-web` runs on Rust 1.26 or later and it works with
|
Most importantly: `actix-web` runs on Rust {{< rust-version "actix-web" >}} or later
|
||||||
stable releases.
|
and it works with stable releases.
|
||||||
|
@ -1,7 +1,30 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"application",
|
"application",
|
||||||
|
"easy-form-handling",
|
||||||
|
"flexible-responders",
|
||||||
"getting-started",
|
"getting-started",
|
||||||
|
"main-example",
|
||||||
|
"powerful-extractors",
|
||||||
|
"request-routing",
|
||||||
"server",
|
"server",
|
||||||
"url-dispatch",
|
"url-dispatch",
|
||||||
|
"responder-trait",
|
||||||
|
"either",
|
||||||
|
"extractors",
|
||||||
|
"autoreload",
|
||||||
|
"errors",
|
||||||
|
"requests",
|
||||||
|
"responses",
|
||||||
|
"middleware",
|
||||||
|
"static-files",
|
||||||
|
"http2",
|
||||||
|
"testing",
|
||||||
|
"async-handlers",
|
||||||
|
"websockets",
|
||||||
|
"request-handlers",
|
||||||
|
]
|
||||||
|
exclude = [
|
||||||
|
"og_databases",
|
||||||
|
"sentry",
|
||||||
]
|
]
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "application"
|
name = "application"
|
||||||
version = "0.7.0"
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
workspace = "../"
|
workspace = "../"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "0.7"
|
actix-web = "1.0"
|
||||||
|
14
examples/application/src/app.rs
Normal file
14
examples/application/src/app.rs
Normal 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>
|
25
examples/application/src/combine.rs
Normal file
25
examples/application/src/combine.rs
Normal 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>
|
34
examples/application/src/config.rs
Normal file
34
examples/application/src/config.rs
Normal 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>
|
@ -1,42 +1,22 @@
|
|||||||
#![allow(unused)]
|
use actix_web::{web, App, HttpResponse};
|
||||||
extern crate actix_web;
|
|
||||||
use actix_web::{http::Method, server, App, HttpRequest, HttpResponse, Responder};
|
|
||||||
|
|
||||||
mod state;
|
pub mod app;
|
||||||
mod vh;
|
pub mod combine;
|
||||||
|
pub mod config;
|
||||||
fn make_app() {
|
pub mod scope;
|
||||||
// <make_app>
|
pub mod state;
|
||||||
fn index(req: &HttpRequest) -> impl Responder {
|
pub mod vh;
|
||||||
"Hello world!"
|
|
||||||
}
|
|
||||||
|
|
||||||
let app = App::new()
|
|
||||||
.prefix("/app")
|
|
||||||
.resource("/index.html", |r| r.method(Method::GET).f(index))
|
|
||||||
.finish()
|
|
||||||
// </make_app>
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_server() {
|
|
||||||
// <run_server>
|
|
||||||
let server = server::new(|| {
|
|
||||||
vec![
|
|
||||||
App::new()
|
|
||||||
.prefix("/app1")
|
|
||||||
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
|
|
||||||
App::new()
|
|
||||||
.prefix("/app2")
|
|
||||||
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
|
|
||||||
App::new().resource("/", |r| r.f(|r| HttpResponse::Ok())),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
// </run_server>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
// <multi>
|
||||||
fn main() {
|
fn main() {
|
||||||
make_app();
|
App::new()
|
||||||
run_server();
|
.service(
|
||||||
state::test();
|
web::scope("/app1")
|
||||||
|
.route("/", web::to(|| HttpResponse::Ok())))
|
||||||
|
.service(
|
||||||
|
web::scope("/app2")
|
||||||
|
.route("/", web::to(|| HttpResponse::Ok())))
|
||||||
|
.route("/", web::to(|| HttpResponse::Ok()));
|
||||||
}
|
}
|
||||||
|
// </multi>
|
||||||
|
15
examples/application/src/scope.rs
Normal file
15
examples/application/src/scope.rs
Normal 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>
|
@ -1,71 +1,54 @@
|
|||||||
// <setup>
|
// <setup>
|
||||||
use actix_web::{http, App, HttpRequest};
|
use actix_web::{web, App, HttpServer};
|
||||||
use std::cell::Cell;
|
use std::sync::{Mutex};
|
||||||
|
|
||||||
// This struct represents state
|
// This struct represents state
|
||||||
struct AppState {
|
struct AppState {
|
||||||
counter: Cell<usize>,
|
app_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index(req: &HttpRequest<AppState>) -> String {
|
fn index(data: web::Data<AppState>) -> String {
|
||||||
let count = req.state().counter.get() + 1; // <- get count
|
let app_name = data.app_name; // <- get app_name
|
||||||
req.state().counter.set(count); // <- store new count in state
|
|
||||||
|
|
||||||
format!("Request number: {}", count) // <- response with count
|
format!("Hello {}!", app_name) // <- response with app_name
|
||||||
}
|
}
|
||||||
// </setup>
|
// </setup>
|
||||||
|
|
||||||
fn make_app() {
|
// <setup_mutable>
|
||||||
// <make_app>
|
struct AppStateWithCounter {
|
||||||
App::with_state(AppState { counter: Cell::new(0) })
|
counter: Mutex<i32>, // <- Mutex is necessary to mutate safely across threads
|
||||||
.resource("/", |r| r.method(http::Method::GET).f(index))
|
|
||||||
.finish()
|
|
||||||
// </make_app>
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
// <start_app>
|
||||||
server::new(|| {
|
pub fn main() {
|
||||||
App::with_state(AppState { counter: Cell::new(0) })
|
HttpServer::new(|| {
|
||||||
.resource("/", |r| r.method(http::Method::GET).f(index))
|
App::new()
|
||||||
}).bind("127.0.0.1:8080")
|
.data(AppState {
|
||||||
|
app_name: String::from("Actix-web")
|
||||||
|
})
|
||||||
|
.route("/", web::get().to(index))
|
||||||
|
})
|
||||||
|
.bind("127.0.0.1:8088")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.run()
|
.run()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
// </start_app>
|
// </start_app>
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
use actix_web::{server, HttpResponse};
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
fn combine() {
|
|
||||||
thread::spawn(|| {
|
|
||||||
// <combine>
|
|
||||||
struct State1;
|
|
||||||
struct State2;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
server::new(|| {
|
|
||||||
vec![
|
|
||||||
App::with_state(State1)
|
|
||||||
.prefix("/app1")
|
|
||||||
.resource("/", |r| r.f(|r| HttpResponse::Ok()))
|
|
||||||
.boxed(),
|
|
||||||
App::with_state(State2)
|
|
||||||
.prefix("/app2")
|
|
||||||
.resource("/", |r| r.f(|r| HttpResponse::Ok()))
|
|
||||||
.boxed(),
|
|
||||||
]
|
|
||||||
}).bind("127.0.0.1:8080")
|
|
||||||
.unwrap()
|
|
||||||
.run()
|
|
||||||
}
|
|
||||||
// </combine>
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test() {
|
|
||||||
make_app();
|
|
||||||
combine();
|
|
||||||
}
|
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
#![allow(unused)]
|
use actix_web::{guard, web, App, HttpResponse, HttpServer};
|
||||||
use actix_web::{http::Method, pred, server, App, HttpRequest, HttpResponse, Responder};
|
|
||||||
|
|
||||||
// <vh>
|
// <vh>
|
||||||
fn main() {
|
pub fn main() {
|
||||||
let server = server::new(|| {
|
HttpServer::new(|| {
|
||||||
vec![
|
|
||||||
App::new()
|
App::new()
|
||||||
.filter(pred::Host("www.rust-lang.org"))
|
.service(
|
||||||
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
|
web::scope("/")
|
||||||
App::new()
|
.guard(guard::Header("Host", "www.rust-lang.org"))
|
||||||
.filter(pred::Host("users.rust-lang.org"))
|
.route("", web::to(|| HttpResponse::Ok().body("www"))),
|
||||||
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
|
)
|
||||||
App::new().resource("/", |r| r.f(|r| HttpResponse::Ok())),
|
.service(
|
||||||
]
|
web::scope("/")
|
||||||
});
|
.guard(guard::Header("Host", "users.rust-lang.org"))
|
||||||
|
.route("", web::to(|| HttpResponse::Ok().body("user"))),
|
||||||
server.run();
|
)
|
||||||
|
.route("/", web::to(|| HttpResponse::Ok()))
|
||||||
|
})
|
||||||
|
.bind("127.0.0.1:8088")
|
||||||
|
.unwrap()
|
||||||
|
.run()
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
// </vh>
|
// </vh>
|
||||||
|
9
examples/async-handlers/Cargo.toml
Normal file
9
examples/async-handlers/Cargo.toml
Normal 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"
|
28
examples/async-handlers/src/async_stream.rs
Normal file
28
examples/async-handlers/src/async_stream.rs
Normal 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();
|
||||||
|
}
|
30
examples/async-handlers/src/main.rs
Normal file
30
examples/async-handlers/src/main.rs
Normal 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>
|
23
examples/async-handlers/src/stream.rs
Normal file
23
examples/async-handlers/src/stream.rs
Normal 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>
|
8
examples/autoreload/Cargo.toml
Normal file
8
examples/autoreload/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "autoreload"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
||||||
|
listenfd = "0.3"
|
21
examples/autoreload/src/main.rs
Normal file
21
examples/autoreload/src/main.rs
Normal 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>
|
8
examples/easy-form-handling/Cargo.toml
Normal file
8
examples/easy-form-handling/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "easy-form-handling"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
||||||
|
serde = "1.0"
|
32
examples/easy-form-handling/src/main.rs
Normal file
32
examples/easy-form-handling/src/main.rs
Normal 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>
|
28
examples/easy-form-handling/static/form.html
Normal file
28
examples/easy-form-handling/static/form.html
Normal 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>
|
8
examples/either/Cargo.toml
Normal file
8
examples/either/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "either"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
||||||
|
futures = "0.1"
|
35
examples/either/src/main.rs
Normal file
35
examples/either/src/main.rs
Normal 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
|
||||||
|
}
|
10
examples/errors/Cargo.toml
Normal file
10
examples/errors/Cargo.toml
Normal 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"
|
25
examples/errors/src/helpers.rs
Normal file
25
examples/errors/src/helpers.rs
Normal 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();
|
||||||
|
}
|
38
examples/errors/src/logging.rs
Normal file
38
examples/errors/src/logging.rs
Normal 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>
|
33
examples/errors/src/main.rs
Normal file
33
examples/errors/src/main.rs
Normal 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();
|
||||||
|
}
|
54
examples/errors/src/override_error.rs
Normal file
54
examples/errors/src/override_error.rs
Normal 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();
|
||||||
|
}
|
35
examples/errors/src/recommend_one.rs
Normal file
35
examples/errors/src/recommend_one.rs
Normal 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();
|
||||||
|
}
|
39
examples/errors/src/recommend_two.rs
Normal file
39
examples/errors/src/recommend_two.rs
Normal 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();
|
||||||
|
}
|
10
examples/extractors/Cargo.toml
Normal file
10
examples/extractors/Cargo.toml
Normal 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"
|
26
examples/extractors/src/form.rs
Normal file
26
examples/extractors/src/form.rs
Normal 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();
|
||||||
|
}
|
24
examples/extractors/src/json_one.rs
Normal file
24
examples/extractors/src/json_one.rs
Normal 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();
|
||||||
|
}
|
42
examples/extractors/src/json_two.rs
Normal file
42
examples/extractors/src/json_two.rs
Normal 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>
|
49
examples/extractors/src/main.rs
Normal file
49
examples/extractors/src/main.rs
Normal 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();
|
||||||
|
}
|
31
examples/extractors/src/multiple.rs
Normal file
31
examples/extractors/src/multiple.rs
Normal 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>
|
25
examples/extractors/src/path_one.rs
Normal file
25
examples/extractors/src/path_one.rs
Normal 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>
|
25
examples/extractors/src/path_three.rs
Normal file
25
examples/extractors/src/path_three.rs
Normal 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>
|
30
examples/extractors/src/path_two.rs
Normal file
30
examples/extractors/src/path_two.rs
Normal 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>
|
24
examples/extractors/src/query.rs
Normal file
24
examples/extractors/src/query.rs
Normal 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();
|
||||||
|
}
|
8
examples/flexible-responders/Cargo.toml
Normal file
8
examples/flexible-responders/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "flexible-responders"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
||||||
|
serde = "1.0"
|
29
examples/flexible-responders/src/main.rs
Normal file
29
examples/flexible-responders/src/main.rs
Normal 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>
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "getting-started"
|
name = "getting-started"
|
||||||
version = "0.7.0"
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
workspace = "../"
|
workspace = "../"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "0.7"
|
actix-web = "1.0"
|
||||||
|
@ -1,16 +1,25 @@
|
|||||||
// <setup>
|
// <setup>
|
||||||
extern crate actix_web;
|
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
|
||||||
use actix_web::{server, App, HttpRequest};
|
|
||||||
|
|
||||||
fn index(_req: &HttpRequest) -> &'static str {
|
fn index() -> impl Responder {
|
||||||
"Hello world!"
|
HttpResponse::Ok().body("Hello world!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index2() -> impl Responder {
|
||||||
|
HttpResponse::Ok().body("Hello world again!")
|
||||||
}
|
}
|
||||||
// </setup>
|
// </setup>
|
||||||
|
|
||||||
// <main>
|
// <main>
|
||||||
fn main() {
|
fn main() {
|
||||||
server::new(|| App::new().resource("/", |r| r.f(index)))
|
HttpServer::new(|| {
|
||||||
|
App::new()
|
||||||
|
.route("/", web::get().to(index))
|
||||||
|
.route("/again", web::get().to(index2))
|
||||||
|
})
|
||||||
.bind("127.0.0.1:8088")
|
.bind("127.0.0.1:8088")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.run();
|
.run()
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
// </main>
|
// </main>
|
||||||
|
8
examples/http2/Cargo.toml
Normal file
8
examples/http2/Cargo.toml
Normal 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"] }
|
25
examples/http2/src/main.rs
Normal file
25
examples/http2/src/main.rs
Normal 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>
|
7
examples/main-example/Cargo.toml
Normal file
7
examples/main-example/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "main-example"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
20
examples/main-example/src/main.rs
Normal file
20
examples/main-example/src/main.rs
Normal 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>
|
11
examples/middleware/Cargo.toml
Normal file
11
examples/middleware/Cargo.toml
Normal 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"
|
24
examples/middleware/src/default_headers.rs
Normal file
24
examples/middleware/src/default_headers.rs
Normal 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>
|
33
examples/middleware/src/errorhandler.rs
Normal file
33
examples/middleware/src/errorhandler.rs
Normal 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>
|
21
examples/middleware/src/logger.rs
Normal file
21
examples/middleware/src/logger.rs
Normal 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>
|
82
examples/middleware/src/main.rs
Normal file
82
examples/middleware/src/main.rs
Normal 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();
|
||||||
|
}
|
33
examples/middleware/src/user_sessions.rs
Normal file
33
examples/middleware/src/user_sessions.rs
Normal 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>
|
7
examples/og_databases/Cargo.toml
Normal file
7
examples/og_databases/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "og_databases"
|
||||||
|
version = "0.7.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "0.7"
|
98
examples/og_databases/src/main.rs
Normal file
98
examples/og_databases/src/main.rs
Normal 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>
|
8
examples/powerful-extractors/Cargo.toml
Normal file
8
examples/powerful-extractors/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "powerful-extractors"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
||||||
|
serde = "1.0"
|
44
examples/powerful-extractors/src/main.rs
Normal file
44
examples/powerful-extractors/src/main.rs
Normal 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();
|
||||||
|
}
|
43
examples/powerful-extractors/static/form.html
Normal file
43
examples/powerful-extractors/static/form.html
Normal 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>
|
7
examples/request-handlers/Cargo.toml
Normal file
7
examples/request-handlers/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "request-handlers"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
39
examples/request-handlers/src/handlers_arc.rs
Normal file
39
examples/request-handlers/src/handlers_arc.rs
Normal 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>
|
40
examples/request-handlers/src/main.rs
Normal file
40
examples/request-handlers/src/main.rs
Normal 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>
|
7
examples/request-routing/Cargo.toml
Normal file
7
examples/request-routing/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "request-routing"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
23
examples/request-routing/src/main.rs
Normal file
23
examples/request-routing/src/main.rs
Normal 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>
|
12
examples/requests/Cargo.toml
Normal file
12
examples/requests/Cargo.toml
Normal 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"
|
21
examples/requests/src/json_two.rs
Normal file
21
examples/requests/src/json_two.rs
Normal 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>
|
28
examples/requests/src/main.rs
Normal file
28
examples/requests/src/main.rs
Normal 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>
|
53
examples/requests/src/manual.rs
Normal file
53
examples/requests/src/manual.rs
Normal 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();
|
||||||
|
}
|
25
examples/requests/src/multipart.rs
Normal file
25
examples/requests/src/multipart.rs
Normal 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>
|
26
examples/requests/src/streaming.rs
Normal file
26
examples/requests/src/streaming.rs
Normal 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();
|
||||||
|
}
|
23
examples/requests/src/urlencoded.rs
Normal file
23
examples/requests/src/urlencoded.rs
Normal 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();
|
||||||
|
}
|
9
examples/responder-trait/Cargo.toml
Normal file
9
examples/responder-trait/Cargo.toml
Normal 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"
|
38
examples/responder-trait/src/main.rs
Normal file
38
examples/responder-trait/src/main.rs
Normal 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();
|
||||||
|
}
|
10
examples/responses/Cargo.toml
Normal file
10
examples/responses/Cargo.toml
Normal 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"
|
21
examples/responses/src/auto.rs
Normal file
21
examples/responses/src/auto.rs
Normal 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>
|
23
examples/responses/src/brotli.rs
Normal file
23
examples/responses/src/brotli.rs
Normal 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>
|
21
examples/responses/src/brotli_two.rs
Normal file
21
examples/responses/src/brotli_two.rs
Normal 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>
|
23
examples/responses/src/chunked.rs
Normal file
23
examples/responses/src/chunked.rs
Normal 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();
|
||||||
|
// }
|
21
examples/responses/src/compress.rs
Normal file
21
examples/responses/src/compress.rs
Normal 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>
|
26
examples/responses/src/identity.rs
Normal file
26
examples/responses/src/identity.rs
Normal 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
Loading…
Reference in New Issue
Block a user