在 Deno 中获取远程客户端 IP 地址

Get remote client IP address in Deno

如何在 Deno 中获取客户端的 IP 地址

我已经使用标准 http 库创建了一个测试服务器,但我想不出提取客户端 IP 的方法。 我需要它作为防止多次提交的安全功能。

In NodeJS/Express there is an ip property of the request object that does the same. req.ip gives the thing I want in Express but what is it's equivalent in Deno?

我的代码是:

import { serve } from "https://deno.land/std@0.125.0/http/server.ts";

serve(
    (req) => {
        console.log(/* the client IP */);
        return new Response("hello");
    },
    { port: 8080 }
);

是否有任何其他解决方法来防止从同一设备进行多次访问?

谢谢

由于 serve 的输入方式,以 type-safe 的方式执行此操作有点复杂。首先,我将向您展示如何操作的示例,然后我将解释类型。

例子

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 function, you can see that it accepts two parameters: a callback of type Handler, and some options of type ServeInit 的文档:

async function serve(handler: Handler, options?: ServeInit): Promise<void>;

Handler callback accepts two parameters: a Request, and an object of type ConnInfo:

type Handler = (request: Request, connInfo: ConnInfo) => Response | Promise<Response>;

ConnInfo 看起来像这样:

interface ConnInfo {
  readonly localAddr: Deno.Addr;
  readonly remoteAddr: Deno.Addr;
}

应该具有远程 IP 地址的部分(从技术上讲,它是远程主机名,但除非您在服务器环境中配置了自定义 DNS 设置,否则它很可能是 IP 地址)是 [=26 处的对象=],它(你可以在上面看到)是 Deno.Addr 类型,它看起来像这样:

// in the Deno namespace

type Addr = NetAddr | UnixAddr;

这就是它变得复杂的地方。 Deno.Addrdiscriminated union of Deno.NetAddrDeno.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 属性.

listener which is created internally to support the server is actually only listening on TCP, so I think it's safe to assume that the remote address will be a net address. However, because the type signature of the Handler callback parameter in the serve 函数没有明确说明(尽管它应该!),TypeScript 不知道。

因此,作为程序员,您需要确保该地址实际上是一个网络地址,然后才能访问网络地址(而不是 unix 地址)上的属性 type-safe 方式 。这就是 type assertion function assertIsNetAddr 发挥作用的地方。 (类型断言执行运行时测试,从而为编译器“保证”条件:如果无法保证条件,则抛出异常。)因为作为程序员,您已经比 TypeScript 编译器了解更多(即地址在 TCP 上并且将是一个网络地址),您可以断言该地址确实是一个网络地址。然后编译器会允许你使用该地址作为网络地址。

If you want to do something besides throwing an Error in the case that the address is not a net address: instead of an assertion function, you can use a type predicate as a condition in your code.

Here is a link 到 TypeScript Playground,我在其中使用我的示例中使用的类型创建了一个游乐场,因此您可以 explore/experiment.


最后,(这不是 type-safe)如果您只想使用没有检查的值(因为您已经完成研究并且确信你永远不会处理 non-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);
};