要以类型安全的方式做到这一点有点复杂,因为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地址的部分(技术上,它是远程主机名,但除非您在服务器环境中配置了自定义DNS设置,否则很可能是一个IP地址)是位于
connInfo.remoteAddr
对象中的,这个对象(如上所示)是
Deno.Addr
类型的,看起来像这样:
type Addr = NetAddr | UnixAddr;
这就是问题变得复杂的地方。
Deno.Addr
是
一个区分联合体,由
Deno.NetAddr
和
Deno.UnixAddr
组成(也就是说它可以是其中任意一种),属性
transport
用于区分这两种情况。
interface NetAddr {
hostname: string;
port: number;
transport: "tcp" | "udp";
}
interface UnixAddr {
path: string;
transport: "unix" | "unixpacket";
}
一个网络地址有一个“主机名”属性(该属性的值将是IP地址)和一个“端口”属性,而Unix地址有一个“路径”属性。
监听器是内部创建的,用于支持服务器
实际上只侦听TCP,因此我认为可以安全地假定远程地址是一个网络地址。但是,由于
serve
函数中
回调参数Handler
的类型签名没有明确说明这一点(虽然它应该!),TypeScript并不知道。
因此,作为程序员,您需要确保地址实际上是网络地址,然后才能以类型安全的方式访问网络地址(而不是Unix地址)上的属性。这就是
type assertion function assertIsNetAddr
发挥作用的地方。(类型断言执行运行时测试,如果无法保证条件,则通过引发异常向编译器“保证”条件。)因为作为程序员,您已经比TypeScript编译器更了解(地址在TCP上且将是网络地址),所以可以断言该地址确实是网络地址。然后编译器将允许您将地址用作网络地址。
如果您想在地址不是网络地址的情况下做一些除抛出错误之外的事情:可以使用type predicate作为代码中的条件,而不是断言函数。
这里是一个链接,指向TypeScript Playground,在那里我创建了一个带有我的示例中使用的类型的游乐场,以便您可以进行探索/实验。
最后,(这不是类型安全的)如果你只想使用值而不进行检查(因为你已经做过研究并且有信心永远不会处理非TCP连接),你可以简单地使用类型断言:
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);
};