Серия Node.js — сборка сервера статических ресурсов

Node.js сервер JavaScript браузер NPM
Серия Node.js — сборка сервера статических ресурсов

Как самообучающийся, который все еще учится на интерфейсе Manman. Я организую свое понимание знаний таким образом, чтобы учиться и делиться ими, и я также надеюсь, что это может быть использовано в качестве справочного материала для всех. Надеюсь вместе со всеми добиться прогресса.Если будут какие-то ошибки, надеюсь, все меня поправят. Большое спасибо!


В предыдущей главе мы построили очень простой сервер «Hello World». В этой главе мы продолжим то, что узнали в предыдущей главе, и попробуем создать сервер, обслуживающий статические ресурсы.

Что такое сервер статических ресурсов?

Что ты скажешь первымстатические ресурсы, что относится кФайлы, которые не изменяются и не генерируются при динамической работе сервера.Так он выглядит до запуска сервера, и когда сервер заканчивает работу, он остается таким.Например, обычно пишетсяjs, css, htmlФайлы можно рассматривать как статические ресурсы.Легко понять, что функция сервера статических ресурсов заключается в предоставлении статических ресурсов клиенту.

Без лишних слов приступим к написанию кода:

Во-первых, мы знаем, что это «сервер».Основываясь на том, что мы узнали в предыдущей главе, нам нужно использоватьhttpМодуль создает HTTP-сервер.

var http = require('http');

var server = http.createServer(function(req, res) {
    // 业务逻辑, 等会儿再写.
});

server.listen(3000, function() {
    console.log("静态资源服务器运行中.");
    console.log("正在监听 3000 端口:")
})

URL-модуль

URL-модуль - Документация

После того, как у нас есть HTTP-сервер, мы можем получить HTTP-запрос, отправленный от клиента.

Сообщение запроса содержит URL-адрес запроса. Как упоминалось ранее, URL-адрес используется для поиска ресурсов в сети. Клиент использует URL-адрес, чтобы указать желаемый ресурс на сервере. Затем, чтобы сервер мог выяснить, что клиент хочет, нам нужно обрабатывать и анализировать URL-адреса. В Node.js мы используемurlмодуль для этого.

Мы знаем, что строки URL — это структурированные строки, содержащие несколько компонентов с разными значениями. пройти черезurl.parse()функция, строка URL-адреса может быть преобразована в объект URL-адреса, свойства которого соответствуют компонентам строки. Как показано ниже.

Screen Shot 2018-10-05 at 2.18.56 AM


Итак, вернемся к коду нашего статического файлового сервера:

доhttp.createServerПеред вызовом функции импортируйтеurlМодуль:

var url = require('url');

Затем проанализируйте URL-адрес запроса на HTTP-сервере. URL-адрес запроса, отправленный клиентом, сохраняется как атрибут вhttp.createServerВ объекте запроса, полученном параметром функции обратного вызова, имя атрибутаurl.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
});

модуль пути

модуль пути - документация

Далее из проанализированного объекта URLurlObjПолучите путь в URL-адресе запроса отсюда. Путь хранится вpathnameв свойствах.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
});

Но имени пути в объекте URL недостаточно, нам также необходимо получить имя каталога (dirname) каталога, в котором находится целевой файл на сервере.

Предположим, структура нашего проекта выглядит следующим образом:

.
├── public
│   ├── index.css
│   └── index.html
└── server.js

Код нашего сервера написан наserver.jsклиент хочет, чтобы запрос был сохранен вpublicв каталогеindex.htmlКогда пользователь вводит URL-адрес в браузере, он знает только, что файл, который он хочет, называетсяindex.html, но «абсолютное местонахождение» этого файла на устройстве, где находится HTTP-сервер, неизвестно, поэтому нам нужно позволить HTTP-серверу выполнить эту часть операции самостоятельно.

Здесь нужно использовать встроенный Node.jspathМодуль предоставляет некоторые служебные функции для работы с путями к файлам и каталогам.
Он очень прост в использовании, прежде всегоhttp.createServerПеред вызовом функции импортируйтеpathМодуль:

var path = require('path');

После этого используемpath.joinЭтот метод объединяет имя каталога, в котором находится целевой файл, с именем пути в URL-адресе запроса. В этом примере все статические файлы, к которым клиент может получить доступ, находятся вpublicв этом каталоге иpublicкаталог сноваserver.jsв каталоге, где находится файл.server.jsСохраненный код нашего сервера.

хочу получитьserver.jsАбсолютный путь каталога во всем устройстве, мы можем вызвать переменную в коде сервера__dirname, которая является переменной, передаваемой, когда текущий файл обертывается оболочкой модуля, которая содержит имя каталога текущего модуля.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
});

Если вы хотите, вы можете использоватьconsole.log(filePathname)Посмотрим, во что превратится полученный от клиента URL-адрес запроса после запуска сервера.

модуль фс

Модуль fs - Документация

Теперь самый важный шаг — чтение целевого файла и возврат файла клиенту.

Нам нужно использовать тот, который поставляется с Node.jsfsв модулеfs.writeметод для достижения этого шага.Первый параметр этого метода — это путь к целевому файлу, последний параметр — функция обратного вызова, а обратный вызов имеет два параметра (ошибка, данные), среди которыхdataсодержимое файла, если возникает ошибкаerrСохраняйте сообщения об ошибках.fs.writeМетод может указывать кодировку символов во втором параметре и возвращает исходный буфер, если он не указан.В данном примере мы не рассматриваем этот пункт.

Тогда конкретный код выглядит следующим образом:

сначала импортироватьfsМодуль, не буду вдаваться в подробности, просто отсылаю к предыдущему, далее код для чтения файла.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
    
    fs.readFile(filePathname, (err, data) => {
        // 如果有问题返回 404
        if (err) {
            res.writeHead(404);
            res.write("404 - File is not found!");
            res.end();
        // 没问题返回文件内容
        } else {
            res.writeHead(200);
            res.write(data);
            res.end();
        }
    })
});

Теперь мы реализовали базовый "статический файловый сервер", который позволяет клиентам запрашивать статические файлы, хранящиеся на сервере. Вы можете попробовать запустить сервер, а затем разрешить браузеру доступhttp://localhost:3000/index.html, Мой эффект таков:

Screen Shot 2018-10-06 at 2.22.08 PM

Установить тип MIME

MIME-документация — MDN Документация по типам содержимого — MDN

Типы многоцелевых расширений почты Интернета (MIME) представляют собой стандартизированный способ выражения «природы» и «формата» документа. Проще говоря, браузеры используют MIME-тип, чтобы определить, как обрабатывать документ, поэтому очень важно установить правильный MIME-тип в заголовке объекта ответа.

Структура MIME очень проста: он состоит из двух строк типа и подтипа.'/'с разделителями, без пробелов Типы MIME не чувствительны к регистру, но традиционно записываются строчными буквами.

Например:

  • text/plain: значение по умолчанию для текстовых файлов. Это означает неизвестный текстовый файл, который, по мнению браузера, может отображаться напрямую.
  • text/html: это тип, который следует использовать для всего содержимого HTML.
  • image/png: тип MIME изображения в формате PNG.

На сервере мы устанавливаемContent-TypeЗначение этого заголовка ответа для указания MIME-типа ресурса, возвращенного в ответе.В Node.js удобно использовать объект ответаwriteHeadметод для установки кода состояния ответа и заголовков ответа.

Если мы хотим ответить на HTML-файл клиенту, то мы должны использовать следующий код:

res.writeHead(200, {"Content-Type":"text/html"});

Вы заметите, что в приведенном выше коде сервера статических ресурсов MIME-тип ресурса ответа не установлен, но если вы попытаетесь запустить сервер, вы обнаружите, что статический ресурс также правильно отображается в браузере.

Причина этого в том, что когда тип MIME отсутствует или клиент считает, что в файле установлен неверный тип MIME, браузер может попытаться угадать тип MIME, просматривая ресурс, который называется «MIME sniffing», может выполнять различные операции. . Итак, чтобы обеспечить согласованное поведение ресурсов под каждым браузером, нам нужно вручную установить тип MIME.

Тогда сначала нам нужно получить файл, который готов ответить клиентусуффикс.

Для этого нам нужно использоватьpathмодульныйparseЭтот метод может преобразовать путь в объект, где свойства соответствуют каждой части пути.

Продолжайте добавлять в код случая статического файлового сервера прямо сейчас:

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
    
    // 解析后对象的 ext 属性中保存着目标文件的后缀名
    var ext = path.parse(urlPathname).ext;
    
    // 读取文件的代码...
});

После получения суффикса файла нам нужно найти его соответствующийMIME-тип, На этом шаге можно легко использовать сторонние модулиMIMEЧтобы добиться этого, вы можете зайти в NPM, чтобы проверить его документацию по использованию.

Для наших текущих нужд нам нужно использовать только модуль MIME.getType()Метод: этот метод принимает строковый параметр (имя суффикса), возвращает соответствующий MIME-тип или возвращает, если его нет.null.

Для использования сначала установите модуль MIME с помощью npm (если вы не создали файл package.json, не забудьте выполнитьnpm init )

npm install mime --save

Установка завершена. Вводим модуль в код сервера, а затем напрямую используем только что полученный суффикс, чтобы найти соответствующий ему тип MIME.

var mime = require('mime');

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
    
    // 解析后对象的 ext 属性中保存着目标文件的后缀名
    var ext = path.parse(urlPathname).ext;
    // 获取后缀对应的 MIME 类型
    var mimeType = mime.getType(ext);
    
    // 读取文件的代码...
});

Хорошо, теперь самое главное — это тип MIME, который у нас есть.writeHeadустановить в методеContent-TypeВот и все.

var server = http.createServer(function(req, res) {
    // 代码省略...
    var mimeType = mime.getType(ext);

    fs.readFile(filePathname, (err, data) => {
        // 如果有问题返回 404
        if (err) {
            res.writeHead(404, { "Content-Type": "text/plain" });
            res.write("404 - File is not found!");
            res.end();
            // 没问题返回文件内容
        } else {
            // 设置好响应头
            res.writeHead(200, { "Content-Type": mimeType });
            res.write(data);
            res.end();
        }
    })

});

Этап Победы ✌️ Теперь запустите сервер, зайдите на него в браузере.localhost:3000/index.htmlПопробуй!

Screen_Shot_2018-10-07_at_11_12_19_PM

могу видеть сейчасContent-Typeнастроен правильно!

перестроить код

Теперь взгляните на свой код, он начинает казаться немного запутанным. Я думаю, вы поняли, что весь код сервера статических файлов делает одну вещь: отвечает на статический файл, который хочет клиент. Этот код Ответственность лежит на single и частота повторного использования очень высока, тогда у нас есть причина инкапсулировать его в модуль.

Не буду вдаваться в детали конкретного процесса, вот код моего модуля:

// readStaticFile.js

// 引入依赖的模块
var path = require('path');
var fs = require('fs');
var mime = require('mime');

function readStaticFile(res, filePathname) {

    var ext = path.parse(filePathname).ext;
    var mimeType = mime.getType(ext);
    
    // 判断路径是否有后缀, 有的话则说明客户端要请求的是一个文件 
    if (ext) {
        // 根据传入的目标文件路径来读取对应文件
        fs.readFile(filePathname, (err, data) => {
            // 错误处理
            if (err) {
                res.writeHead(404, { "Content-Type": "text/plain" });
                res.write("404 - NOT FOUND");
                res.end();
            } else {
                res.writeHead(200, { "Content-Type": mimeType });
                res.write(data);
                res.end();
            }
        });
        // 返回 false 表示, 客户端想要的 是 静态文件
        return true;
    } else {
        // 返回 false 表示, 客户端想要的 不是 静态文件
        return false;
    }
}

// 导出函数
module.exports = readStaticFile;

Модуль для чтения статических файловreadStaticFileПосле упаковки.Мы можем создать новый каталог модулей в каталоге проекта для хранения модулей.Ниже представлена ​​структура моего текущего проекта.

Screen Shot 2018-10-08 at 3.55.58 PM

После инкапсуляции модуля мы можем удалить код для чтения файла в коде сервера, и просто обратиться к модулю напрямую.Вот мой модифицированный код server.js:

// server.js 

// 引入相关模块
var http = require('http');
var url = require('url');
var path = require('path');
var readStaticFile = require('./modules/readStaticFile');

// 搭建 HTTP 服务器
var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
    
    // 读取静态文件
    readStaticFile(res, filePathname);
});

// 在 3000 端口监听请求
server.listen(3000, function() {
    console.log("服务器运行中.");
    console.log("正在监听 3000 端口:")
})

😆 Ну вот и закончился сегодняшний обмен. В следующей статье я расскажу «Как создать маршрутизацию сервера» и «Обработка отправки форм в браузере».

Портал -Серия Node.js — построение маршрутизации и обработка отправленных форм

Подпишитесь, если вам это нравится! О(∩_∩)О Спасибо за поддержку❗️