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"],
});
在certFile
和keyFile
选项是必需的,并指向相应的服务器的证书和密钥文件。它们与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利用 Request
和 Response
对象类。因此,如果您熟悉Fetch API,则只需要将它们翻转一下,现在它已成为服务器API。
如上所述,Deno.HttpConn
异步产生 Deno.RequestEvent
s。这些请求事件包含一个.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,分别表示为h2
和http/1.1
。
当前,Deno不支持通过Upgrade
标头将纯文本HTTP / 1.1连接升级为HTTP / 2明文连接(请参阅: #10275),因此HTTP / 2支持仅通过TLS / HTTPS连接可用。