如何在 Deno 中获取客户端的IP 地址?
我已经使用标准
http
库创建了一个测试服务器,但我无法找到提取客户端IP的方法。
我需要它作为防止多次提交的安全功能。
在 NodeJS/Express 中,
对象的ip
属性具有相同的作用。request
给出了我在 Express 中想要的东西,但它在 Deno 中的等价物是什么?req.ip
我的代码是:
import { serve } from "https://deno.land/[email protected]/http/server.ts";
serve(
(req) => {
console.log(/* the client IP */);
return new Response("hello");
},
{ port: 8080 }
);
是否有其他解决方法来防止同一设备的多次访问?
谢谢
以类型安全的方式执行此操作有点复杂,因为
serve
的输入方式不同。首先,我将向您展示如何执行此操作的示例,然后我将解释类型。
example.ts
:
import {
serve,
type ConnInfo,
type Handler,
type ServeInit,
} from 'https://deno.land/[email protected]/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
(这意味着它可以是其中之一)的可区分联合,并且属性
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
属性。
内部创建的用于支持服务器的监听器实际上只监听TCP,所以我认为可以安全地假设远程地址将是一个网络地址。但是,由于 Handler
serve
回调参数的类型签名并未明确说明这一点(尽管它应该如此!),TypeScript 并不知道这一点。
因此,作为程序员,您需要确保该地址实际上是一个网络地址,然后才能以类型安全的方式访问网络地址(而不是 unix 地址)上的属性。这就是 type 断言函数 assertIsNetAddr
发挥作用的地方。 (类型断言执行运行时测试,从而向编译器提供条件的“保证”:如果无法保证条件,则抛出异常。)因为作为程序员,您已经比 TypeScript 编译器了解更多(即该地址位于 TCP 上并且将是一个网络地址),您可以断言该地址确实是一个网络地址。然后编译器将允许您使用该地址作为网络地址。
type predicate这里是 TypeScript Playground 的链接
,我在其中使用示例中使用的类型创建了一个 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);
};
Deno.serve
是
推荐的HTTP服务器API。 其 handler
对象作为第二个参数,该对象具有
remoteAddr
属性,该属性具有 hostname
属性。这是客户端 IP 地址。Deno.serve((req, info) => new Response(info.remoteAddr.hostname));
此外,似乎已折旧的
server
也为其处理程序提供了第二个参数。