Shelf能够轻松建立和组合Web服务器和Web服务器的一部分。 怎么样?html
参见example / example.dartjava
import 'package:shelf/shelf.dart' as shelf; import 'package:shelf/shelf_io.dart' as io; void main() { var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests()) .addHandler(_echoRequest); io.serve(handler, 'localhost', 8080).then((server) { print('Serving at http://${server.address.host}:${server.port}'); }); } shelf.Response _echoRequest(shelf.Request request) { return new shelf.Response.ok('Request for "${request.url}"'); }
handler是处理shelf.Request并返回shelf.Response的任何函数。它能够处理请求自己 - 例如,在文件系统上查找请求的URI的静态文件服务器 - 或者它能够进行一些处理并将其转发到另外一个处理程序 - 例如,打印有关信息的记录器 请求和对命令行的响应。web
后一种处理程序称为“中间件”,由于它位于服务器堆栈的中间。中间件能够被认为是一个函数,它接受一个处理程序并将其包装在另外一个处理程序中以提供其余功能。Shelf应用程序一般由多层中间件组成,中间有一个或多个处理程序; shelf.Pipeline类使这种应用程序易于构建。api
一些中间件也能够采用多个处理程序,并为每一个请求调用其中一个或多个。例如,路由中间件可能会根据请求的URI或HTTP方法选择要调用的处理程序,而级联中间件可能会按顺序调用每一个处理程序,直到返回成功的响应。浏览器
在处理程序之间路由请求的中间件应确保更新每一个请求的handlerPath和url。 这容许内部处理程序知道它们在应用程序中的位置,以便它们能够正确地执行本身的路由。 这可使用Request.change()轻松完成:缓存
// 在一个虚构的路由中间件中...... var component = request.url.pathComponents.first; var handler = _handlers[component]; if (handler == null) return new Response.notFound(null); // 建立一个与此相似的新请求,但改成使用[component]以后的任何URL。 return handler(request.change(script: component));
适配器是建立shelf.Request对象的任何代码,将它们传递给处理程序,并处理生成的shelf.Response。在大多数状况下,适配器转发来自底层HTTP服务器的请求和响应; shelf_io.serve就是这种适配器。适配器也可能使用window.location和window.history在浏览器中合成HTTP请求,或者它可能直接将请求从HTTP客户端传递到Shelf处理程序。服务器
适配器必须处理来自处理程序的全部错误,包括返回null响应的处理程序。若是可能的话,它应该将每一个错误打印到控制台,而后就像处理程序返回500响应同样。适配器可能包含500响应的正文数据,但此正文数据不得包含有关发生的错误的信息。这可确保默认状况下意外错误不会致使生产中的内部信息泄露; 若是用户想要返回详细的错误描述,他们应该明确包含中间件来执行此操做。app
适配器应确保处理程序抛出的异步错误不会致使应用程序崩溃,即便future链未报告它们。具体来讲,不该将这些错误传递给根区域的错误处理程序; 可是,若是适配器在另外一个错误区域内运行,则应容许将这些错误传递到该区域。如下函数可用于捕获单一错误不然那将是顶级的:异步
/// 运行[callback] 而且捕获任何顶级错误. /// /// 若是在非根错误区域中调用[this],它将只运行[callback] /// 并返回结果。 不然,它将使用[runZoned]捕获任何错误并将它们传递给[onError]。 catchTopLevelErrors(callback(), void onError(error, StackTrace stackTrace)) { if (Zone.current.inSameErrorZone(Zone.ROOT)) { return runZoned(callback, onError: onError); } else { return callback(); } }
知道本身的URL的适配器应该提供Server接口的实现。async
实现适配器时,必须遵循一些规则。适配器不能将url或handlerPath参数传递给新的shelf.Request; 它应该只传递requestedUri。若是它传递了context参数,则全部Key必须以适配器的包名称开头,后跟句点。若是收到多个具备相同名称的标头,则适配器必须按照RFC 2616第4.2节将它们折叠为用逗号分隔的单个标头。
若是基础请求使用分块传输编码,则适配器必须先解码主体,而后再将其传递给新的shelf.Request,并应删除Transfer-Encoding标头。这能够确保当且仅当标头声明它们是时,才会对邮件正文进行分块。
适配器不得为响应添加或修改任何实体标头。
若是如下条件均不为真,则适配器必须将分块传输编码应用于响应的正文并将其Transfer-Encoding标头设置为chunked:
若是底层服务器没有手动实现,那么适配器可能会发现[addChunkedEncoding()] [addChunkedEncoding]中间件对实现此行为颇有用。
响应HEAD请求时,适配器不得发出实体主体。 不然,它不该以任何方式修改实体主体。
默认状况下,适配器应在响应的Server标头中包含有关其自身的信息。 若是处理程序返回带有Server标头集的响应,则该响应必须优先于适配器的默认标头。
适配器应包含Date标头以及处理程序返回响应的时间。 若是处理程序返回带有Date标头集的响应,则必须优先。
一个帮助程序,它按顺序调用多个处理程序并返回第一个可接受的响应。[...]
默认状况下,若是响应的状态不是404或405,则认为该响应是可接受的; 其余状态代表处理程序理解请求。
若是全部处理程序都返回不可接受的响应,则将返回最终响应。
var handler = new Cascade() .add(webSocketHandler) .add(staticFileHandler) .add(application) .handler;
构造函数
Cascade({Iterable<int> statusCodes, bool shouldCascade(Response response) })
建立一个新的空的cascase
属性
方法
add(Handler handler) → Cascade
noSuchMethod(Invocation invocation) → dynamic
帮助程序,能够轻松组成一组中间件和一个处理程序。
var handler = const Pipeline() .addMiddleware(loggingMiddleware) .addMiddleware(cachingMiddleware) .addHandler(application);
构造函数
Pipeline()
属性
方法
addHandler(Handler handler) → Handler
addMiddleware(Middleware middleware) → Pipeline
noSuchMethod(Invocation invocation) → dynamic
表示要由Shelf应用程序处理的HTTP请求。
构造函数
Request(String method, Uri requestedUri, { String protocolVersion, Map<String, String> headers, String handlerPath, Uri url, dynamic body, Encoding encoding, Map<String, Object> context, void onHijack(void hijack(StreamChannel<List<int>> channel)) })
建立一个新的Request
属性
contentLength → int
encoding → Encoding
isEmpty → bool
mimeType → String
方法
change({Map<String, String> headers, Map<String, Object> context, String path, dynamic body }) → Request
hijack(void callback(StreamChannel<List<int>> channel)) → void
noSuchMethod(Invocation invocation) → dynamic
readAsString([Encoding encoding ]) → Future<String>
Response
处理程序返回的响应
构造函数
Response(int statusCode, { dynamic body, Map<String, String> headers, Encoding encoding, Map<String, Object> context })
Response.forbidden(dynamic body, { Map<String, String> headers, Encoding encoding, Map<String, Object> context })
Response.found(dynamic location, { dynamic body, Map<String, String> headers, Encoding encoding, Map<String, Object> context })
Response.internalServerError({dynamic body, Map<String, String> headers, Encoding encoding, Map<String, Object> context })
Response.movedPermanently(dynamic location, { dynamic body, Map<String, String> headers, Encoding encoding, Map<String, Object> context })
Response.notFound(dynamic body, { Map<String, String> headers, Encoding encoding, Map<String, Object> context })
Response.notModified({Map<String, String> headers, Map<String, Object> context })
Response.ok(dynamic body, { Map<String, String> headers, Encoding encoding, Map<String, Object> context })
Response.seeOther(dynamic location, { dynamic body, Map<String, String> headers, Encoding encoding, Map<String, Object> context })
属性
contentLength → int
encoding → Encoding
isEmpty → bool
mimeType → String
方法
change({Map<String, String> headers, Map<String, Object> context, dynamic body }) → Response
noSuchMethod(Invocation invocation) → dynamic
readAsString([Encoding encoding ]) → Future<String>
Server
具备具体URL的适配器
“适配器”的最基本定义包括将传入请求传递给处理程序并将其响应传递给某个外部客户端的任何函数,可是,在实践中,大多数适配器也是服务器 - 也就是说,它们正在处理对某个已知URL进行的请求
此接口以通常方式表示这些服务器。 它对于编写须要知道本身的URL而不将该代码紧密耦合到单个服务器实现的代码颇有用
这个接口有两个内置的实现。 您可使用IOServer建立由dart:io支持的服务器,或者您可使用ServerHandler建立由普通Handler支持的服务器
此接口的实现负责确保成员按照文档的方式工做
Implemented by IOServer
构造函数
Server()
属性
方法
noSuchMethod(Invocation invocation) → dynamic
链接的服务器和处理程序对
处理程序的请求一旦可用就会发送到服务器的挂载处理程序。这用于公开其实是较大URL空间的一部分的虚拟服务器。
构造函数
ServerHandler(Uri url, { dynamic onClose() })
属性
方法
noSuchMethod(Invocation invocation) → dynamic
中间件addChunkedEncoding final
若是如下条件均不属实,中间件将分块传输编码添加到响应中
提供Content-Length标头。 Content-Type标头指示MIME类型multipart / byteranges。Transfer-Encoding标头已包含分块编码
这适用于Shelf适配器而非最终用户
final addChunkedEncoding = createMiddleware(responseHandler: (response) { if (response.contentLength != null) return response; if (response.statusCode < 200) return response; if (response.statusCode == 204) return response; if (response.statusCode == 304) return response; if (response.mimeType == 'multipart/byteranges') return response; // We only check the last coding here because HTTP requires that the chunked // encoding be listed last. var coding = response.headers['transfer-encoding']; if (coding != null && !equalsIgnoreAsciiCase(coding, 'identity')) { return response; } return response.change( headers: {'transfer-encoding': 'chunked'}, body: chunkedCoding.encoder.bind(response.read())); })
Middleware createMiddleware ({ FutureOr<Response> requestHandler( Request request ), FutureOr<Response> responseHandler( Response response ), FutureOr<Response> errorHandler( dynamic error, StackTrace stackTrace ) })
使用提供的函数建立中间件。
若是提供,requestHandler将收到一个请求。 它能够经过返回Response或Future<Response>来响应请求。对于部分requestHandler也能够返回null,货所有请求被发送到内部处理程序
若是提供,则使用内部处理程序生成的响应调用responseHandler。requestHandler生成的响应不会发送到responseHandler
responseHandler应该返回Response或Future <Response>。 它能够返回它接收的响应参数或建立一个新的Response对象
若是提供,errorHandler会收到内部处理程序抛出的错误。 它不会收到requestHandler或responseHandler抛出的错误,也不会收到HijackExceptions。 它能够返回新响应或抛出错误
实现
Middleware createMiddleware( {FutureOr<Response> requestHandler(Request request), FutureOr<Response> responseHandler(Response response), FutureOr<Response> errorHandler(error, StackTrace stackTrace)}) { if (requestHandler == null) requestHandler = (request) => null; if (responseHandler == null) responseHandler = (response) => response; var onError; if (errorHandler != null) { onError = (error, stackTrace) { if (error is HijackException) throw error; return errorHandler(error, stackTrace); }; } return (Handler innerHandler) { return (request) { return new Future.sync(() => requestHandler(request)).then((response) { if (response != null) return response; return new Future.sync(() => innerHandler(request)) .then((response) => responseHandler(response), onError: onError); }); }; }; }
Middleware logRequests ({ void logger( String msg, bool isError ) })
中间件打印请求的时间,内部处理程序的已用时间,响应的状态代码和请求URI
若是传递了logger,则会为每一个请求调用它。msg参数是一个格式化的字符串,包括请求时间,持续时间,请求方法和请求的路径。抛出异常时,它还包括异常的字符串和堆栈跟踪; 不然,它包括状态代码。isError参数指示消息是否由错误引发
若是未传递logger,则只传递message以进行打印
实现
Middleware logRequests({void logger(String msg, bool isError)}) => (innerHandler) { if (logger == null) logger = _defaultLogger; return (request) { var startTime = new DateTime.now(); var watch = new Stopwatch()..start(); return new Future.sync(() => innerHandler(request)).then((response) { var msg = _getMessage(startTime, response.statusCode, request.requestedUri, request.method, watch.elapsed); logger(msg, false); return response; }, onError: (error, stackTrace) { if (error is HijackException) throw error; var msg = _getErrorMessage(startTime, request.requestedUri, request.method, watch.elapsed, error, stackTrace); logger(msg, true); throw error; }); }; };
FutureOr<Response> Handler ( Request request )
处理请求的函数
例如,静态文件处理程序能够从文件系统读取请求的URI,并将其做为Response的主体返回
包装一个或多个其余处理程序以执行前处理或后处理的处理程序称为“中间件”
处理程序能够直接从HTTP服务器接收请求,或者可能已被其余中间件处理过。相似地,响应能够由HTTP服务器直接返回,或者由其余中间件完成进一步处理
Handler Middleware ( Handler innerHandler )
经过包装处理程序建立新Handler的函数
您能够经过将处理程序包装在中间件中来扩展其功能,中间件能够在请求发送处处理程序以前拦截并处理请求,处理程序发送后的响应或者二者均可以。
因为中间件使用处理程序并返回新的处理程序,所以能够将多个中间件实例组合在一块儿以提供丰富的功能。
中间件的常见用途包括缓存,日志记录和身份验证。
捕获异常的中间件应确保无需修改便可传递HijackExceptions。
可使用createMiddleware建立一个简单的中间件
用于表示请求已被劫持的异常
除了建立可劫持请求的Shelf适配器以外的任何代码都不该捕获此内容。 捕获异常的中间件应确保传递HijackExceptions
另请参见Request.hijack。
Implements Exception
构造函数
const
属性
方法
noSuchMethod(Invocation invocation) → dynamic
由dart:io HttpServer支持的服务器
Implements Server
构造函数
IOServer(HttpServer server)
属性
方法
noSuchMethod(Invocation invocation) → dynamic
静态方法
bind(dynamic address, int port, { int backlog }) → Future<IOServer>
handleRequest(HttpRequest request, Handler handler) → Future
serve(Handler handler, dynamic address, int port, { SecurityContext securityContext, int backlog }) → Future<HttpServer>
serveRequests(Stream<HttpRequest> requests, Handler handler) → void