中间件
kayrx::web的中间件系统允许我们向请求/响应处理添加其他行为。中间件可以挂接到传入的请求流程中,使我们能够修改请求以及暂停请求处理以尽早返回响应.
中间件也可以加入响应处理. 通常,中间件涉及以下操作:
- 预处理请求
- 后处理响应
- 修改应用程序状态
- 访问外部服务(redis, 日志记录, 会话)
中间件被注入每个App,scope或Resource, 并按照相反的顺序执行. 通常,中间件是一种实现 Service trait和Transform trait的类型。特征中的每个方法都有一个默认实现。每个方法都可以立即返回结果或future的对象。
以下演示了如何创建一个简单的中间件:
use std::pin::Pin;
use std::task::{Context, Poll};
use kayrx::service::{Service, Transform};
use kayrx::web::{dev::ServiceRequest, dev::ServiceResponse, Error};
use futures::future::{ok, Ready};
use futures::Future;
// 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 kayrx::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 = Ready<Result<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 = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
println!("Hi from start. You requested: {}", req.path());
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
println!("Hi from response");
Ok(res)
})
}
}
另外,对于简单的用例,可以使用wrap_fn创建小型的临时中间件:
use kayrx::service::Service;
use kayrx::web::{web, App};
use futures::future::FutureExt;
#[kayrx::main]
async fn main() {
let app = App::new()
.wrap_fn(|req, srv| {
println!("Hi from start. You requested: {}", req.path());
srv.call(req).map(|res| {
println!("Hi from response");
res
})
})
.route(
"/index.html",
web::get().to(|| async {
"Hello, middleware!"
}),
);
}
kayrx::web提供了几种有用的中间件,例如 logging, cors, compress .
日志
日志记录是作为中间件实现的。通常将日志记录中间件注册为该应用程序的第一个中间件。记录中间件必须为每个应用注册.
该Logger中间件使用标准log crate 记录信息。您应该为kayrx软件包启用记录器以查看访问日志(env_logger或类似的)
Create Logger middleware with the specified format. Default Logger can be created
with default method, it uses the default format:
Logger使用指定的格式创建中间件, 可以使用defaul方法 创建Logger默认值,它使用默认格式:
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
use kayrx::web::middleware::Logger;
use env_logger;
#[kayrx::main]
async fn main() -> std::io::Result<()> {
use kayrx::web::{App, HttpServer};
std::env::set_var("RUST_LOG", "kayrx::web=info");
env_logger::init();
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.wrap(Logger::new("%a %{User-Agent}i"))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
以下是默认日志记录格式的示例:
INFO:kayrx::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:kayrx::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
格式
%%百分号%a远程IP地址(如果使用反向代理,则为代理的IP地址)%t请求开始处理的时间%P为请求提供服务的子进程ID%r第一行要求%s响应状态码%b响应大小(以字节为单位),包括HTTP标头%T服务请求所用的时间,以秒为单位,浮动分数为.06f格式%D服务请求所花费的时间(以毫秒为单位)%{FOO}i请求头['FOO']%{FOO}o响应头['FOO']%{FOO}eos.environ['FOO']
默认标头
要设置默认响应头,可以使用DefaultHeaders中间件. 如果响应头已经包含指定的报头,所述 DefaultHeaders中间件不设置标题
use kayrx::web::{http, middleware, HttpResponse};
#[kayrx::main]
async fn main() -> std::io::Result<()> {
use kayrx::web::{web, App, HttpServer};
HttpServer::new(|| {
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()),
),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
错误处理
ErrorHandlers 中间件使我们能够提供自定义的响应处理.
您可以使用该ErrorHandlers::handler()方法为特定状态代码注册自定义错误处理, 您可以修改现有响应或创建一个全新的响应, 错误处理程序可以立即返回响应,也可以返回解析为Future的响应.
use kayrx::web::middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers};
use kayrx::web::{dev, http, 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))
}
#[kayrx::main]
async fn main() -> std::io::Result<()> {
use kayrx::web::{web, App, HttpServer};
HttpServer::new(|| {
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())),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
