由于serve 的输入方式,以类型安全的方式执行此操作有点复杂。首先,我将向您展示如何执行此操作的示例,然后我将解释类型。
示例
example.ts:
import {
serve,
type ConnInfo,
type Handler,
type ServeInit,
} from 'https://deno.land/std@0.125.0/http/server.ts';
function assertIsNetAddr (addr: Deno.Addr): asserts addr is Deno.NetAddr {
if (!['tcp', 'udp'].includes(addr.transport)) {
throw new Error('Not a network address');
}
}
function getRemoteAddress (connInfo: ConnInfo): Deno.NetAddr {
assertIsNetAddr(connInfo.remoteAddr);
return connInfo.remoteAddr;
}
const handler: Handler = (request, connInfo) => {
const {hostname, port} = getRemoteAddress(connInfo);
const message = `You connected from the following address: ${hostname}`;
return new Response(message);
};
const init: ServeInit = {port: 8080};
serve(handler, init);
console.log(`Listening on port ${init.port}...\nUse ctrl+c to stop`);
类型
查看serve函数的文档,可以看到它接受两个参数:Handler类型的回调,以及ServeInit类型的一些选项:
async function serve(handler: Handler, options?: ServeInit): Promise<void>;
Handler 回调接受两个参数:Request 和 ConnInfo 类型的对象:
type Handler = (request: Request, connInfo: ConnInfo) => Response | Promise<Response>;
ConnInfo 看起来像这样:
interface ConnInfo {
readonly localAddr: Deno.Addr;
readonly remoteAddr: Deno.Addr;
}
应该有远程 IP 地址的部分(从技术上讲,它是远程主机名,但它很可能是一个 IP 地址,除非您在服务器环境中配置了自定义 DNS 设置)是 connInfo.remoteAddr 的对象,它(你可以在上面看到)是Deno.Addr 类型,看起来像这样:
// in the Deno namespace
type Addr = NetAddr | UnixAddr;
这就是它变得复杂的地方。 Deno.Addr 是Deno.NetAddr 和Deno.UnixAddr 中的discriminated union(这意味着它可以是任何一个),而属性transport用于区分两者。
// in the Deno namespace
interface NetAddr {
hostname: string;
port: number;
transport: "tcp" | "udp";
}
interface UnixAddr {
path: string;
transport: "unix" | "unixpacket";
}
网络地址具有hostname 属性(其值将是IP 地址)和port 属性,而unix 地址具有path 属性。
listener 在内部创建以支持服务器is actually only listening on TCP,因此我认为可以安全地假设远程地址将是网络地址。但是,由于 serve 函数中 Handler 回调参数的类型签名并没有明确说明(尽管它应该!),TypeScript 不知道。
因此,作为程序员,您需要确保该地址实际上是一个网络地址,然后才能以类型访问位于网络地址(而不是 unix 地址)上的属性安全的方式。这就是type assertion function assertIsNetAddr 发挥作用的地方。 (类型断言执行运行时测试,从而为编译器提供条件“保证”:如果条件不能得到保证,则抛出异常。)因为作为程序员,您已经比 TypeScript 编译器了解更多(即该地址在 TCP 上并且将是一个网络地址),您可以断言该地址确实是一个网络地址。然后编译器将允许您使用该地址作为网络地址。
如果您想在地址不是网络地址的情况下抛出错误之外的其他操作:您可以在代码中使用type predicate 作为条件,而不是断言函数。
Here is a link 到 TypeScript Playground,在那里我使用示例中使用的类型创建了一个 Playground,因此您可以探索/实验。
最后,(这不是类型安全的)如果您只想使用没有检查的值(因为您已经完成了研究并且确信您永远不会处理非TCP连接,你可以简单地使用type assertion:
const handler: Handler = (request, connInfo) => {
const {hostname, port} = connInfo.remoteAddr as Deno.NetAddr;
const message = `You connected from the following address: ${hostname}`;
return new Response(message);
};