Простой сервер Node.JS без фреймворков для размещения приложения SPA React

Здесь, на сайте create-react-app, мы находим руководство Express по размещению приложения SPA React (обратите внимание, что * возвращает один index.html при каждом действительном запросе пути):

https://create-react-app.dev/docs/deployment

const express = require('express');
const path = require('path');
const app = express();

app.use(express.static(path.join(__dirname, 'build')));

app.get('/*', function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(9000);

Есть ли способ воспроизвести это поведение на чистом Node.js без фреймворков? То, как он будет обрабатывать каждый допустимый путь, но отвечать ошибкой, когда путь не существует, и т. д. Я не нашел никакой информации об этом, возможно ли это сделать с небольшим количеством кода или разветвлением Express?


person John    schedule 15.12.2020    source источник
comment
что именно вы пытаетесь сделать? просто не иметь экспресс и использовать библиотеку http узла js? Вам вообще нужен узел? просто используйте nginx   -  person azium    schedule 15.12.2020
comment
Только не используйте /*, он перехватит все запросы и вернет ваш index.html. Используйте только свои действительные пути, и ваши неверные получат ошибки: D   -  person Omri Attiya    schedule 15.12.2020
comment
@azium Я создаю API для отдыха и хочу развернуть свой React SPA, используя чистый узел.   -  person John    schedule 15.12.2020
comment
Что такое чистый узел? Как вы думаете, экспресс не является чистым по какой-то причине?   -  person azium    schedule 15.12.2020
comment
@OmriAttiya, но как мне определить, действителен ли путь, если у меня есть один index.html? Я мог обрабатывать неправильные пути в моем приложении для реагирования, хотя. Но другая проблема в том, что я не могу отвечать как html на все, потому что мне нужно также отвечать медиа и активами.   -  person John    schedule 15.12.2020
comment
@azium нет, я просто имею в виду без использования каких-либо фреймворков. Хочу попробовать этот способ (в некотором роде как вызов), потому что мое приложение не будет слишком сложным.   -  person John    schedule 15.12.2020
comment
@John У вас есть здесь примеры того, как это сделать. Просто определите шаблон для ресурсов. Вы также можете прочитать здесь, как обрабатывать недопустимые маршруты.   -  person Omri Attiya    schedule 15.12.2020
comment
Хорошо, вот документация узла для http, вы можете делать с ним все, что вам нужно https://nodejs.org/api/http.html   -  person azium    schedule 15.12.2020


Ответы (1)


Простой ответ: отвечайте на любые GET запросы, которые не соответствуют вашим типам MIME или не имеют расширения, с index.html и обрабатывайте 404 запросы на вашем внешнем интерфейсе.

Я следил за своей лекцией и исходным кодом — https://github.com/HowProgrammingWorks/ServeStatic — и немного изменил его для работы с React SPA. Размещая упрощенный код здесь, я надеюсь, вы поняли идею:

const fs = require('fs');
const http = require('http');
const path = require('path');

// postOrder and updateJson are predifined async fucntions.
const postTypes = {
  '/api/order': postOrder,
  '/api/update_json': updateJson,
};

const STATIC_PATH = path.join(process.cwd(), './public');

const MIME_TYPES = {
  html: 'text/html; charset=UTF-8',
  js: 'application/javascript; charset=UTF-8',
  css: 'text/css',
  json: 'application/json',
  png: 'image/png',
  jpg: 'image/jpeg',
  jpeg: 'image/jpeg',
  ico: 'image/x-icon',
  svg: 'image/svg+xml',
};

const serveFile = name => {
  const filePath = path.join(STATIC_PATH, name);
  if (!filePath.startsWith(STATIC_PATH)) {
    console.log(`Can't be served: ${name}`);
    return null;
  }
  const stream = fs.createReadStream(filePath);
  console.log(`Served: ${name}`);
  return stream;
};

http
  .createServer(async (req, res) => {
    const { url } = req;
    if (req.method === 'GET') {
      const fileExt = path.extname(url).substring(1);
      const mimeType = MIME_TYPES[fileExt] || MIME_TYPES.html;
      res.writeHead(200, { 'Content-Type': mimeType });
      const stream = fileExt === '' ? serveFile('/index.html') : serveFile(url);
      if (stream) stream.pipe(res);
    } else if (req.method === 'POST') {
      const postType = postTypes[url];
      let response = postType ? await postType(req) : `Woops, no ${url} post type!`;
      res.writeHead(response ? 200 : 500, { 'Content-Type': 'text/plain' });
      response ||= `Woops, your response failed to arrive!`;
      res.end(response);
    }
  })
  .listen(3000);

person John    schedule 18.12.2020