Быстрый и простой способ создания шлюза 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