前言
本来想随手一搜就出来答案,结果都是些什么博文?可见中文环境下的垃圾信息充斥程度。甚至还要特别安装 request-ip 包?
不能忍,于是只能顺手自己摸索,并贴出自己的答案。
普通情况
只需要使用 @Requst 属性修饰器,即可拿到 Express 的 Request 对象。如下:
import {
Body,
Request,
Post,
Controller,
} from '@nestjs/common';
import { Request as ExpRequest } from 'express';
@Controller('content')
export class ContentController {
@Post()
async postContent(
@Request() req: ExpRequest,
@Body() body: CreateLogDTO,
): Promise<any> {}
}
在 Express Reqeust 对象中,有ip属性,直接索引即可拿到:
const ip = req.ip
拿到的字符串类似:
::ffff:127.0.0.1
前面是 ipv6,后面是 ipv4。相信以目前国内环境来说,大部分的情况下只需要 ipv4。获取ipv4可以用简单的字符串处理方法进行处理:
if (ip.indexOf('::ffff:') !== -1)
{
ip = ip.substring(7)
}
如果需要ipv6也可以反过来操作。操作后IP输出类似:
127.0.0.1
Nginx代理的情况
nginx 代理转发到 backend 下,就不能直接拿 IP 了,否则拿到的都是代理 nginx 的 IP(比如可能是本机 127.0.0.1 也可能是其他的内网地址),也就失去了意义。
这个时候需要如下步骤。
nginx配置携带自身代理的真实请求地址到 header
修改nginx的代理配置,添加如下 header
proxy_set_header X-Real-IP $remote_addr;
注意配置是代理配置,非 nginx 程序配置,有关 nginx 配置可另行解说,在此不再讨论。其中 X-Real-IP 是自定义的 header,可以修改成自己喜欢的、独特的字段。一般自定义的头,都会以 X 开头,以与标准 header 作区分。
在 Nestjs 中读取头
如下(前略):
@Post()
async postContent(
@Request() req: ExpRequest,
@Headers('x-real-ip') headerRealIP: string,
): Promise<any> {
console.log(req.ip);
console.log(headerRealIP);
}
注意这个修饰符里填入的头,是不区分大小写的(RFC 2616 -“HTTP / 1.1”,第4.2),按照习惯填写即可。
AWS的 CloudFront 代理情况
情况类似自定义 nginx 头,CF 会把请求 IP 放在 x-forward-for 头里带上来。需要注意的是头里同样包含了 ipv4 跟 ipv6 两种地址,需要字符串处理分开取用。
关键代码如下:
@Post()
async postContent(
@Request() req: ExpRequest,
@Headers('x-forwarded-for') headerRealIP: string,
): Promise<any> {
let ip = headerRealIP.split(", ")[0]
console.log(ip)
}
综合使用
像阿里云、腾讯云的 CDN 或者 DCDN 代理原理都类似,需要查阅对应平台的自定义标头部分定义。
在代码里,先判断各种平台携带的真实 IP 的标头是否有值,有值则采用对应平台的策略,如果都没值,则使用 Express 里的 Request 下的 ip 值。
这样代码就可以通用于所有环境来获取请求发起的 IP 地址了。