3.5 Deno HTTP服务器API

从Deno 1.9和更高版本开始,引入了native HTTP服务器API,这些API使用户可以在Deno中创建功能强大的高性能Web服务器。

API尽可能的使用Web标准,简单明了。

这些API目前不稳定,这意味着它们将来可能会会改变,在生产代码中使用前应仔细考虑。他们需要--unstable标记以使其可用。

Listening for a connection

为了接受请求,首先您需要接听网络端口上的连接。要在Deno中执行此操作,请使用Deno.listen()

const server = Deno.listen({ port: 8080 });

ℹ️在提供端口时,Deno假定您将在TCP套接字上侦听并绑定到本地主机。您可以指定transport: "tcp"为更明确,也可以在hostname 属性中提供IP地址或主机名。

如果打开网络端口存在问题,Deno.listen()则会抛出该try ... catch 异常,因此从服务器的角度来看,通常需要将其包装在块中以处理异常,例如端口已在使用中。

您还可以使用以下方法监听TLS连接(例如HTTPS)Deno.listenTls()

const server = Deno.listenTls({
  port: 8443,
  certFile: "localhost.crt",
  keyFile: "localhost.key",
  alpnProtocols: ["h2", "http/1.1"],
});

certFilekeyFile选项是必需的,并指向相应的服务器的证书和密钥文件。它们与Deno的CWD有关。该alpnProtocols属性是可选的,但是如果希望能够在服务器上支持HTTP / 2,请在此处添加协议,因为协议协商是在与客户端和服务器进行TLS协商期间进行的。

生成SSL证书不在本文档的范围之内。网上有很多资源可以解决这个问题。

处理连接

侦听连接后,我们需要处理该连接。的返回值Deno.listen()Deno.listenTls()Deno.Listener其是一个异步迭代其产生了Deno.Conn连接以及提供用于处理连接的几个方法。

要将其用作异步迭代,我们将执行以下操作:

const server = Deno.listen({ port: 8080 });

for await (const conn of server) {
  // ...handle the connection...
}

进行的每个连接都会产生一个Deno.Conn分配给conn。然后可以对连接进行进一步的处理。

.accept()侦听器上还有一种可以使用的方法:

const server = Deno.listen({ port: 8080 });

while (true) {
  const conn = server.accept();
  if (conn) {
    // ... handle the connection ...
  } else {
    // The listener has closed
    break;
  }
}

无论使用异步迭代器还是使用.accept()方法,都可以引发异常,并且可靠的生产代码应使用代码try ... catch 块处理这些异常。特别是在接受TLS连接时,可能存在许多情况,例如无效或未知的证书,这些条件可能会出现在侦听器上,并且可能需要在用户代码中进行处理。

侦听器还具有.close()可用于关闭侦听器的方法。

服务HTTP

接受连接后,就可以使用它Deno.serveHttp()来处理连接上的HTTP请求和响应。Deno.serveHttp()返回 Deno.HttpConn。ADeno.HttpConn就像a一样Deno.Listener,从客户端接收的连接请求异步地产生为a Deno.RequestEvent

要以异步可迭代的方式处理HTTP请求,它将看起来像这样:

const server = Deno.listen({ port: 8080 });

for await (const conn of server) {
  (async () => {
    const httpConn = Deno.serveHttp(conn);
    for await (const requestEvent of httpConn) {
      // ... handle requestEvent ...
    }
  })();
}

Deno.HttpConn还具有所述方法.nextRequest()可用于以等待下一个请求。它看起来像这样:

const server = Deno.listen({ port: 8080 });

while (true) {
  const conn = server.accept();
  if (conn) {
    (async () => {
      const httpConn = Deno.serveHttp(conn);
      while (true) {
        const requestEvent = await httpConn.nextRequest();
        if (requestEvent) {
          // ... handle requestEvent ...
        } else {
          // the connection has finished
          break;
        }
      }
    })();
  } else {
    // The listener has closed
    break;
  }
}

请注意,在两种情况下,我们都使用IIFE创建内部函数来处理每个连接。如果我们在与接收连接相同的功能范围内等待HTTP请求,我们将阻止接受其他连接,这将使我们的服务器似乎“冻结”。在实践中,一起拥有单独的功能可能更有意义:

async function handle(conn: Deno.Conn) {
  const httpConn = Deno.serveHttp(conn);
  for await (const requestEvent of httpConn) {
    // ... handle requestEvent
  }
}

const server = Deno.listen({ port: 8080 });

for await (const conn of server) {
  handle(conn);
}

在此后的示例中,我们将重点关注示例handle()函数中将发生的情况,并删除侦听和连接“样板”。

HTTP请求和响应

Deno中的HTTP请求和响应本质上是Web标准Fetch API的逆过程 。Deno HTTP Server API和Fetch API利用 RequestResponse对象类。因此,如果您熟悉Fetch API,则只需要将它们翻转一下,现在它已成为服务器API。

如上所述,Deno.HttpConn异步产生 Deno.RequestEvents。这些请求事件包含一个.request属性和一个 .respondWith()方法。

.request属性是具有Request有关请求信息的类的实例。例如,如果我们想知道所请求的URL路径,我们将执行以下操作:

async function handle(conn: Deno.Conn) {
  const httpConn = Deno.serveHttp(conn);
  for await (const requestEvent of httpConn) {
    const url = new URL(requestEvent.request.url);
    console.log(`path: ${url.path}`);
  }
}

.respondWith()方法是我们完成请求的方式。该方法可以使用一个Response对象,Promise也可以使用一个通过Response 对象解析的对象。用基本的“ hello world”进行响应如下所示:

async function handle(conn: Deno.Conn) {
  const httpConn = Deno.serveHttp(conn);
  for await (const requestEvent of httpConn) {
    await requestEvent.respondWith(new Response("hello world"), {
      status: 200,
    });
  }
}

请注意,我们等待了该.respondWith()方法。这不是必需的,但实际上,处理响应中的任何错误都会导致从方法返回的承诺被拒绝,就像客户端在发送所有响应之前断开连接一样。尽管您的应用程序可能不需要做任何事情,但是不处理拒绝将导致发生“未处理的拒绝”,这将终止Deno进程,这对于服务器而言不是那么好。另外,您可能需要等待返回的承诺,以便确定何时对请求/响应周期进行任何清理。

Web标准Response对象非常强大,可以轻松创建对客户端的复杂且丰富的响应,并且Deno努力提供一个Response 与Web标准尽可能紧密匹配的对象,因此,如果您想知道如何发送特定的响应,请结帐。淘汰Web标准的文档 Response

HTTP / 2支持

在Deno运行时中,HTTP / 2支持实际上是透明的。通常,在通过ALPN建立TLS连接期间,客户端和服务器之间会协商HTTP / 2 。要启用此功能,您需要在通过该alpnProtocols属性开始侦听时提供要支持的协议。这将使在建立连接时进行协商。例如:

const server = Deno.listenTls({
  port: 8443,
  certFile: "localhost.crt",
  keyFile: "localhost.key",
  alpnProtocols: ["h2", "http/1.1"],
});

协议按优先顺序提供。实际上,当前仅支持两种协议,即HTTP / 2和HTTP / 1.1,分别表示为h2http/1.1

当前,Deno不支持通过Upgrade标头将纯文本HTTP / 1.1连接升级为HTTP / 2明文连接(请参阅: #10275),因此HTTP / 2支持仅通过TLS / HTTPS连接可用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容