本文/教程的主要目的是演示如何通过使用 NodeJS 构建 API 代理服务器来隐藏公共 API 密钥。
在这篇文章/教程中,我们将使用 NodeJS 创建一个 API 代理服务器,这背后的主要原因是向您展示如何隐藏公共 API 密钥,而不是像我过去那样公开它们。我们所有人都创建了一个应用程序,只是为了了解一个新的库或框架,而实现这一点的最佳方法是使用开放 API(如TMDB)。
这些开放 API 中的大多数都需要在发出请求之前在请求 URL 或标头中添加 API 密钥。无论哪种方式,API 密钥都可能被实际上不拥有此密钥的人窃取和使用。因此,您最终可能会被暂停帐户,或者您的应用程序无法按预期运行。
请记住:API 密钥属于应用程序的服务器端。
话不多说,让我们创建这个 API 代理服务器。为了这篇文章/教程的缘故,我们将使用Wea?ther API。
要求
在开始编码之前,我们需要具备以下条件:
- API 密钥
打开 OpenWeatherMapAPI 网站,创建一个免费帐户,转到我的 API 密钥并创建一个。 - JavaScript和NodeJS的基础知识
- java
依赖项和脚本
创建应用程序的项目文件夹
壳
cd ~/Documents/tutorials
mkdir api-proxy-server && cd api-proxy-server
npm init -y
安装所需的 NPM 包
npm install -S express cors dotenv axios
npm install -D nodemon # for faster development
在 package.json 文件中准备脚本
JSON
{
"scripts": {
"start": "node index.js",
"dev": "nodemon --config nodemon.json"
}
}
让我们看一下项目的文件夹和文件,以便更清晰地了解结构。
server/
├── src
│ ├── utils
│ │ └── env.js
│ └── routes
│ └── index.js
├── package.json
├── nodemon.json
├── index.js
└── .env
3 directories, 6 files
在项目的根目录下创建一个index.js和一个文件。nodemon.json
JSON
// nodemon.json
{
"verbose": true,
"ignore": ["node_modules/"],
"watch": ["./**/*"]
}
在我们开始编辑 entry ( index.js) 文件之前,我们需要创建另外两个有用的文件。第一个是.env我们所有环境变量所在的文件。它将有点可重用,以便能够与除 OpenWeatherMap API 之外的其他公共 API 一起使用。
API_PORT = 1337
API_BASE_URL = "https://api.openweathermap.org/data/2.5/weather"
API_KEY = "1d084c18063128c282ee3b41e91b6740" # not actually api key; use a valid one
另一个文件是src/utils/env.js负责读取和导出所有环境变量的文件。
JavaScript
require("dotenv").config();
module.exports = {
port: Number(process.env.API_PORT) || 3000,
baseURL: String(process.env.API_BASE_URL) || "",
apiKey: String(process.env.API_KEY) || "",
};
现在是时候编辑主文件并向这个代理服务器应用程序添加一些逻辑了。
JavaScript
// imports
const express = require("express");
const cors = require("cors");
const env = require("./src/utils/env");
// Application
const app = express();
// Middlewares
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
// Routes
app.use("/api", require("./src/routes")); // Every request that starts with /api will be handled by this handler
// This route will handle all the requests that are not handled by any other route handler
app.all("*", (request, response, next) => {
return response.status(404).json({ message: "Endpoint not found!" });
});
// Bootstrap server
app.listen(env.port, () => {
console.log(`Server is up and running at http://localhost:${env.port}`);
});
这里的事情很简单;它是一个基本的 ExpressJs 服务器应用程序。在顶部,我们有所需的导入。然后我们有一些需要的中间件,例如JSON中间件,它负责解析带有 JSON 有效负载的传入请求,并且基于body-parser. 接下来,我们在此处管理端点及其处理程序的路由。最后,我们得到了服务器的引导。
让我们看一下/api路由的处理程序。
JavaScript
// server/src/routes/index.js
const router = require("express").Router();
const { default: axios } = require("axios");
const env = require("../utils/env");
// [GET] Current weather data
router.get("/", async (request, response, next) => {
try {
const query = request.query || {};
const params = {
appid: env.apiKey, // required field
...query,
};
const { data } = await axios.get(env.baseURL, { params });
return response.status(200).json({
message: "Current weather data fetched!",
details: { ...data },
});
} catch (error) {
const {
response: { data },
} = error;
const statusCode = Number(data.cod) || 400;
return response
.status(statusCode)
.json({ message: "Bad Request", details: { ...data } });
}
});
module.exports = router;
请求处理函数将GET我们在调用它时提供的查询字段作为输入。
例如,如果我们没有这个 API 代理服务器,我们的请求看起来像这样https://api.openweathermap.org/data/2.5/weather?q=Thessaloniki,Greece&appid={API key},但是现在我们已经有了它,我们的请求看起来像这样http://localhost/api?q=Thessaloniki,Greece。不需要添加必填字段appid,因为代理服务器自己将其添加为请求参数。当我们在 OpenWeatherMap API 上发出请求时,我们尽量不显示 API 密钥。
概括
主要概念是将您的公共 API 密钥隐藏在 API 代理服务器后面,以防止用户窃取您的密钥。为了实现这一点,它是创建端点来包装实际开放 API 的端点,在需要此密钥的每个请求中提供代理服务器内的 API 密钥作为环境变量。这种方法的明显缺点是这是一个耗时的过程,在某些情况下甚至不需要。
您可以在此处获取此 API 代理服务器的源代码。
下一步是什么
我正在考虑向这个代理服务器添加一些功能。其中之一是添加缓存功能,以便更快地提供最近请求的数据。另一种是添加速率限制器,以防止用户过度使用 API 并导致服务器崩溃。
如果您想到了另一种隐藏 API 密钥的解决方案,或者您有任何提示或技巧要告诉我,请发表评论。