Как получать POST запросы только от локального хоста?

Есть такая задача:

получить POST запрос через Express
  -> провести необходимые операции с данными
    -> отправить их через socket.io

Загвоздка в том, что Express и socket.io должны быть на одном и том же порте, при этом запросы к экспрессу должны поступать только от localhost и не более.

Можно ли как-то это реализовать, не проверяя IP отдельно при принятии каждого запроса?


Ответы (1 шт):

Автор решения: nörbörnën

Наш проект будет иметь такую структуру:

.
./package.json
./src
./src/index.js
./public
./public/index.html

Создаём проект

$ yarn init -y

Устанавливаем пакеты, которые нужны для работы:

  • express для того чтобы написать простой сервер

  • body-parser для того чтобы раскодировать данные, приходящие в POST-запросах

  • socket.io для работы с websocket-ами

$ yarn add express body-parser socket.io

package.json

{
  "name": "html",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "socket.io": "^3.0.5"
  }
}

По инструкции, опубликованной на сайте socket.io, создаём простой сервер src/index.js.

Он должен уметь:

  • отдавать index.html и скрипты клиентской части socket.io

  • принимать POST-запросы

  • устанавливать websocket-соединение и посылать на client-side сообщения

const http = require('http');
const path = require('path');
const { promisify } = require('util');
const express = require('express');
const bodyParser = require('body-parser');
const { Server: socketioServer } = require('socket.io');


const app = express();
const server = http.createServer(app);
const io = new socketioServer(server);

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//
app.use(
  '/static',
  express.static(process.cwd() + '/static')
);

app.get(
  '/',
  (req, res) => res.sendFile(path.join(process.cwd(), '/public/index.html'))
);

app.post(
  '/',
  async (req, res) => {
    res.status(204).json();

    await promisify(setTimeout)(1000 * Math.random());
    io.sockets.emit('message', JSON.stringify(req.body));
  }
);

//
io.on('connection', (socket) => {
  console.log('a user connected');
  io.sockets.emit('message', 'hello!');
});

//
const port = +(process.env.PORT || 3000);
const hostname = process.env.HOST || 'localhost';
server.listen(port, hostname, () => console.log(`Server running http://${hostname}:${port}`));

Обработчик, о котором вы спрашиваете, выглядит так:

app.post(
  '/',
  async (req, res) => {
    res.status(204).json();

    // ... выполнение каких-то действий ...

    // отсылка сообщения клиенту
    io.sockets.emit('message', JSON.stringify(req.body));
  }
);

Создаём клиентскую страничку public/index.html на которой будет форма и показ сообщений, пришедших по websocket

<!DOCTYPE html>
<html>

  <head>
    <title>Example</title>
    <style>
      body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

      #form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
      #input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
      #input:focus { outline: none; }
      #form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }

      #messages { list-style-type: none; margin: 0; padding: 0; font-size: 8pt; }
      #messages > li { padding: 0.5rem 1rem; white-space: pre; }
      #messages > li:nth-child(odd) { background: #efefef; }
    </style>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io();
      socket.on('message', (data) => {
        const el = document.createElement('li');
        document.getElementById('messages').prepend(el);
        el.innerHTML = `${new Date().toISOString()}:    ${data}`;
      });

      async function syncData(ev) {
        ev.preventDefault();

        const message = document.getElementById('input').value;
        const data = { message };

        try {
          await fetch('/', {
            method: 'POST',
            body: JSON.stringify(data),
            headers: {
              'Accept': 'application/json',
              'Content-Type': 'application/json'
            }
          });
        } catch (err) {
          alert(err.message);
        }

        document.getElementById('form').reset();
      }
    </script>
  </head>

  <body>
    <ul id="messages"></ul>
    <form id="form" action="" onsubmit="return true">
      <input id="input" autocomplete="off" /><button onclick="syncData(event)">Send</button>
    </form>
  </body>

</html>

Запускаем приложение в консоли

$ yarn start

Открываем страницу в браузере введите сюда описание изображения

→ Ссылка