1. Предварительные знания
Прежде чем понять принцип работы koa-bodyparser, вам сначала нужно понять некоторые знания, связанные с HTTP.
1. Основная часть сообщения
HTTP-сообщения в основном делятся на сообщения-запросы и сообщения-ответы, а koa-bodyparser в основном предназначен для обработки сообщений-запросов.
Сообщение запроса в основном состоит из следующих трех частей:
- заголовок пакета
- пустая строка
- тело сообщения
А тело в koa-bodyparser относится к основной части сообщения в сообщении запроса.
2. Процесс получения основной части сообщения на стороне сервера
Нижний уровень HTTP использует TCP для предоставления надежных услуг потока байтов.Короче говоря, основная часть сообщения будет преобразована в двоичные данные для передачи в сети, поэтому серверу сначала необходимо получить данные двоичного потока.
Когда дело доходит до передачи по сети, конечно, это включает в себя оптимизацию скорости передачи, и одним из методов оптимизации является сжатие и кодирование контента.Обычно используемые методы сжатия и кодирования:
- gzip
- compress
- deflate
- идентификатор (формат кодирования по умолчанию, который не выполняет сжатие или изменения)
Сервер подтвердит, какую кодировку декомпрессии использовать в соответствии с Content-Encoding в информации заголовка сообщения.
Далее вам нужно преобразовать двоичные данные в соответствующие символы, а символы также имеют разные методы кодирования символов.Например, для UTF-8 и GBK, которые имеют огромные различия в обработке китайских символов, для кодирования китайских символов UTF-8 обычно требуется три байта. , тогда как GBK требуется только два байта. Следовательно, также необходимо установить информацию о кодировке символов, используемую Content-Type, в информации заголовка сообщения запроса (по умолчанию используется UTF-8), чтобы сервер мог использовать соответствующие правила символов для декодирования и получения правильные символы строка.
После того, как получит строку, сервер должен снова спросить: Клиент, что вы подразумеваете под этой строкой?
В соответствии с различными сценариями применения клиент будет использовать разные методы кодирования строк. Наиболее распространенными методами кодирования являются:
- Метод кодирования URL: a=1&b=2
- Кодировка JSON: {a:1,b:2}
Клиент установит метод кодирования строк, используемый в атрибуте Content-Type информации заголовка сообщения запроса, чтобы сервер мог декодировать информацию в соответствии с соответствующими правилами кодирования строк и понимать информацию, передаваемую клиентом.
Далее шаг за шагом анализируется, как koa-bodyparser обрабатывает эту серию операций для получения содержимого тела сообщения.
2. Получите поток двоичных данных
NodeJS получает поток двоичных данных тела сообщения запроса, в основном, прослушивая событие данных объекта запроса:
// 示例一
const http = require('http')
http.createServer((req, res) => {
const body = []
req.on('data', chunk => {
body.push(chunk)
})
req.on('end', () => {
const chunks = Buffer.concat(body) // 接收到的二进制数据流
// 利用res.end进行响应处理
res.end(chunks.toString())
})
}).listen(1234)
А koa-bodyparser в основном дляco-bodyинкапсуляция, и [co-body] в основном используетraw-bodyМодуль получает поток двоичных данных тела сообщения запроса. [тело строки] в основном предназначено для инкапсуляции и обработки надежности приведенного выше примера кода.
3. Декодирование контента
Клиент поместит метод кодирования содержимого в атрибут Content-Encoding информации заголовка сообщения запроса. Когда сервер получит двоичные данные тела сообщения, он будет распакован в соответствии с информацией заголовка. Конечно, сервер может response Добавлены поддерживаемые методы декомпрессии в атрибут Accept-Encoding заголовка пакета.
И【тело строки】 в основном используютinflationМодуль распакован.
4. Расшифровка символов
Вообще говоря, UTF-8 является основным методом кодирования символов в Интернете.Как упоминалось ранее, существует также метод кодирования GBK.По сравнению с UTF-8, для кодирования китайского языка требуется всего 2 байта, поэтому UTF неправильно используется при декодировании символов. -8 При декодировании символов в кодировке GBK возникнет проблема искажения китайских символов.
NodeJS в основном обрабатывает потоки бинарных данных через Buffer, но не поддерживает кодировку символов GBK, ему нужно передатьiconv-liteмодуль для обработки.
Код в [Примере 1] имеет проблему, заключающуюся в том, что кодировка символов обрабатывается неправильно, поэтому символы в основной части сообщения закодированы в GBK, и неизбежно появляются искаженные китайские символы:
const request = require('request')
const iconv = require('iconv-lite')
request.post({
url: 'http://localhost:1234/',
body: iconv.encode('中文', 'gbk'),
headers: {
'Content-Type': 'text/plain;charset=GBK'
}
}, (error, response, body) => {
console.log(body) // 发生中文乱码情况
})
Буфер в NodeJS по умолчанию обрабатывается кодировкой символов UTF-8.Здесь модуль [iconv-lite] используется для обработки различных методов кодировки символов:
const chunks = Buffer.concat(body)
res.end(iconv.decode(chunks, charset)) // charset通过Content-Type得到
5. Декодирование строк
Два метода кодирования строк были упомянуты ранее, и соответствующие им Content-Type:
- Приложение с кодировкой URL/x-www-form-urlencoded
- Приложение для кодирования JSON/json
Для внешнего интерфейса кодирование URL-адресов не является чем-то новым, и оно часто используется для операций сращивания URL-адресов.Единственное, на что следует обратить внимание, это не забыть выполнить decodeURIComponent() пару ключ-значение.
Когда клиент отправляет тело запроса, ему необходимо выполнить операцию кодирования:
'a=1&b=2&c=3'
Затем серверная сторона декодирует его в соответствии с правилами кодирования URL, чтобы получить соответствующий объект.
// URL编码方式 简单的解码方法实现
function decode (qs, sep = '&', eq = '=') {
const obj = {}
qs = qs.split(sep)
for (let i = 0, max = qs.length; i < max; i++) {
const item = qs[i]
const index = item.indexOf(eq)
let key, value
if (~index) {
key = item.substr(0, index)
value = item.substr(index + 1)
} else {
key = item
value = ''
}
key = decodeURIComponent(key)
value = decodeURIComponent(value)
if (!obj.hasOwnProperty(key)) {
obj[key] = value
}
}
return obj
}
console.log(decode('a=1&b=2&c=3')) // { a: '1', b: '2', c: '3' }
Метод кодирования URL подходит для обработки простых парных данных ключ-значение, и значение Content-Type по умолчанию в Ajax многих фреймворков — именно оно, но работать со сложными вложенными объектами непросто.В настоящее время кодировка JSON метод требуется, чтобы показать свои таланты.
Когда клиент отправляет тело запроса, ему нужно только использовать JSON.stringify для кодирования. На стороне сервера нужно только использовать JSON.parse для декодирования:
const strictJSONReg = /^[\x20\x09\x0a\x0d]*(\[|\{)/;
function parse(str) {
if (!strict) return str ? JSON.parse(str) : str;
// 严格模式下,总是返回一个对象
if (!str) return {};
// 是否为合法的JSON字符串
if (!strictJSONReg.test(str)) {
throw new Error('invalid JSON, only supports object and array');
}
return JSON.parse(str);
}
В дополнение к двум указанным выше методам кодирования строк, koa-bodyparser также поддерживает обычные строки, не использующие какие-либо методы кодирования строк.
Модуль [co-body] предоставляет три метода обработки кодирования строк: koa-bodyparser оценивает текущий тип Content-Type, вызывает различные методы обработки и монтирует полученные результаты в ctx.request.body:
return async function bodyParser(ctx, next) {
if (ctx.request.body !== undefined) return await next();
if (ctx.disableBodyParser) return await next();
try {
// 最重要的一步 将解析的内容挂载到koa的上下文中
const res = await parseBody(ctx);
ctx.request.body = 'parsed' in res ? res.parsed : {};
if (ctx.request.rawBody === undefined) ctx.request.rawBody = res.raw; // 保存原始字符串
} catch (err) {
if (onerror) {
onerror(err, ctx);
} else {
throw err;
}
}
await next();
};
async function parseBody(ctx) {
if (enableJson && ((detectJSON && detectJSON(ctx)) || ctx.request.is(jsonTypes))) {
return await parse.json(ctx, jsonOpts); // application/json等json type
}
if (enableForm && ctx.request.is(formTypes)) {
return await parse.form(ctx, formOpts); // application/x-www-form-urlencoded
}
if (enableText && ctx.request.is(textTypes)) {
return await parse.text(ctx, textOpts) || ''; // text/plain
}
return {};
}
};
На самом деле, существует относительно общий тип контента. При использовании загрузки формы, тело сообщения будет содержать несколько органов сущности:
------WebKitFormBoundaryqsAGMB6Us6F7s3SF
Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: image/png
------WebKitFormBoundaryqsAGMB6Us6F7s3SF
Content-Disposition: form-data; name="text"
------WebKitFormBoundaryqsAGMB6Us6F7s3SF--
Этот метод относительно сложен, и в Koa-BodyParser нет разбора этого типа контента. (В следующей статье следует представить ^ _ ^)
V. Резюме
Вышеизложенное является основным принципом реализации koa-bodyparser, который требует большого количества базовых знаний о HTTP.Для студентов, которые не знакомы с HTTP, вы можете порекомендовать волну коллекций начального уровня [Иллюстрированный HTTP].
Последнее фото: