2018-03-28 22:16:01 +02:00
|
|
|
# Middleware
|
2017-12-04 05:47:15 +01:00
|
|
|
|
2018-04-07 00:29:18 +02:00
|
|
|
Actix's middleware system allows us to add additional behavior to request/response processing.
|
2018-04-07 01:35:11 +02:00
|
|
|
Middleware can hook into an incoming request process, enabling us to modify requests
|
|
|
|
as well as halt request processing to return a response early.
|
2017-12-26 16:58:21 +01:00
|
|
|
|
2018-04-07 00:29:18 +02:00
|
|
|
Middleware can also hook into response processing.
|
|
|
|
|
|
|
|
Typically, middleware is involved in the following actions:
|
2017-12-26 16:58:21 +01:00
|
|
|
|
|
|
|
* Pre-process the Request
|
|
|
|
* Post-process a Response
|
|
|
|
* Modify application state
|
|
|
|
* Access external services (redis, logging, sessions)
|
|
|
|
|
2018-04-07 00:29:18 +02:00
|
|
|
Middleware is registered for each application and executed in same order as
|
|
|
|
registration. In general, a *middleware* is a type that implements the
|
2017-12-26 16:58:21 +01:00
|
|
|
[*Middleware trait*](../actix_web/middlewares/trait.Middleware.html). Each method
|
2018-03-28 22:16:01 +02:00
|
|
|
in this trait has a default implementation. Each method can return a result immediately
|
|
|
|
or a *future* object.
|
2017-12-26 16:58:21 +01:00
|
|
|
|
2018-04-07 01:35:11 +02:00
|
|
|
The following demonstrates using middleware to add request and response headers:
|
2017-12-26 16:58:21 +01:00
|
|
|
|
|
|
|
```rust
|
|
|
|
# extern crate http;
|
|
|
|
# extern crate actix_web;
|
|
|
|
use http::{header, HttpTryFrom};
|
2018-03-31 09:16:55 +02:00
|
|
|
use actix_web::{App, HttpRequest, HttpResponse, Result};
|
2017-12-27 04:59:41 +01:00
|
|
|
use actix_web::middleware::{Middleware, Started, Response};
|
2017-12-26 16:58:21 +01:00
|
|
|
|
|
|
|
struct Headers; // <- Our middleware
|
|
|
|
|
|
|
|
/// Middleware implementation, middlewares are generic over application state,
|
|
|
|
/// so you can access state with `HttpRequest::state()` method.
|
|
|
|
impl<S> Middleware<S> for Headers {
|
|
|
|
|
|
|
|
/// Method is called when request is ready. It may return
|
|
|
|
/// future, which should resolve before next middleware get called.
|
2018-01-10 07:48:35 +01:00
|
|
|
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
2017-12-26 16:58:21 +01:00
|
|
|
req.headers_mut().insert(
|
|
|
|
header::CONTENT_TYPE, header::HeaderValue::from_static("text/plain"));
|
2018-01-10 07:48:35 +01:00
|
|
|
Ok(Started::Done)
|
2017-12-26 16:58:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Method is called when handler returns response,
|
|
|
|
/// but before sending http message to peer.
|
2018-01-10 07:48:35 +01:00
|
|
|
fn response(&self, req: &mut HttpRequest<S>, mut resp: HttpResponse) -> Result<Response> {
|
2017-12-26 16:58:21 +01:00
|
|
|
resp.headers_mut().insert(
|
|
|
|
header::HeaderName::try_from("X-VERSION").unwrap(),
|
|
|
|
header::HeaderValue::from_static("0.2"));
|
2018-01-10 07:48:35 +01:00
|
|
|
Ok(Response::Done(resp))
|
2017-12-26 16:58:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
2018-03-31 09:16:55 +02:00
|
|
|
App::new()
|
2018-03-28 22:16:01 +02:00
|
|
|
.middleware(Headers) // <- Register middleware, this method can be called multiple times
|
2018-03-31 08:07:33 +02:00
|
|
|
.resource("/", |r| r.f(|_| HttpResponse::Ok()));
|
2017-12-26 16:58:21 +01:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2018-04-07 00:29:18 +02:00
|
|
|
> Actix provides several useful middlewares, such as *logging*, *user sessions*, etc.
|
2017-12-26 16:58:21 +01:00
|
|
|
|
2017-12-04 05:47:15 +01:00
|
|
|
## Logging
|
|
|
|
|
2018-03-28 22:16:01 +02:00
|
|
|
Logging is implemented as a middleware.
|
|
|
|
It is common to register a logging middleware as the first middleware for the application.
|
2018-04-07 00:29:18 +02:00
|
|
|
Logging middleware must be registered for each application.
|
|
|
|
|
|
|
|
The `Logger` middleware uses the standard log crate to log information. You should enable logger
|
2018-04-07 01:35:11 +02:00
|
|
|
for *actix_web* package to see access log ([env_logger](https://docs.rs/env_logger/*/env_logger/)
|
|
|
|
or similar).
|
2017-12-04 05:47:15 +01:00
|
|
|
|
|
|
|
### Usage
|
|
|
|
|
2017-12-27 04:59:41 +01:00
|
|
|
Create `Logger` middleware with the specified `format`.
|
2018-03-28 22:16:01 +02:00
|
|
|
Default `Logger` can be created with `default` method, it uses the default format:
|
2017-12-04 05:47:15 +01:00
|
|
|
|
|
|
|
```ignore
|
2018-01-01 02:26:32 +01:00
|
|
|
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
|
2017-12-04 05:47:15 +01:00
|
|
|
```
|
2018-04-07 00:29:18 +02:00
|
|
|
|
2017-12-04 05:47:15 +01:00
|
|
|
```rust
|
2017-12-06 20:00:39 +01:00
|
|
|
# extern crate actix_web;
|
2018-02-20 21:49:42 +01:00
|
|
|
extern crate env_logger;
|
2018-03-31 09:16:55 +02:00
|
|
|
use actix_web::App;
|
2017-12-27 04:59:41 +01:00
|
|
|
use actix_web::middleware::Logger;
|
2017-12-04 05:47:15 +01:00
|
|
|
|
|
|
|
fn main() {
|
2018-02-20 21:49:42 +01:00
|
|
|
std::env::set_var("RUST_LOG", "actix_web=info");
|
|
|
|
env_logger::init();
|
|
|
|
|
2018-03-31 09:16:55 +02:00
|
|
|
App::new()
|
2017-12-04 05:47:15 +01:00
|
|
|
.middleware(Logger::default())
|
|
|
|
.middleware(Logger::new("%a %{User-Agent}i"))
|
|
|
|
.finish();
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2018-04-07 00:29:18 +02:00
|
|
|
The following is an example of the default logging format:
|
2017-12-04 05:47:15 +01:00
|
|
|
|
|
|
|
```
|
2017-12-27 04:59:41 +01:00
|
|
|
INFO:actix_web::middleware::logger: 127.0.0.1:59934 [02/Dec/2017:00:21:43 -0800] "GET / HTTP/1.1" 302 0 "-" "curl/7.54.0" 0.000397
|
|
|
|
INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800] "GET /index.html HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0" 0.000646
|
2017-12-04 05:47:15 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
### Format
|
|
|
|
|
|
|
|
`%%` The percent sign
|
|
|
|
|
|
|
|
`%a` Remote IP-address (IP-address of proxy if using reverse proxy)
|
|
|
|
|
|
|
|
`%t` Time when the request was started to process
|
|
|
|
|
|
|
|
`%P` The process ID of the child that serviced the request
|
|
|
|
|
|
|
|
`%r` First line of request
|
|
|
|
|
|
|
|
`%s` Response status code
|
|
|
|
|
|
|
|
`%b` Size of response in bytes, including HTTP headers
|
|
|
|
|
2017-12-04 22:32:05 +01:00
|
|
|
`%T` Time taken to serve the request, in seconds with floating fraction in .06f format
|
2017-12-04 05:47:15 +01:00
|
|
|
|
|
|
|
`%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
|
|
|
|
|
2018-04-07 00:29:18 +02:00
|
|
|
To set default response headers, the `DefaultHeaders` middleware can be used. The
|
2018-03-28 22:16:01 +02:00
|
|
|
*DefaultHeaders* middleware does not set the header if response headers already contain
|
2018-04-07 00:29:18 +02:00
|
|
|
a specified header.
|
2017-12-04 05:47:15 +01:00
|
|
|
|
|
|
|
```rust
|
2017-12-06 20:00:39 +01:00
|
|
|
# extern crate actix_web;
|
2018-03-31 09:16:55 +02:00
|
|
|
use actix_web::{http, middleware, App, HttpResponse};
|
2017-12-04 05:47:15 +01:00
|
|
|
|
|
|
|
fn main() {
|
2018-03-31 09:16:55 +02:00
|
|
|
let app = App::new()
|
2017-12-04 05:47:15 +01:00
|
|
|
.middleware(
|
2018-04-03 06:43:50 +02:00
|
|
|
middleware::DefaultHeaders::new()
|
|
|
|
.header("X-Version", "0.2"))
|
2017-12-04 05:47:15 +01:00
|
|
|
.resource("/test", |r| {
|
2018-03-31 08:07:33 +02:00
|
|
|
r.method(http::Method::GET).f(|req| HttpResponse::Ok());
|
|
|
|
r.method(http::Method::HEAD).f(|req| HttpResponse::MethodNotAllowed());
|
2017-12-04 05:47:15 +01:00
|
|
|
})
|
|
|
|
.finish();
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## User sessions
|
2017-12-14 07:36:28 +01:00
|
|
|
|
2018-03-28 22:16:01 +02:00
|
|
|
Actix provides a general solution for session management. The
|
2018-04-07 00:29:18 +02:00
|
|
|
[**SessionStorage**](../actix_web/middleware/struct.SessionStorage.html) middleware can be
|
2018-03-28 22:16:01 +02:00
|
|
|
used with different backend types to store session data in different backends.
|
2018-04-07 00:29:18 +02:00
|
|
|
|
|
|
|
> By default, only cookie session backend is implemented. Other backend implementations
|
|
|
|
> can be added.
|
|
|
|
|
|
|
|
[**CookieSessionBackend**](../actix_web/middleware/struct.CookieSessionBackend.html)
|
|
|
|
uses signed cookies as session storage. `CookieSessionBackend` creates sessions which
|
|
|
|
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a
|
|
|
|
single cookie. An internal server error is generated if a session contains more than 4000 bytes.
|
|
|
|
|
|
|
|
You need to pass a random value to the constructor of `CookieSessionBackend`.
|
|
|
|
This is a private key for cookie session. When this value is changed, all session data is lost.
|
|
|
|
|
|
|
|
> **Note**: anything you write into the session is visible by the user, but it is not modifiable.
|
|
|
|
|
|
|
|
In general, you create a
|
|
|
|
`SessionStorage` middleware and initialize it with specific backend implementation,
|
|
|
|
such as a `CookieSessionBackend`. To access session data,
|
2017-12-27 04:59:41 +01:00
|
|
|
[*HttpRequest::session()*](../actix_web/middleware/trait.RequestSession.html#tymethod.session)
|
2018-04-07 00:29:18 +02:00
|
|
|
must be used. This method returns a
|
|
|
|
[*Session*](../actix_web/middleware/struct.Session.html) object, which allows us to get or set
|
2017-12-20 00:44:25 +01:00
|
|
|
session data.
|
|
|
|
|
|
|
|
```rust
|
|
|
|
# extern crate actix;
|
|
|
|
# extern crate actix_web;
|
2018-04-06 17:40:11 +02:00
|
|
|
use actix_web::{server, App, HttpRequest, Result};
|
2017-12-27 04:59:41 +01:00
|
|
|
use actix_web::middleware::{RequestSession, SessionStorage, CookieSessionBackend};
|
2017-12-20 00:44:25 +01:00
|
|
|
|
|
|
|
fn index(mut 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");
|
2018-04-06 17:40:11 +02:00
|
|
|
server::new(
|
2018-03-31 09:16:55 +02:00
|
|
|
|| App::new()
|
2017-12-27 04:59:41 +01:00
|
|
|
.middleware(SessionStorage::new( // <- create session middleware
|
2017-12-20 03:38:02 +01:00
|
|
|
CookieSessionBackend::build(&[0; 32]) // <- create cookie session backend
|
2017-12-20 00:44:25 +01:00
|
|
|
.secure(false)
|
|
|
|
.finish()
|
2017-12-27 04:59:41 +01:00
|
|
|
)))
|
2017-12-20 00:44:25 +01:00
|
|
|
.bind("127.0.0.1:59880").unwrap()
|
|
|
|
.start();
|
2018-02-13 07:56:47 +01:00
|
|
|
# actix::Arbiter::system().do_send(actix::msgs::SystemExit(0));
|
2017-12-20 00:44:25 +01:00
|
|
|
# let _ = sys.run();
|
|
|
|
}
|
|
|
|
```
|
2018-04-03 06:37:00 +02:00
|
|
|
|
|
|
|
## Error handlers
|
|
|
|
|
2018-04-07 00:29:18 +02:00
|
|
|
`ErrorHandlers` middleware allows us to provide custom handlers for responses.
|
2018-04-03 06:37:00 +02:00
|
|
|
|
2018-04-07 00:29:18 +02:00
|
|
|
You can use the `ErrorHandlers::handler()` method to register a custom error handler
|
2018-04-07 01:35:11 +02:00
|
|
|
for a specific status code. You can modify an existing response or create a completly new
|
2018-04-07 00:29:18 +02:00
|
|
|
one. The error handler can return a response immediately or return a future that resolves
|
|
|
|
into a response.
|
2018-04-03 06:37:00 +02:00
|
|
|
|
|
|
|
```rust
|
|
|
|
# extern crate actix_web;
|
|
|
|
use actix_web::{
|
|
|
|
App, HttpRequest, HttpResponse, Result,
|
|
|
|
http, middleware::Response, middleware::ErrorHandlers};
|
|
|
|
|
|
|
|
fn render_500<S>(_: &mut HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
|
|
|
let mut builder = resp.into_builder();
|
|
|
|
builder.header(http::header::CONTENT_TYPE, "application/json");
|
|
|
|
Ok(Response::Done(builder.into()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let app = App::new()
|
|
|
|
.middleware(
|
|
|
|
ErrorHandlers::new()
|
|
|
|
.handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500))
|
|
|
|
.resource("/test", |r| {
|
|
|
|
r.method(http::Method::GET).f(|_| HttpResponse::Ok());
|
|
|
|
r.method(http::Method::HEAD).f(|_| HttpResponse::MethodNotAllowed());
|
|
|
|
})
|
|
|
|
.finish();
|
|
|
|
}
|
|
|
|
```
|