mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-25 00:12:59 +01:00
213 lines
7.2 KiB
Markdown
213 lines
7.2 KiB
Markdown
# Middlewares
|
|
|
|
Actix middlewares system allows to add additional behavior to request/response processing.
|
|
Middleware can hook into incoming request process and modify request or halt request
|
|
processing and return response early. Also it can hook into response processing.
|
|
|
|
Typically middlewares involves in following actions:
|
|
|
|
* Pre-process the Request
|
|
* Post-process a Response
|
|
* Modify application state
|
|
* Access external services (redis, logging, sessions)
|
|
|
|
Middlewares are registered for each application and get executed in same order as
|
|
registration order. In general, *middleware* is a type that implements
|
|
[*Middleware trait*](../actix_web/middlewares/trait.Middleware.html). Each method
|
|
in this trait has default implementation. Each method can return result immediately
|
|
or *future* object.
|
|
|
|
Here is example of simple middleware that adds request and response headers:
|
|
|
|
```rust
|
|
# extern crate http;
|
|
# extern crate actix_web;
|
|
use http::{header, HttpTryFrom};
|
|
use actix_web::*;
|
|
use actix_web::middleware::{Middleware, Started, Response};
|
|
|
|
struct Headers; // <- Our middleware
|
|
|
|
/// Middleware implementation, middlewares are generic over application state,
|
|
/// so you can access state with `HttpRequest::state()` method.
|
|
impl<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: &mut HttpRequest<S>) -> Result<Started> {
|
|
req.headers_mut().insert(
|
|
header::CONTENT_TYPE, header::HeaderValue::from_static("text/plain"));
|
|
Ok(Started::Done)
|
|
}
|
|
|
|
/// Method is called when handler returns response,
|
|
/// but before sending http message to peer.
|
|
fn response(&self, req: &mut HttpRequest<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() {
|
|
Application::new()
|
|
.middleware(Headers) // <- Register middleware, this method could be called multiple times
|
|
.resource("/", |r| r.h(httpcodes::HTTPOk));
|
|
}
|
|
```
|
|
|
|
Active provides several useful middlewares, like *logging*, *user sessions*, etc.
|
|
|
|
|
|
## Logging
|
|
|
|
Logging is implemented as middleware.
|
|
It is common to register logging middleware as first middleware for application.
|
|
Logging middleware has to be registered for each application. *Logger* middleware
|
|
uses standard log crate to log information. You should enable logger for *actix_web*
|
|
package to see access log. ([env_logger](https://docs.rs/env_logger/*/env_logger/) or similar)
|
|
|
|
### Usage
|
|
|
|
Create `Logger` middleware with the specified `format`.
|
|
Default `Logger` could be created with `default` method, it uses the default format:
|
|
|
|
```ignore
|
|
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
|
|
```
|
|
```rust
|
|
# extern crate actix_web;
|
|
extern crate env_logger;
|
|
use actix_web::Application;
|
|
use actix_web::middleware::Logger;
|
|
|
|
fn main() {
|
|
std::env::set_var("RUST_LOG", "actix_web=info");
|
|
env_logger::init();
|
|
|
|
Application::new()
|
|
.middleware(Logger::default())
|
|
.middleware(Logger::new("%a %{User-Agent}i"))
|
|
.finish();
|
|
}
|
|
```
|
|
|
|
Here is example of default logging format:
|
|
|
|
```
|
|
INFO:actix_web::middleware::logger: 127.0.0.1:59934 [02/Dec/2017:00:21:43 -0800] "GET / HTTP/1.1" 302 0 "-" "curl/7.54.0" 0.000397
|
|
INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800] "GET /index.html HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0" 0.000646
|
|
```
|
|
|
|
### Format
|
|
|
|
`%%` The percent sign
|
|
|
|
`%a` Remote IP-address (IP-address of proxy if using reverse proxy)
|
|
|
|
`%t` Time when the request was started to process
|
|
|
|
`%P` The process ID of the child that serviced the request
|
|
|
|
`%r` First line of request
|
|
|
|
`%s` Response status code
|
|
|
|
`%b` Size of response in bytes, including HTTP headers
|
|
|
|
`%T` Time taken to serve the request, in seconds with floating fraction in .06f format
|
|
|
|
`%D` Time taken to serve the request, in milliseconds
|
|
|
|
`%{FOO}i` request.headers['FOO']
|
|
|
|
`%{FOO}o` response.headers['FOO']
|
|
|
|
`%{FOO}e` os.environ['FOO']
|
|
|
|
|
|
## Default headers
|
|
|
|
To set default response headers `DefaultHeaders` middleware could be used.
|
|
*DefaultHeaders* middleware does not set header if response headers already contains
|
|
specified header.
|
|
|
|
```rust
|
|
# extern crate actix_web;
|
|
use actix_web::*;
|
|
|
|
fn main() {
|
|
let app = Application::new()
|
|
.middleware(
|
|
middleware::DefaultHeaders::build()
|
|
.header("X-Version", "0.2")
|
|
.finish())
|
|
.resource("/test", |r| {
|
|
r.method(Method::GET).f(|req| httpcodes::HTTPOk);
|
|
r.method(Method::HEAD).f(|req| httpcodes::HTTPMethodNotAllowed);
|
|
})
|
|
.finish();
|
|
}
|
|
```
|
|
|
|
## User sessions
|
|
|
|
Actix provides general solution for session management.
|
|
[*Session storage*](../actix_web/middleware/struct.SessionStorage.html) middleware can be
|
|
use with different backend types to store session data in different backends.
|
|
By default only cookie session backend is implemented. Other backend implementations
|
|
could be added later.
|
|
|
|
[*Cookie session backend*](../actix_web/middleware/struct.CookieSessionBackend.html)
|
|
uses signed cookies as session storage. *Cookie session backend* creates sessions which
|
|
are limited to storing fewer than 4000 bytes of data (as the payload must fit into a
|
|
single cookie). Internal server error get generated if session contains more than 4000 bytes.
|
|
|
|
You need to pass a random value to the constructor of *CookieSessionBackend*.
|
|
This is private key for cookie session. When this value is changed, all session data is lost.
|
|
Note that whatever you write into your session is visible by the user (but not modifiable).
|
|
|
|
In general case, you create
|
|
[*Session storage*](../actix_web/middleware/struct.SessionStorage.html) middleware
|
|
and initializes it with specific backend implementation, like *CookieSessionBackend*.
|
|
To access session data
|
|
[*HttpRequest::session()*](../actix_web/middleware/trait.RequestSession.html#tymethod.session)
|
|
method has to be used. This method returns
|
|
[*Session*](../actix_web/middleware/struct.Session.html) object, which allows to get or set
|
|
session data.
|
|
|
|
```rust
|
|
# extern crate actix;
|
|
# extern crate actix_web;
|
|
use actix_web::*;
|
|
use actix_web::middleware::{RequestSession, SessionStorage, CookieSessionBackend};
|
|
|
|
fn index(mut req: HttpRequest) -> Result<&'static str> {
|
|
// access session data
|
|
if let Some(count) = req.session().get::<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");
|
|
HttpServer::new(
|
|
|| Application::new()
|
|
.middleware(SessionStorage::new( // <- create session middleware
|
|
CookieSessionBackend::build(&[0; 32]) // <- create cookie session backend
|
|
.secure(false)
|
|
.finish()
|
|
)))
|
|
.bind("127.0.0.1:59880").unwrap()
|
|
.start();
|
|
# actix::Arbiter::system().do_send(actix::msgs::SystemExit(0));
|
|
# let _ = sys.run();
|
|
}
|
|
```
|