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

问题描述 投票:0回答:2

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

我已经使用标准

http
库创建了一个测试服务器,但我无法找到提取客户端IP的方法。 我需要它作为防止多次提交的安全功能。

在 NodeJS/Express 中,

ip
对象的
request
属性具有相同的作用。
req.ip
给出了我在 Express 中想要的东西,但它在 Deno 中的等价物是什么?

我的代码是:

import { serve } from "https://deno.land/[email protected]/http/server.ts";

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

是否有其他解决方法来防止同一设备的多次访问?

谢谢

http networking ip deno
2个回答
11
投票

以类型安全的方式执行此操作有点复杂,因为

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); };



0
投票
Deno.serve

推荐的HTTP服务器API

handler

Deno.ServeHandlerInfo

 对象作为第二个参数,该对象具有 
remoteAddr 属性,该属性具有
hostname
属性。这是客户端 IP 地址。
Deno.serve((req, info) => new Response(info.remoteAddr.hostname));

此外,似乎已折旧的
server

也为其处理程序提供了第二个参数

© www.soinside.com 2019 - 2024. All rights reserved.