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

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

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

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

index.jshttp.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, находящийся в этой папке. To есть наш URL следует трактовать как /dir/sub/index.html.

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

index.jshttp.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 ошибку. Сделаем это:

index.jshttp.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);
Задание 1

Рееализуйте описанный статический сервер.

Задание 2

Сделайте так, чтобы 404 страница тоже бралась из файла, например, из файла root/404.html.

Задание 3

При обращении к папке URL со слешем /dir/sub/ и без слеша считаются одинаковым /dir/sub и оба ведут на index папки. Проверьте, как наш сервер справляется с этим. Если есть какие-то проблемы - исправьте их.

Выдача ресурсов

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

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

index.jsfunction 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';
  }
}

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

index.jshttp.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);
Задание 4

Скопируйте функцию getMimeType из учебника. Исправьте код вашего сервера, используя эту функцию.

Задание 5

Сделайте сайт о вашем городе. Пусть сайт состоит из 6-ти HTML страниц. К этим страницам должен быть подключен общий CSS файл, общий JavaScript файл, добавлены картинки. На каждой странице должна быть менюшка, с помощью которой можно будет перемещаться по страницам сайта.

Убираем расширение из URL

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

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

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

Задание 6

Уберите из ваших адресов расширения HTML файлов.