# Middleware Actix's middleware system allows us to add additional behavior to request/response processing. Middleware can hook into an incoming request process, enabling us to modify requests as well as halt request processing to return a response early. Middleware can also hook into response processing. Typically, middleware is involved in the following actions: * Pre-process the Request * Post-process a Response * Modify application state * Access external services (redis, logging, sessions) Middleware is registered for each application and executed in same order as registration. In general, a *middleware* is a type that implements the [*Middleware trait*](../actix_web/middlewares/trait.Middleware.html). Each method in this trait has a default implementation. Each method can return a result immediately or a *future* object. The following demonstrates using middleware to add request and response headers: ```rust # extern crate http; # extern crate actix_web; use http::{header, HttpTryFrom}; use actix_web::{App, HttpRequest, HttpResponse, Result}; 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 Middleware 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) -> Result { 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, mut resp: HttpResponse) -> Result { 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() .middleware(Headers) // <- Register middleware, this method can be called multiple times .resource("/", |r| r.f(|_| HttpResponse::Ok())); } ``` > Actix provides several useful middlewares, such as *logging*, *user sessions*, etc. ## Logging Logging is implemented as a middleware. It is common to register a logging middleware as the first middleware for the application. Logging middleware must be registered for each application. The `Logger` middleware uses the standard log crate to log information. You should enable logger for *actix_web* package to see access log ([env_logger](https://docs.rs/env_logger/*/env_logger/) or similar). ### Usage Create `Logger` middleware with the specified `format`. Default `Logger` can be created with `default` method, it uses the default format: ```ignore %a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T ``` ```rust # extern crate actix_web; 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: ``` 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, the `DefaultHeaders` middleware can be used. The *DefaultHeaders* middleware does not set the header if response headers already contain a specified header. ```rust # extern crate actix_web; 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 Actix provides a general solution for session management. The [**SessionStorage**](../actix_web/middleware/struct.SessionStorage.html) middleware can be used with different backend types to store session data in different backends. > By default, only cookie session backend is implemented. Other backend implementations > can be added. [**CookieSessionBackend**](../actix_web/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, [*HttpRequest::session()*](../actix_web/middleware/trait.RequestSession.html#tymethod.session) must be used. This method returns a [*Session*](../actix_web/middleware/struct.Session.html) object, which allows us to get or set session data. ```rust # extern crate actix; # extern crate actix_web; use actix_web::{server, App, HttpRequest, Result}; 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::("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( // <- 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(); } ``` ## Error handlers `ErrorHandlers` middleware allows us to provide custom handlers for responses. You can use the `ErrorHandlers::handler()` method to register a custom error handler for a specific status code. You can modify an existing response or create a completly new one. The error handler can return a response immediately or return a future that resolves into a response. ```rust # extern crate actix_web; use actix_web::{ App, HttpRequest, HttpResponse, Result, http, middleware::Response, middleware::ErrorHandlers}; fn render_500(_: &mut HttpRequest, resp: HttpResponse) -> Result { 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(); } ```