Быстрый и простой способ создания шлюза API
Если вы используете архитектуру микросервисов для своей распределенной системы, вам понадобится шлюз API. Один из хороших способов создания шлюза API - использовать обратный прокси. Node.js хорош для обратного прокси, потому что он быстрый и имеет множество библиотек для создания обратного прокси.
Express - самая популярная платформа для создания веб-приложений. Это также очень хорошо для создания обратного прокси, потому что для Express есть надстройки, которые могут выполнять маршрутизацию для обратного прокси.
Конструкция будет очень простой. Он будет перенаправлять трафик только в наши различные серверные приложения в соответствии с URL-адресом запроса. Кроме того, он должен иметь возможность передавать данные заголовка, файлы и файлы cookie в наши службы в дополнение к полезной нагрузке запроса.
Чтобы сделать все это, мы сначала создаем каркас нашего приложения, запустив генератор приложений Express, следуя инструкциям на странице https://expressjs.com/en/starter/generator.html.
Мы должны запустить npx express-generator
, чтобы сгенерировать нужный нам код.
Я предлагаю использовать nodemon
для запуска нашего приложения в среде разработки, чтобы оно перезапускалось при изменении нашего кода.
Затем нам нужно установить несколько пакетов для выполнения функции обратного прокси и включить CORS, чтобы внешние приложения могли использовать обратный прокси. Для этого запустите npm i express-http-proxy glob node-env-file cookie-parser babel-register body-parser
.
express-http-proxy
- это библиотека обратного прокси-сервера HTTP. cors
- это надстройка для Express, которая включает CORS. cookie-parser
- это надстройка, позволяющая Express анализировать файлы cookie. babel-register
позволяет нам использовать новейшие функции JavaScript. node-env-file
позволяет нам использовать .env
файл для хранения переменных среды. body-parser
будет использоваться для проверки составных запросов. Многостраничные запросы - это запросы, в которых есть файлы. Запросы с файлами не будут проходить body-parser
до перенаправления.
Теперь мы готовы писать код. Мы создаем новый файл с именем helper.js
в папке helpers
, которую мы создаем.
Там мы добавляем:
module.exports = { isMultipartRequest: (req) => { let contentTypeHeader = req.headers['content-type']; return contentTypeHeader && contentTypeHeader.indexOf('multipart') > -1; } }
Эта функция проверяет наличие составных запросов, то есть запросов, отправляемых в виде данных формы. Он может включать текст или файлы.
В app.js
пишем:
require("babel-register"); let express = require('express'); let cors = require('cors') let config = require('./config/config'); let env = require('node-env-file'); let helpers = require('./app/helpers/helpers'); let bodyParser = require('body-parser'); env(__dirname + '/.env'); let app = express(); app.use(cors({ credentials: true, origin: true })); const bodyParserJsonMiddleware = function () { return function (req, res, next) { if (helpers.isMultipartRequest(req)) { return next(); } return bodyParser.json()(req, res, next); }; }; app.use(bodyParserJsonMiddleware()); app.all('*', (req, res, next) => { let origin = req.get('origin'); res.header('Access-Control-Allow-Origin', origin); res.header("Access-Control-Allow-Headers", "X-Requested-With"); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); }); module.exports = require('./config/express')(app, config); app.listen(config.port, () => { console.log('Express server listening on port ' + config.port); });
Код работает, передавая файлы cookie на стороне сервера и позволяя многостраничным запросам не проходить через body-parser
, поскольку это не JSON.
Кроме того, мы разрешаем CORS в блоке ниже:
app.all('*', (req, res, next) => { let origin = req.get('origin'); res.header('Access-Control-Allow-Origin', origin); res.header("Access-Control-Allow-Headers", "X-Requested-With"); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); });
Это позволяет принимать запросы из всех источников. По умолчанию принимаются только запросы от того же хоста, что и серверная часть, поскольку разрешать запросы с других хостов небезопасно. Однако, если мы разрешаем мобильным и автономным интерфейсным веб-приложениям делать запросы через наш обратный прокси-сервер, мы должны разрешить все источники. Он получает источник из заголовка и разрешает выполнение запроса от этого источника.
Затем мы добавляем код для проксирования запросов в controllers/home.js
:
const express = require('express'); let proxy = require("express-http-proxy"); let helpers = require('../helpers/helpers'); let loginAppRoutes = [ '/login*', '/loginms', '/register', '/resetPassword', '/getActivationKey*', '/activateAccount', '/logout', '/reports', '/' ] let uploadRoutes = [ '/uploadlogo*', ] module.exports = (app) => { const proxyMiddleware = () => { return (req, res, next) => { let reqAsBuffer = false; let reqBodyEncoding = true; let contentTypeHeader = req.headers['content-type']; if (helpers.isMultipartRequest(req)) { reqAsBuffer = true; reqBodyEncoding = null; } return proxy(process.env.UPLOAD_URL, { reqAsBuffer: reqAsBuffer, reqBodyEncoding: reqBodyEncoding, parseReqBody: false, proxyReqOptDecorator: (proxyReq) => { return proxyReq; }, proxyReqPathResolver: (req) => { return `${process.env.UPLOAD_APP_URL}/${req.baseUrl}${req.url.slice(1)}`; }, userResDecorator: (rsp, data, req, res) => { res.set('Access-Control-Allow-Origin', req.headers.origin); return data.toString('utf-8'); } })(req, res, next); }; } uploadRoutes.forEach(r => { app.use(r, proxyMiddleware()); }) loginAppRoutes.forEach(r => { app.use(r, proxy(process.env.LOGIN_URL, { proxyReqOptDecorator: (proxyReq) => { return proxyReq; }, proxyReqPathResolver: (req) => { return `${process.env.LOGIN_URL}/${req.baseUrl}${req.url.slice(1)}`; }, userResDecorator: (rsp, data, req, res) => { res.set('Access-Control-Allow-Origin', req.headers.origin); return data.toString('utf-8'); } })); }) };
Мы проверяем наличие многоэлементных запросов с помощью:
if (helpers.isMultipartRequest(req)) { reqAsBuffer = true; reqBodyEncoding = null; }
Он будет передавать запросы с файлами напрямую нашим внутренним API.
Следующий:
proxyReqPathResolver: (req) => { return `${process.env.LOGIN_URL}/${req.baseUrl}${req.url.slice(1)}`; },
здесь устанавливается фактический URL-адрес перенаправления с прокси-сервера на внутренний API. Обратите внимание, что parseReqBody
равно false
, поэтому запросы составных форм не анализируются как JSON.
Переменные process.env
устанавливаются в файле .env
.
В файле .env
у нас есть:
LOGIN_URL='http://urlForYourLoginApp' UPLOAD_URL='http://anotherUrlForYourUploadApp'
Благодаря надстройке обратного прокси-сервера Express - один из лучших вариантов для создания обратного прокси-сервера в Node.
Следуйте за мной в Twitter: https://twitter.com/AuMayeung