mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-24 07:53:00 +01:00
added default headers middleware
This commit is contained in:
parent
d35be02587
commit
57fd35ffc1
1
build.rs
1
build.rs
@ -21,7 +21,6 @@ fn main() {
|
||||
"guide/src/qs_7.md",
|
||||
"guide/src/qs_9.md",
|
||||
"guide/src/qs_10.md",
|
||||
"guide/src/qs_11.md",
|
||||
"guide/src/qs_12.md",
|
||||
"guide/src/qs_13.md",
|
||||
]);
|
||||
|
@ -8,7 +8,6 @@
|
||||
- [Application state](./qs_6.md)
|
||||
- [Request & Response](./qs_7.md)
|
||||
- [WebSockets](./qs_9.md)
|
||||
- [User sessions](./qs_10.md)
|
||||
- [Logging](./qs_11.md)
|
||||
- [Middlewares](./qs_10.md)
|
||||
- [Static file handling](./qs_12.md)
|
||||
- [HTTP/2](./qs_13.md)
|
||||
|
@ -1 +1,87 @@
|
||||
# User sessions
|
||||
# Middlewares
|
||||
|
||||
## Logging
|
||||
|
||||
Logging is implemented as middleware. Middlewares get executed in same order as registraton order.
|
||||
It is common to register logging middleware as first middleware for application.
|
||||
Logging middleware has to be registered for each application.
|
||||
|
||||
### Usage
|
||||
|
||||
Create `Logger` middlewares with the specified `format`.
|
||||
Default `Logger` could be created with `default` method, it uses the default format:
|
||||
|
||||
```ignore
|
||||
%a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
|
||||
```
|
||||
```rust
|
||||
extern crate actix_web;
|
||||
use actix_web::Application;
|
||||
use actix_web::middlewares::Logger;
|
||||
|
||||
fn main() {
|
||||
Application::default("/")
|
||||
.middleware(Logger::default())
|
||||
.middleware(Logger::new("%a %{User-Agent}i"))
|
||||
.finish();
|
||||
}
|
||||
```
|
||||
|
||||
Here is example of default logging format:
|
||||
|
||||
```
|
||||
INFO:actix_web::middlewares::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::middlewares::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
|
||||
|
||||
It is possible to set default response headers with `DefaultHeaders` middleware.
|
||||
*DefaultHeaders* middleware does not set header if response headers already contains it.
|
||||
|
||||
```rust
|
||||
extern crate actix_web;
|
||||
use actix_web::*;
|
||||
|
||||
fn main() {
|
||||
let app = Application::default("/")
|
||||
.middleware(
|
||||
middlewares::DefaultHeaders::build()
|
||||
.header("X-Version", "0.2")
|
||||
.finish())
|
||||
.resource("/test", |r| {
|
||||
r.get(|req| httpcodes::HTTPOk);
|
||||
r.handler(Method::HEAD, |req| httpcodes::HTTPMethodNotAllowed);
|
||||
})
|
||||
.finish();
|
||||
}
|
||||
```
|
||||
|
||||
## User sessions
|
||||
|
@ -1,59 +0,0 @@
|
||||
# Logging
|
||||
|
||||
Logging is implemented as middleware. Middlewares get executed in same order as registraton order.
|
||||
It is common to register logging middleware as first middleware for application.
|
||||
Logging middleware has to be registered for each application.
|
||||
|
||||
## Usage
|
||||
|
||||
Create `Logger` middlewares with the specified `format`.
|
||||
Default `Logger` could be created with `default` method, it uses the default format:
|
||||
|
||||
```ignore
|
||||
%a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
|
||||
```
|
||||
```rust
|
||||
extern crate actix_web;
|
||||
use actix_web::Application;
|
||||
use actix_web::middlewares::Logger;
|
||||
|
||||
fn main() {
|
||||
Application::default("/")
|
||||
.middleware(Logger::default())
|
||||
.middleware(Logger::new("%a %{User-Agent}i"))
|
||||
.finish();
|
||||
}
|
||||
```
|
||||
|
||||
Here is example of default logging format:
|
||||
|
||||
```
|
||||
INFO:actix_web::middlewares::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::middlewares::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']
|
@ -303,6 +303,7 @@ impl HttpResponseBuilder {
|
||||
/// By default `ContentEncoding::Auto` is used, which automatically
|
||||
/// negotiates content encoding based on request's `Accept-Encoding` headers.
|
||||
/// To enforce specific encoding, use specific ContentEncoding` value.
|
||||
#[inline]
|
||||
pub fn content_encoding(&mut self, enc: ContentEncoding) -> &mut Self {
|
||||
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
||||
parts.encoding = enc;
|
||||
@ -311,6 +312,7 @@ impl HttpResponseBuilder {
|
||||
}
|
||||
|
||||
/// Set connection type
|
||||
#[inline]
|
||||
pub fn connection_type(&mut self, conn: ConnectionType) -> &mut Self {
|
||||
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
||||
parts.connection_type = Some(conn);
|
||||
@ -319,16 +321,19 @@ impl HttpResponseBuilder {
|
||||
}
|
||||
|
||||
/// Set connection type to Upgrade
|
||||
#[inline]
|
||||
pub fn upgrade(&mut self) -> &mut Self {
|
||||
self.connection_type(ConnectionType::Upgrade)
|
||||
}
|
||||
|
||||
/// Force close connection, even if it is marked as keep-alive
|
||||
#[inline]
|
||||
pub fn force_close(&mut self) -> &mut Self {
|
||||
self.connection_type(ConnectionType::Close)
|
||||
}
|
||||
|
||||
/// Enables automatic chunked transfer encoding
|
||||
#[inline]
|
||||
pub fn enable_chunked(&mut self) -> &mut Self {
|
||||
if let Some(parts) = parts(&mut self.parts, &self.err) {
|
||||
parts.chunked = true;
|
||||
@ -337,6 +342,7 @@ impl HttpResponseBuilder {
|
||||
}
|
||||
|
||||
/// Set response content type
|
||||
#[inline]
|
||||
pub fn content_type<V>(&mut self, value: V) -> &mut Self
|
||||
where HeaderValue: HttpTryFrom<V>
|
||||
{
|
||||
|
113
src/middlewares/defaultheaders.rs
Normal file
113
src/middlewares/defaultheaders.rs
Normal file
@ -0,0 +1,113 @@
|
||||
//! Default response headers
|
||||
use http::{HeaderMap, HttpTryFrom};
|
||||
use http::header::{HeaderName, HeaderValue};
|
||||
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middlewares::{Response, Middleware};
|
||||
|
||||
/// `Middleware` for setting default response headers.
|
||||
///
|
||||
/// This middleware does not set header if response headers already contains it.
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate actix_web;
|
||||
/// use actix_web::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = Application::default("/")
|
||||
/// .middleware(
|
||||
/// middlewares::DefaultHeaders::build()
|
||||
/// .header("X-Version", "0.2")
|
||||
/// .finish())
|
||||
/// .resource("/test", |r| {
|
||||
/// r.get(|req| httpcodes::HTTPOk);
|
||||
/// r.handler(Method::HEAD, |req| httpcodes::HTTPMethodNotAllowed);
|
||||
/// })
|
||||
/// .finish();
|
||||
/// }
|
||||
/// ```
|
||||
pub struct DefaultHeaders(HeaderMap);
|
||||
|
||||
impl DefaultHeaders {
|
||||
pub fn build() -> DefaultHeadersBuilder {
|
||||
DefaultHeadersBuilder{headers: Some(HeaderMap::new())}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for DefaultHeaders {
|
||||
|
||||
fn response(&self, _: &mut HttpRequest, mut resp: HttpResponse) -> Response {
|
||||
for (key, value) in self.0.iter() {
|
||||
if !resp.headers().contains_key(key) {
|
||||
resp.headers_mut().insert(key, value.clone());
|
||||
}
|
||||
}
|
||||
Response::Done(resp)
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure that follows the builder pattern for building `DefaultHeaders` middleware.
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultHeadersBuilder {
|
||||
headers: Option<HeaderMap>,
|
||||
}
|
||||
|
||||
impl DefaultHeadersBuilder {
|
||||
|
||||
/// Set a header.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(match_wild_err_arm))]
|
||||
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
|
||||
where HeaderName: HttpTryFrom<K>,
|
||||
HeaderValue: HttpTryFrom<V>
|
||||
{
|
||||
if let Some(ref mut headers) = self.headers {
|
||||
match HeaderName::try_from(key) {
|
||||
Ok(key) => {
|
||||
match HeaderValue::try_from(value) {
|
||||
Ok(value) => { headers.append(key, value); }
|
||||
Err(_) => panic!("Can not create header value"),
|
||||
}
|
||||
},
|
||||
Err(_) => panic!("Can not create header name"),
|
||||
};
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Finishes building and returns the built `DefaultHeaders` middleware.
|
||||
pub fn finish(&mut self) -> DefaultHeaders {
|
||||
let headers = self.headers.take().expect("cannot reuse middleware builder");
|
||||
DefaultHeaders(headers)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use http::header::CONTENT_TYPE;
|
||||
|
||||
#[test]
|
||||
fn test_default_headers() {
|
||||
let mw = DefaultHeaders::build()
|
||||
.header(CONTENT_TYPE, "0001")
|
||||
.finish();
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
|
||||
let resp = HttpResponse::Ok().finish().unwrap();
|
||||
let resp = match mw.response(&mut req, resp) {
|
||||
Response::Done(resp) => resp,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
||||
|
||||
let resp = HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish().unwrap();
|
||||
let resp = match mw.response(&mut req, resp) {
|
||||
Response::Done(resp) => resp,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002");
|
||||
}
|
||||
}
|
@ -7,7 +7,9 @@ use httpresponse::HttpResponse;
|
||||
|
||||
mod logger;
|
||||
mod session;
|
||||
mod defaultheaders;
|
||||
pub use self::logger::Logger;
|
||||
pub use self::defaultheaders::{DefaultHeaders, DefaultHeadersBuilder};
|
||||
pub use self::session::{RequestSession, Session, SessionImpl, SessionBackend, SessionStorage,
|
||||
CookieSessionError, CookieSessionBackend, CookieSessionBackendBuilder};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user