Expand description
The middleware and handler system module.
It’s highly inspired by The Iron middleware & handler system and intended to work with latest Hyper 0.14.
The middleware and handler system are the fundamental building blocks for handling HTTP requests and generating responses with Hyper.
§Handlers
A Handler will produce a Response given a Request. Most handlers are
functions or closures that accept a &mut Request as an argument and return
an Result containing a Response. An Result is returned instead of
directly returning a Response in order to indicate a possibility of
failure (e.g. database timeout).
Here’s an example of a Handler:
use hyper_middleware::{async_trait, Handler, Request, Response, Result, Body};
struct Application {}
#[async_trait]
impl Handler for Application {
async fn handle(&self, req: &mut Request) -> Result<Response> {
Ok(Response::builder().body(Body::from("¡Hola!")).unwrap())
}
}§Middleware
In situations involving more complex logic, it may be desirable to transform
Requests passed to a Handler or altering Responses sent to the
clients. For example, an authorization step could only allow requests sent
by authorized users to be passed to a Handler and respond to all other
requests with a 403 status code. To faciliate such use cases, Iron’s
middleware system allows Handlers to be extended by defining middleware,
which will perform transformations.
There are three types of middleware:
- A
BeforeMiddlewarealters aRequest. It can be useful for handling control flow (e.g. routing and authorization). - An
AroundMiddlewarewraps aHandler, changing both theResponsepassed to theHandlerand the returnedResponse. - An
AfterMiddlewareperformsResponsepost-processing. It can be used for editing headers or loggingResponses, but it should not be used for changing the body of aResponse.
See the documentation for each middleware for more details.
§Defining the middleware pipeline
the Middlewares chain is a Handler that wraps another Handler. It is used to attach
middleware to the wrapped Handler using a link method corresponding to
each type of middleware. A sample middleware pipeline is shown below:
use hyper::Server;
use hyper_middleware::{async_trait, Handler, BeforeMiddleware, Body, Middlewares, Request, Response, Result, Service};
struct Application {}
#[async_trait]
impl Handler for Application {
async fn handle(&self, req: &mut Request) -> Result<Response> {
let mut resp = Response::new(Body::from("¡Hola!"));
resp.headers_mut().insert(
hyper::header::CONTENT_TYPE,
"text/html; charset=utf-8".parse().unwrap(),
);
Ok(resp)
}
}
struct RequestLoggingMiddleware {}
#[async_trait]
impl BeforeMiddleware for RequestLoggingMiddleware {
async fn before(&self, req: &mut Request) -> Result {
println!("{:?}", req);
Ok(())
}
}
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result {
let mut middlewares = Middlewares::new(Application {});
// Plug in the custom middleware(s)
middlewares.link_before(RequestLoggingMiddleware {});
let addr = ([127, 0, 0, 1], 8080).into();
let service = Service::new(middlewares);
let server = Server::bind(&addr).serve(service);
println!("Listening on http://{}", addr);
// server.await?;
Ok(())
}§The Request Handling Flow
A diagram modeling the entire middleware system process is shown below:
[b] = BeforeMiddleware
[a] = AfterMiddleware
[[h]] = AroundMiddleware
[h] = HandlerWith no errors, the flow looks like:
[b] -> [b] -> [b] -> [[[[h]]]] -> [a] -> [a] -> [a] -> [a]A request first travels through all BeforeMiddleware, then a Response is
generated by the Handler, which can be an arbitrary nesting of
AroundMiddleware, then all AfterMiddleware are called with both the
Request and Response. After all AfterMiddleware have been fired, the
response is written back to the client.
Iron’s error handling system is pragmatic and focuses on tracking two pieces of information for error receivers (other middleware):
- The cause of the error
- The result (what to do about) the error.
The cause of the error is represented simply by the error itself, and the result of the error, representing the action to take in response to the error, is a complete Response, which will be sent at the end of the error flow.
When an error is thrown in Iron by any middleware or handler returning an
Err variant with an Error, the flow of the Request switches to the
error flow, which proceeds to just call the catch method of middleware and
sidesteps the Handler entirely, since there is already a Response in the
error.
A Request can exit the error flow by returning an Ok from any of the catch
methods. This resumes the flow at the middleware immediately following the
middleware which handled the error. It is impossible to “go back” to an
earlier middleware that was skipped.
Generally speaking, returning a 5xx error code means that the error flow
should be entered by raising an explicit error. Dealing with 4xx errors is
trickier, since the server may not want to recognize an error that is
entirely the clients fault; handling of 4xx error codes is up to to each
application and middleware author.
Middleware authors should be cognizant that their middleware may be skipped
during the error flow. Anything that must be done to each Request or
Response should be run during both the normal and error flow by
implementing the catch method to also do the necessary action.
Structs§
- Middlewares
- The middleware chain which can append other middlewares.
Traits§
- After
Middleware AfterMiddlewareare fired after aHandleris called inside of theMiddlewareschain.- Around
Middleware AroundMiddlewareare used to wrap and replace theHandlerin theMiddlewareschain.- Before
Middleware BeforeMiddlewareare fired before aHandleris called inside of a Middlewares.- Handler
Handlers are responsible for handling requests by creatingResponses from thoseRequests.