Реализуем статический сервер на NodeJS


Давайте теперь сделаем так, чтобы по URL на нашем сайте искался соответствующий ему HTML файл. Например, если запрашивается /page.html, то мы должны отдать такой же файл, а если запрашивается /dir/test.html, то мы должны отдать файл test.html из папки dir.

Давайте все наши HTML файлы разместим в папке root. Это будет корневая папка нашего сайта и поиск HTML файлов мы будем начинать относительно этой папки.

Реализуем описанное:

http.createServer(async (request, response) => {
if (request.url != '/favicon.ico') {
let path = 'root' + request.url'; // преобразуем URL в путь к файлу
let text = await fs.promises.readFile(path, 'utf8');

response.writeHead(200, {'Content-Type': 'text/html'});
response.write(text);
response.end();
}
}).listen(3000);
Давайте теперь обработаем URL вида /dir/sub/. Как вы видите, в этом адресе не указано имя файла и его расширение. В интернете принято считать, что такой адрес ссылается на файл index.html, находящийся в этой папке. То есть наш URL следует трактовать как /dir/sub/index.html.

Давайте модифицируем наш код:

http.createServer(async (request, response) => {
if (request.url != '/favicon.ico') {
let path = 'root' + request.url';

if ((await fs.promises.stat(path)).isDirectory()) {
path += 'index.html';
}

let text = await fs.promises.readFile(path, 'utf8');

response.writeHead(200, {'Content-Type': 'text/html'});
response.write(text);
response.end();
}
}).listen(3000);
Если запрошенный URL не соответствует файлу на нашем сайте, очевидно, что мы должны отдавать 404 ошибку. Сделаем это:

http.createServer(async (request, response) => {
if (request.url != '/favicon.ico') {
let text;
let status;
let path = 'root' + request.url;

try {
if ((await fs.promises.stat(path)).isDirectory()) {
path += '/index.html';
}

status = 200;
text = await fs.promises.readFile(path, 'utf8');
} catch (err) {
status = 404;
text = 'page not found';
}

response.writeHead(status, {'Content-Type': 'text/html'});
response.write(text);
response.end();
}
}).listen(3000);

Выдача ресурсов
Давайте теперь модифицируем наш сервер так, чтобы кроме HTML файлов, автоматически также выдавались запрошенные ресурсы.

Для начала давайте сделаем функцию, которая будет принимать путь к файлу и по расширению этого файла выдавать его mime тип:

function getMimeType(path) {
let mimes = {
html: 'text/html',
jpeg: 'image/jpeg',
jpg: 'image/jpeg',
png: 'image/png',
svg: 'image/svg+xml',
json: 'application/json',
js: 'text/javascript',
css: 'text/css',
ico: 'image/x-icon',
};

let exts = Object.keys(mimes);
let extReg = new RegExp('\\.(' + exts.join('|') + ')$');

let ext = path.match(extReg)[1];

if (ext) {
return mimes[ext];
} else {
return 'text/plain';
}
}
Имея такую функцию, легко адаптировать наш сервер для выдачи файлов любого типа:

http.createServer(async (request, response) => {
let text;
let status;
let path = 'root' + request.url;

try {
if ((await fs.promises.stat(path)).isDirectory()) {
path += '/index.html';
}

status = 200;
text = await fs.promises.readFile(path, 'utf8');
} catch (err) {
status = 404;
text = 'page not found';
}

response.writeHead(status, {'Content-Type': getMimeType(path)}); // изменение
response.write(text);
response.end();
}).listen(3000);

Убираем расширение из URL
Мы реализовали наш сервер так, что все URL (кроме адресов папок) заканчиваются расширением .html. Однако, в современном мире наличие расширения файла в адресе считается признаком дурного тона и старомодности.

Для красоты необходимо сделать так, чтобы адрес вида /page/ соответствовал файлу root/page.html, адрес вида /dir/page/ - файлу root/dir/page.html, а адрес главной страницы / - файлу root/index.html.

При этом запросы к ресурсам должны работать, как и работали.