Web Server Middleware for Dart: Building Flexible and Extensible Server Logic
Published on by Flutter News Hub
Shelf is a powerful Dart library that simplifies the creation of web servers and their components. Its key principles include using simple types, mapping server logic to a single function, and supporting both synchronous and asynchronous processing.
Handlers and Middleware
In Shelf, a Handler is a function that takes a Request and returns a Response. It can either directly handle the request or perform some processing and delegate it to another handler.
Middleware is a type of handler that sits between server components. It intercepts requests, performs additional tasks, and forwards the request to the next handler. This allows for modular and flexible server logic.
Adapters
An Adapter is a component that handles the transport of requests and responses between a handler and an underlying HTTP server. Adapters typically handle error conditions and ensure that the handler receives valid requests.
Benefits of Using Shelf
- Simplicity: Shelf uses a concise and understandable API.
- Flexibility: You can easily mix and match handlers and middleware to create custom server configurations.
- Extensibility: Adapters allow Shelf to be integrated with various HTTP servers.
Code Example
Below is an example of a simple Shelf application that listens on port 8080 and responds with a message:
import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart' as shelf_io; void main() async { var handler = const Pipeline().addMiddleware(logRequests()).addHandler(_echoRequest); var server = await shelf_io.serve(handler, 'localhost', 8080); print('Serving at http://$serverAddress'); } Response _echoRequest(Request request) { return new Response.ok('Request for "${request.url.path}"'); }
Middleware Responsibilities
When creating middleware, you should adhere to the following guidelines:
- Update Request's URL and Path: Middleware should update the handlerPath and url properties of the request to reflect its position in the application.
- Handle Errors: Middleware should handle and log errors from handlers, returning a 500 response with no error details.
- Prevent Top-Level Errors: Middleware should prevent asynchronous errors from handlers from crashing the application by using custom error zones.
Adapter Responsibilities
Adapters must follow these requirements:
- Request Requirements:
- Do not pass url or handlerPath to Request; only pass requestedUri.
- Collapse duplicate headers with the same name into a comma-separated string.
- Decode chunked transfer-encoded bodies and remove the Transfer-Encoding header.
- Response Requirements:
- Do not modify entity headers.
- Apply chunked transfer encoding only if specific conditions are not met.
- Include Server and Date headers with default values.
Inspiration
Shelf draws inspiration from popular middleware frameworks in other languages, such as Connect for Node.js and Rack for Ruby.