Сервер Node.js без фреймворка
В этой статье представлен простой статический файловый сервер, построенный на чистом Node.js без использования фреймворка.
Современное состояние Node.js таково, что практически все, что нам нужно, обеспечивается встроенными API и всего несколькими строчками кода.
Пример
Простой статический файловый сервер, построенный с помощью Node.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 | import * as fs from 'node:fs';
import * as http from 'node:http';
import * as path from 'node:path';
const PORT = 8000;
const MIME_TYPES = {
default: 'application/octet-stream',
html: 'text/html; charset=UTF-8',
js: 'application/javascript',
css: 'text/css',
png: 'image/png',
jpg: 'image/jpg',
gif: 'image/gif',
ico: 'image/x-icon',
svg: 'image/svg+xml',
};
const STATIC_PATH = path.join(process.cwd(), './static');
const toBool = [() => true, () => false];
const prepareFile = async (url) => {
const paths = [STATIC_PATH, url];
if (url.endsWith('/')) paths.push('index.htm');
const filePath = path.join(...paths);
const pathTraversal = !filePath.startsWith(STATIC_PATH);
const exists = await fs.promises
.access(filePath)
.then(...toBool);
const found = !pathTraversal && exists;
const streamPath = found
? filePath
: STATIC_PATH + '/404.html';
const ext = path
.extname(streamPath)
.substring(1)
.toLowerCase();
const stream = fs.createReadStream(streamPath);
return { found, ext, stream };
};
http.createServer(async (req, res) => {
const file = await prepareFile(req.url);
const statusCode = file.found ? 200 : 404;
const mimeType =
MIME_TYPES[file.ext] || MIME_TYPES.default;
res.writeHead(statusCode, { 'Content-Type': mimeType });
file.stream.pipe(res);
console.log(`${req.method} ${req.url} ${statusCode}`);
}).listen(PORT);
console.log(`Server running at http://127.0.0.1:${PORT}/`);
|
Разбиение
Следующие строки импортируют внутренние модули Node.js.
| import * as fs from 'node:fs';
import * as http from 'node:http';
import * as path from 'node:path';
|
Далее у нас есть функция для создания сервера. https.createServer возвращает объект Server, который мы можем запустить, прослушивая по PORT.
| http.createServer((req, res) => {
/* handle http requests */
}).listen(PORT);
console.log(`Server running at http://127.0.0.1:${PORT}/`);
|
Асинхронная функция prepareFile возвращает структуру: { found: boolean , ext: string, stream: ReadableStream }. Если файл может быть передан (серверный процесс имеет доступ и уязвимость обхода пути не обнаружена), то в качестве statusCode, указывающего на успех, мы вернем HTTP-статус 200 (в противном случае мы вернем HTTP 404). Обратите внимание, что другие коды статуса можно найти в http.STATUS_CODES. При статусе 404 мы вернем содержимое файла '/404.html.
Расширение запрашиваемого файла будет разобрано и приведено к нижнему регистру. После этого мы выполним поиск в коллекции MIME_TYPES нужных MIME-типов. Если совпадений не найдено, то в качестве типа по умолчанию используется application/octet-stream.
Наконец, если ошибок нет, мы отправляем запрошенный файл. В file.stream будет содержаться поток Readable, который будет передан в res (экземпляр потока Writable).
| res.writeHead(statusCode, { 'Content-Type': mimeType });
file.stream.pipe(res);
|
Ссылки