mirror of
https://github.com/actix/actix-website
synced 2024-11-27 18:12:57 +01:00
First pass at Middlewares.
This commit is contained in:
parent
083522ef19
commit
9a16b6db93
@ -21,48 +21,14 @@ Typically, middleware is involved in the following actions:
|
|||||||
|
|
||||||
Middleware is registered for each application and executed in same order as
|
Middleware is registered for each application and executed in same order as
|
||||||
registration. In general, a *middleware* is a type that implements the
|
registration. In general, a *middleware* is a type that implements the
|
||||||
[*Middleware trait*](../../actix-web/actix_web/middleware/trait.Middleware.html).
|
[*Service trait*](../../actix-web/actix_web/dev/trait.Service.html) and
|
||||||
Each method in this trait has a default implementation. Each method can return
|
[*Transform trait*](../../actix-web/actix_web/dev/trait.Transform.html).
|
||||||
|
Each method in the traits has a default implementation. Each method can return
|
||||||
a result immediately or a *future* object.
|
a result immediately or a *future* object.
|
||||||
|
|
||||||
The following demonstrates using middleware to add request and response headers:
|
The following demonstrates creating a simple middleware:
|
||||||
|
|
||||||
```rust
|
{{< include-example example="middleware" file="main.rs" section="main" >}}
|
||||||
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<S> Middleware<S> for Headers {
|
|
||||||
|
|
||||||
/// Method is called when request is ready. It may return
|
|
||||||
/// future, which should resolve before next middleware get called.
|
|
||||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
|
||||||
Ok(Started::Done)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Method is called when handler returns response,
|
|
||||||
/// but before sending http message to peer.
|
|
||||||
fn response(&self, req: &HttpRequest<S>, mut resp: HttpResponse)
|
|
||||||
-> Result<Response>
|
|
||||||
{
|
|
||||||
resp.headers_mut().insert(
|
|
||||||
header::HeaderName::try_from("X-VERSION").unwrap(),
|
|
||||||
header::HeaderValue::from_static("0.2"));
|
|
||||||
Ok(Response::Done(resp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
// Register middleware, this method can be called multiple times
|
|
||||||
.middleware(Headers)
|
|
||||||
.resource("/", |r| r.f(|_| HttpResponse::Ok()));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Actix provides several useful middlewares, such as *logging*, *user sessions*, etc.
|
> Actix provides several useful middlewares, such as *logging*, *user sessions*, etc.
|
||||||
|
|
||||||
@ -85,21 +51,7 @@ Default `Logger` can be created with `default` method, it uses the default forma
|
|||||||
%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:
|
||||||
|
|
||||||
@ -140,37 +92,23 @@ 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 provides a general solution for session management. The
|
||||||
[**SessionStorage**](../../actix-web/actix_web/middleware/session/struct.SessionStorage.html) middleware can be
|
[**actix-session**](https://docs.rs/actix-session/0.1.1/actix_session/) middleware can be
|
||||||
used with different backend types to store session data in different backends.
|
used with different backend types 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**](../../actix-web/actix_web/middleware/session/struct.CookieSessionBackend.html)
|
||||||
uses cookies as session storage. `CookieSessionBackend` creates sessions which
|
uses cookies as session storage. `CookieSessionBackend` creates sessions which
|
||||||
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a
|
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.
|
single cookie. An internal server error is generated if a session contains more than 4000 bytes.
|
||||||
|
|
||||||
A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor.
|
A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSession` constructor.
|
||||||
|
|
||||||
A *signed* cookie may be viewed but not modified by the client. A *private* cookie may neither be viewed nor modified by the client.
|
A *signed* cookie may be viewed but not modified by the client. A *private* cookie may neither be viewed nor modified by the client.
|
||||||
|
|
||||||
@ -178,41 +116,13 @@ The constructors take a key as an argument. This is the private key for cookie s
|
|||||||
|
|
||||||
In general, you create a
|
In general, you create a
|
||||||
`SessionStorage` middleware and initialize it with specific backend implementation,
|
`SessionStorage` middleware and initialize it with specific backend implementation,
|
||||||
such as a `CookieSessionBackend`. To access session data,
|
such as a `CookieSession`. To access session data,
|
||||||
[*HttpRequest::session()*](../../actix-web/actix_web/middleware/session/trait.RequestSession.html#tymethod.session)
|
[*HttpRequest::session()*](../../actix-web/actix_web/middleware/session/trait.RequestSession.html#tymethod.session)
|
||||||
must be used. This method returns a
|
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*](../../actix-web/actix_web/middleware/session/struct.Session.html) object, which allows us to get or set
|
||||||
session data.
|
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 +133,4 @@ 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> {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -21,4 +21,5 @@ exclude = [
|
|||||||
"requests",
|
"requests",
|
||||||
"responses",
|
"responses",
|
||||||
"testing",
|
"testing",
|
||||||
|
"middleware",
|
||||||
]
|
]
|
||||||
|
11
examples/middleware/Cargo.toml
Normal file
11
examples/middleware/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "middleware"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "1.0"
|
||||||
|
actix-service = "0.4"
|
||||||
|
actix-session = "0.1"
|
||||||
|
futures = "0.1"
|
||||||
|
env_logger = "0.6"
|
16
examples/middleware/src/default_headers.rs
Normal file
16
examples/middleware/src/default_headers.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// <default-headers>
|
||||||
|
use actix_web::{http, middleware, web, App, HttpResponse};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
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()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// </default-headers>
|
25
examples/middleware/src/errorhandler.rs
Normal file
25
examples/middleware/src/errorhandler.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// <error-handler>
|
||||||
|
use actix_web::middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers};
|
||||||
|
use actix_web::{dev, http, web, App, 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
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())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// </error-handler>
|
14
examples/middleware/src/logger.rs
Normal file
14
examples/middleware/src/logger.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// <logger>
|
||||||
|
use actix_web::middleware::Logger;
|
||||||
|
use actix_web::App;
|
||||||
|
use env_logger;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
std::env::set_var("RUST_LOG", "actix_web=info");
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
App::new()
|
||||||
|
.wrap(Logger::default())
|
||||||
|
.wrap(Logger::new("%a %{User-Agent}i"));
|
||||||
|
}
|
||||||
|
// </logger>
|
72
examples/middleware/src/main.rs
Normal file
72
examples/middleware/src/main.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
mod default_headers;
|
||||||
|
mod errorhandler;
|
||||||
|
mod logger;
|
||||||
|
mod user_sessions;
|
||||||
|
// <main>
|
||||||
|
use actix_service::{Service, Transform};
|
||||||
|
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, web, App, 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)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// </main>
|
||||||
|
fn main() {
|
||||||
|
App::new().wrap(SayHi).service(
|
||||||
|
web::resource("/")
|
||||||
|
.to(|| "Hello, middleware! Check the console where the server is run."),
|
||||||
|
);
|
||||||
|
}
|
37
examples/middleware/src/user_sessions.rs
Normal file
37
examples/middleware/src/user_sessions.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// <user-session>
|
||||||
|
use actix_session::{CookieSession, Session};
|
||||||
|
use actix_web::{middleware::Logger, web, App, HttpRequest, HttpServer, Result};
|
||||||
|
|
||||||
|
/// simple index handler with session
|
||||||
|
fn index(session: Session, req: HttpRequest) -> Result<&'static str> {
|
||||||
|
println!("{:?}", req);
|
||||||
|
|
||||||
|
// RequestSession trait is used for session access
|
||||||
|
let mut counter = 1;
|
||||||
|
if let Some(count) = session.get::<i32>("counter")? {
|
||||||
|
println!("SESSION value: {}", count);
|
||||||
|
counter = count + 1;
|
||||||
|
session.set("counter", counter)?;
|
||||||
|
} else {
|
||||||
|
session.set("counter", counter)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok("welcome!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
std::env::set_var("RUST_LOG", "actix_web=info");
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
HttpServer::new(|| {
|
||||||
|
App::new()
|
||||||
|
// enable logger
|
||||||
|
.wrap(Logger::default())
|
||||||
|
// cookie session middleware
|
||||||
|
.wrap(CookieSession::signed(&[0; 32]).secure(false))
|
||||||
|
.service(web::resource("/").to(index))
|
||||||
|
})
|
||||||
|
.bind("127.0.0.1:8080")?
|
||||||
|
.run()
|
||||||
|
}
|
||||||
|
// </user-session>
|
Loading…
Reference in New Issue
Block a user