Dart服务器端 shelf包

介绍

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方法选择要调用的处理程序,而级联中间件可能会按顺序调用每一个处理程序,直到返回成功的响应。浏览器

在处理程序之间路由请求的中间件应确保更新每一个请求的handlerPathurl。 这容许内部处理程序知道它们在应用程序中的位置,以便它们能够正确地执行本身的路由。 这可使用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.locationwindow.history在浏览器中合成HTTP请求,或者它可能直接将请求从HTTP客户端传递到Shelf处理程序。服务器

API要求

适配器必须处理来自处理程序的全部错误,包括返回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

Request 要求

实现适配器时,必须遵循一些规则。适配器不能将urlhandlerPath参数传递给新的shelf.Request; 它应该只传递requestedUri。若是它传递了context参数,则全部Key必须以适配器的包名称开头,后跟句点。若是收到多个具备相同名称的标头,则适配器必须按照RFC 2616第4.2节将它们折叠为用逗号分隔的单个标头。

若是基础请求使用分块传输编码,则适配器必须先解码主体,而后再将其传递给新的shelf.Request,并应删除Transfer-Encoding标头。这能够确保当且仅当标头声明它们是时,才会对邮件正文进行分块。

Response 要求

适配器不得为响应添加或修改任何实体标头。

若是如下条件均不为真,则适配器必须将分块传输编码应用于响应的正文并将其Transfer-Encoding标头设置为chunked:

  • 状态代码小于200,或等于204或304。
  • 提供Content-Length标头。
  • Content-Type标头指示MIME类型multipart / byteranges
  • Transfer-Encoding标头设置为identity之外的任何其余标头。

若是底层服务器没有手动实现,那么适配器可能会发现[addChunkedEncoding()] [addChunkedEncoding]中间件对实现此行为颇有用。

响应HEAD请求时,适配器不得发出实体主体。 不然,它不该以任何方式修改实体主体。

默认状况下,适配器应在响应的Server标头中包含有关其自身的信息。 若是处理程序返回带有Server标头集的响应,则该响应必须优先于适配器的默认标头。

适配器应包含Date标头以及处理程序返回响应的时间。 若是处理程序返回带有Date标头集的响应,则必须优先。

shelf

Cascade

一个帮助程序,它按顺序调用多个处理程序并返回第一个可接受的响应。[...]

默认状况下,若是响应的状态不是404或405,则认为该响应是可接受的; 其余状态代表处理程序理解请求。

若是全部处理程序都返回不可接受的响应,则将返回最终响应。

var handler = new Cascade()
    .add(webSocketHandler)
    .add(staticFileHandler)
    .add(application)
    .handler;

构造函数

Cascade({Iterable<int> statusCodes, bool shouldCascade(Response response) })

建立一个新的空的cascase

属性

handler → Handler

  • 将此cascase做为单个处理程序公开
  • read-only

hashCode → int

runtimeType → Type

方法

add(Handler handler) → Cascade

  • 返回一个新的cascase,并将处理程序添加到末尾

noSuchMethod(Invocation invocation) → dynamic

toString() → String

Pipeline

帮助程序,能够轻松组成一组中间件和一个处理程序。

var handler = const Pipeline()
    .addMiddleware(loggingMiddleware)
    .addMiddleware(cachingMiddleware)
    .addHandler(application);

构造函数

Pipeline()

  • const

属性

middleware → Middleware

  • 将此中间件pipeline公开为单个中间件实例

hashCode → int

runtimeType → Type

方法

addHandler(Handler handler) → Handler

  • 若是pipeline中的全部中间件都已经过请求,则返回一个新的Handler,其中handler做为Request的最终处理器。

addMiddleware(Middleware middleware) → Pipeline

  • 返回一个新的Pipeline,其中间件添加到现有的中间件集中

noSuchMethod(Invocation invocation) → dynamic

toString() → String

Request

表示要由Shelf应用程序处理的HTTP请求。

构造函数

Request(String method, Uri requestedUri, { String protocolVersion, Map<StringString> headers, String handlerPath, Uri url, dynamic body, Encoding encoding, Map<StringObject> context, void onHijack(void hijack(StreamChannel<List<int>> channel)) })

建立一个新的Request

属性

canHijack → bool

  • 此请求是否能够被劫持
  • read-only

handlerPath → String

  • 当前处理程序的URL路径
  • final

ifModifiedSince → DateTime

  • 若是此值为非null而且自此日期和时间以来所请求的资源未修改,则服务器应返回304 Not Modified响应
  • read-only

method → String

  • HTTP请求方法,例如“GET”或“POST”
  • final

protocolVersion → String

  • 请求中使用的HTTP协议版本,“1.0”或“1.1”。
  • final

requestedUri → Uri

  • 原始的Uri请求
  • final

url → Uri

  • 从当前处理程序到请求的资源的URL路径,相对于handlerPath,以及任何查询参数
  • final

contentLength → int

  • 标题中content-length字段的内容
  • read-only, inherited

context → Map<StringObject>

  • 中间件和处理程序可使用的额外上下文
  • final, inherited

encoding → Encoding

  • 消息正文的编码
  • read-only, inherited

hashCode → int

headers → Map<StringString>

  • HTTP标头
  • final, inherited

isEmpty → bool

  • 若是为true,则read返回的流将不会发出任何字节
  • read-only, inherited

mimeType → String

  • 消息的MIME类型
  • read-only, inherited

runtimeType → Type

方法

change({Map<StringString> headers, Map<StringObject> context, String path, dynamic body }) → Request

  • 经过复制现有值并应用指定的更改来建立新的请求

hijack(void callback(StreamChannel<List<int>> channel)) → void

  • 控制底层请求套接字

noSuchMethod(Invocation invocation) → dynamic

read() → Stream<List<int>>

  • 返回表示正文的Stream
  • inherited

readAsString([Encoding encoding ]) → Future<String>

  • 返回包含Body做为String的Future
  • inherited

toString() → String

Response

处理程序返回的响应

构造函数

Response(int statusCode, { dynamic body, Map<StringString> headers, Encoding encoding, Map<StringObject> context })

  • 使用给定的statusCode构造HTTP响应

Response.forbidden(dynamic body, { Map<StringString> headers, Encoding encoding, Map<StringObject> context })

  • 构造403 Forbidden响应

Response.found(dynamic location, { dynamic body, Map<StringString> headers, Encoding encoding, Map<StringObject> context })

  • 构造302 Found响应

Response.internalServerError({dynamic body, Map<StringString> headers, Encoding encoding, Map<StringObject> context })

  • 构造500内部服务器错误响应

Response.movedPermanently(dynamic location, { dynamic body, Map<StringString> headers, Encoding encoding, Map<StringObject> context })

  • 构造301 Moved Permanently响应

Response.notFound(dynamic body, { Map<StringString> headers, Encoding encoding, Map<StringObject> context })

  • 构造404 Not Found响应

Response.notModified({Map<StringString> headers, Map<StringObject> context })

  • 构造304 Not Modified响应

Response.ok(dynamic body, { Map<StringString> headers, Encoding encoding, Map<StringObject> context })

  • 构造200 OK响应

Response.seeOther(dynamic location, { dynamic body, Map<StringString> headers, Encoding encoding, Map<StringObject> context })

  • 构造一个303见其余响应

属性

expires → DateTime

  • 应将响应数据视为过期的日期和时间
  • read-only

lastModified → DateTime

  • 上次修改响应数据源的日期和时间
  • read-only

statusCode → int

  • 响应的HTTP状态代码
  • final

contentLength → int

  • 标题中content-length字段的内容
  • read-only, inherited

context → Map<StringObject>

  • 中间件和处理程序可使用的额外上下文
  • final, inherited

encoding → Encoding

  • 消息正文的编码
  • read-only, inherited

hashCode → int

headers → Map<StringString>

  • HTTP标头
  • final, inherited

isEmpty → bool

  • 若是为true,则read返回的流将不会发出任何字节
  • read-only, inherited

mimeType → String

  • 消息的MIME类型
  • read-only, inherited

runtimeType → Type

方法

change({Map<StringString> headers, Map<StringObject> context, dynamic body }) → Response

  • 经过复制现有值并应用指定的更改来建立新的响应

noSuchMethod(Invocation invocation) → dynamic

read() → Stream<List<int>>

  • 返回表示正文的Stream
  • inherited

readAsString([Encoding encoding ]) → Future<String>

  • 返回包含Body做为String的Future
  • inherited

toString() → String

Server 

具备具体URL的适配器

“适配器”的最基本定义包括将传入请求传递给处理程序并将其响应传递给某个外部客户端的任何函数,可是,在实践中,大多数适配器也是服务器 - 也就是说,它们正在处理对某个已知URL进行的请求

此接口以通常方式表示这些服务器。 它对于编写须要知道本身的URL而不将该代码紧密耦合到单个服务器实现的代码颇有用

这个接口有两个内置的实现。 您可使用IOServer建立由dart:io支持的服务器,或者您可使用ServerHandler建立由普通Handler支持的服务器

此接口的实现负责确保成员按照文档的方式工做

Implemented by IOServer

构造函数

Server()

属性

url → Uri

  • 服务器的URL
  • read-only

hashCode → int

runtimeType → Type

方法

close() → Future

  • 关闭服务器并返回在释放全部资源时完成的Future

mount(Handler handler) → void

  • 处理程序挂载为此服务器的基本处理程序

noSuchMethod(Invocation invocation) → dynamic

  • 访问不存在的方法或属性时调用

toString() → String

  • 返回此对象的字符串表示形式

ServerHandler

链接的服务器和处理程序对

处理程序的请求一旦可用就会发送到服务器的挂载处理程序。这用于公开其实是较大URL空间的一部分的虚拟服务器。

构造函数

ServerHandler(Uri url, { dynamic onClose() })

  • 使用给定的URL和Handler建立一个新的链接的服务器对

属性

handler → Handler

  • 处理程序
  • read-only

server → Server

  • 服务器
  • read-only

hashCode → int

runtimeType → Type

方法

noSuchMethod(Invocation invocation) → dynamic

toString() → String

属性

addChunkedEncodin顶级属性

中间件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()));
})

方法

createMiddleware

Middleware createMiddleware ({
     FutureOr<Response> requestHandler(
        Request request
   ),
     FutureOr<Response> responseHandler(
        Response response
   ),
     FutureOr<Response> errorHandler(
        dynamic error,
        StackTrace stackTrace
   )
})

使用提供的函数建立中间件。

若是提供,requestHandler将收到一个请求。 它能够经过返回ResponseFuture<Response>来响应请求。对于部分requestHandler也能够返回null,货所有请求被发送到内部处理程序

若是提供,则使用内部处理程序生成的响应调用responseHandlerrequestHandler生成的响应不会发送到responseHandler

responseHandler应该返回ResponseFuture <Response>。 它能够返回它接收的响应参数或建立一个新的Response对象

若是提供,errorHandler会收到内部处理程序抛出的错误。 它不会收到requestHandlerresponseHandler抛出的错误,也不会收到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);
      });
    };
  };
}

logRequests

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;
        });
      };
    };

类型定义

Handler 

FutureOr<Response> Handler (
     Request request
)

处理请求的函数

例如,静态文件处理程序能够从文件系统读取请求的URI,并将其做为Response的主体返回

包装一个或多个其余处理程序以执行前处理或后处理的处理程序称为“中间件”

处理程序能够直接从HTTP服务器接收请求,或者可能已被其余中间件处理过。相似地,响应能够由HTTP服务器直接返回,或者由其余中间件完成进一步处理

Middleware 

Handler Middleware (
     Handler innerHandler
)

经过包装处理程序建立新Handler的函数

您能够经过将处理程序包装在中间件中来扩展其功能,中间件能够在请求发送处处理程序以前拦截并处理请求,处理程序发送后的响应或者二者均可以。

因为中间件使用处理程序并返回新的处理程序,所以能够将多个中间件实例组合在一块儿以提供丰富的功能。

中间件的常见用途包括缓存,日志记录和身份验证。

捕获异常的中间件应确保无需修改便可传递HijackExceptions

可使用createMiddleware建立一个简单的中间件

异常

HijackException 

用于表示请求已被劫持的异常

除了建立可劫持请求的Shelf适配器以外的任何代码都不该捕获此内容。 捕获异常的中间件应确保传递HijackExceptions

另请参见Request.hijack

Implements  Exception

构造函数

HijackException()

const

属性

hashCode → int

runtimeType → Type

方法

toString() → String

noSuchMethod(Invocation invocation) → dynamic

shelf_io

IOServer

由dart:io HttpServer支持的服务器

Implements Server

构造函数

IOServer(HttpServer server)

属性

server → HttpServer

  • 底层的HttpServer
  • final

url → Uri

  • 服务器的URL
  • read-only

hashCode → int

runtimeType → Type

方法

close() → Future

  • 关闭服务器并返回在释放全部资源时完成的Future

mount(Handler handler) → void

  • 将处理程序挂载为此服务器的基本处理程序

noSuchMethod(Invocation invocation) → dynamic

toString() → String

静态方法

bind(dynamic address, int port, { int backlog }) → Future<IOServer>

  • 调用HttpServer.bind并将结果包装在IOServer

方法

handleRequest(HttpRequest request, Handler handler) → Future

  • 使用handler来处理请求

serve(Handler handler, dynamic address, int port, { SecurityContext securityContext, int backlog }) → Future<HttpServer>

  • 启动一个侦听指定地址和端口的HttpServer,并将请求发送给处理程序

serveRequests(Stream<HttpRequest> requests, Handler handler) → void

  • 提供Http请求流。
相关文章
相关标签/搜索