Пошук уроків, статей та іншого контенту
Розберемо MIME-типи, заголовок Content-Type та правила обміну текстом, HTML, JSON і бінарними даними.
Коли клієнт і сервер обмінюються даними, недостатньо передати лише самі байти. Отримувачу потрібно знати, як їх інтерпретувати:
як звичайний текст;
як HTML-документ;
як JSON;
як зображення;
як файл невідомого формату;
як інші бінарні дані.
Для цього використовують MIME-тип. У HTTP він передається в заголовку Content-Type.
MIME-тип має формат:
тип/підтипПриклади:
text/plain
text/html
application/json
image/png
application/octet-streamДодатково можуть передаватися параметри, наприклад кодування тексту:
text/plain; charset=utf-8Content-TypeContent-Type описує формат тіла повідомлення.
У відповіді сервера цей заголовок повідомляє клієнту, що саме повернув сервер:
Content-Type: application/jsonУ запиті клієнта цей заголовок описує дані, які клієнт надсилає серверу:
Content-Type: application/jsonНаприклад, тіло запиту:
{"name":"Olena","age":25}має бути надіслане з таким заголовком:
Content-Type: application/jsonБез цього сервер не повинен автоматично припускати, що отримані дані є JSON.
Для простого тексту використовують:
text/plainПриклад тіла:
Привіт, Node.js!Для текстових даних бажано явно вказувати кодування:
text/plain; charset=utf-8UTF-8 підтримує українські та інші Unicode-символи.
HTML позначають типом:
text/html; charset=utf-8Приклад:
<h1>Головна сторінка</h1>Браузер розуміє, що такий текст потрібно обробити як HTML-розмітку.
Для JSON використовують:
application/jsonПриклад:
{
"id": 1,
"name": "Olena"
}JSON не є звичайним текстовим форматом у контексті HTTP-протоколу, тому для нього використовують application/json, а не text/plain.
Зображення мають конкретні MIME-типи:
image/png
image/jpeg
image/gifДля даних, формат яких невідомий або не має спеціального MIME-типу, використовують:
application/octet-streamБінарні дані не слід обробляти як звичайний текст. Під час роботи з ними важливо зберігати початкові байти без змін.
Content-Type у Node.jsУ модулі http заголовки відповіді можна встановити методом response.setHeader():
response.setHeader("Content-Type", "text/plain; charset=utf-8");Після цього тіло відповіді передається методом response.end():
response.end("Привіт!");Для JSON об’єкт спочатку потрібно перетворити на рядок:
const data = { message: "Привіт!" };
response.setHeader("Content-Type", "application/json; charset=utf-8");
response.end(JSON.stringify(data));Для бінарних даних response.end() може приймати Buffer.
Цей сервер повертає різні типи даних залежно від URL, а також приймає текстові й JSON-запити:
const http = require("node:http");
const server = http.createServer((request, response) => {
if (request.method === "GET" && request.url === "/text") {
response.setHeader("Content-Type", "text/plain; charset=utf-8");
response.end("Це звичайний текст.");
return;
}
if (request.method === "GET" && request.url === "/html") {
response.setHeader("Content-Type", "text/html; charset=utf-8");
response.end("<h1>Це HTML-сторінка</h1>");
return;
}
if (request.method === "GET" && request.url === "/json") {
const data = {
message: "Це JSON-відповідь",
success: true
};
response.setHeader("Content-Type", "application/json; charset=utf-8");
response.end(JSON.stringify(data));
return;
}
if (request.method === "GET" && request.url === "/binary") {
// Створюємо послідовність байтів як приклад бінарних даних
const binaryData = Buffer.from([0, 1, 2, 3, 255]);
response.setHeader("Content-Type", "application/octet-stream");
response.end(binaryData);
return;
}
if (request.method === "POST" && request.url === "/echo") {
const chunks = [];
request.on("data", (chunk) => {
chunks.push(chunk);
});
request.on("end", () => {
const body = Buffer.concat(chunks).toString("utf8");
const contentType = request.headers["content-type"] || "";
if (contentType.startsWith("application/json")) {
try {
const parsedBody = JSON.parse(body);
response.setHeader(
"Content-Type",
"application/json; charset=utf-8"
);
response.end(JSON.stringify({
receivedAs: "json",
body: parsedBody
}));
} catch {
response.statusCode = 400;
response.setHeader(
"Content-Type",
"text/plain; charset=utf-8"
);
response.end("Некоректний JSON.");
}
return;
}
response.setHeader("Content-Type", "text/plain; charset=utf-8");
response.end(`Отримано як текст: ${body}`);
});
return;
}
response.statusCode = 404;
response.setHeader("Content-Type", "text/plain; charset=utf-8");
response.end("Сторінку не знайдено.");
});
server.listen(3000, () => {
console.log("Сервер запущено на http://localhost:3000");
});Збережіть код у файл server.js і запустіть:
node server.jsПісля цього можна перевірити різні відповіді:
curl http://localhost:3000/text
curl http://localhost:3000/html
curl http://localhost:3000/jsonДля перевірки типу бінарної відповіді:
curl -i http://localhost:3000/binaryПараметр -i показує заголовки відповіді разом із її тілом.
JSON-запит можна надіслати так:
curl -i \
-X POST \
-H "Content-Type: application/json" \
-d '{"name":"Olena","age":25}' \
http://localhost:3000/echoТекстовий запит:
curl -i \
-X POST \
-H "Content-Type: text/plain; charset=utf-8" \
-d "Привіт із клієнта" \
http://localhost:3000/echoТіло HTTP-запиту надходить до Node.js частинами. Подія data викликається для кожної отриманої частини:
request.on("data", (chunk) => {
// Обробка чергової частини тіла запиту
});Після отримання всіх частин викликається подія end:
request.on("end", () => {
// Тіло запиту отримано повністю
});У прикладі частини об’єднуються в один Buffer:
const body = Buffer.concat(chunks).toString("utf8");Для тексту та JSON цей буфер перетворюється на рядок. Потім сервер перевіряє Content-Type:
const contentType = request.headers["content-type"] || "";
if (contentType.startsWith("application/json")) {
// Тіло потрібно розібрати як JSON
}Перевірка через startsWith() дозволяє обробити обидва варіанти:
application/json
application/json; charset=utf-8Після цього JSON можна розібрати за допомогою JSON.parse():
const data = JSON.parse(body);Результатом буде звичайний JavaScript-об’єкт.
Content-Type і AcceptЦі заголовки мають різне призначення:
Content-Type описує формат фактично переданого тіла;
Accept повідомляє, який формат відповіді бажає отримати клієнт.
Наприклад:
Content-Type: application/json
Accept: application/jsonУ такому запиті клієнт надсилає JSON і очікує JSON у відповідь.
Запит може мати такий вигляд:
curl \
-H "Accept: application/json" \
http://localhost:3000/jsonДля початкової роботи достатньо запам’ятати:
Content-Typeописує те, що передається зараз, аAcceptописує бажаний формат відповіді.
Content-TypeЯкщо сервер повертає JSON, але не встановлює:
Content-Type: application/jsonклієнт може неправильно визначити формат відповіді.
Завжди встановлюйте відповідний тип перед відправленням тіла.
String(object)Такий код неправильний:
response.end(String({ name: "Olena" }));Він поверне:
[object Object]Для JSON використовуйте:
response.end(JSON.stringify({ name: "Olena" }));Виклик JSON.parse() може завершитися помилкою, якщо тіло запиту не є коректним JSON. Тому обробку слід виконувати в try...catch.
Не варто без потреби виконувати:
binaryData.toString("utf8");Для довільних байтів це може змінити або пошкодити дані. Бінарні дані потрібно передавати як Buffer.
Розширення файлу саме по собі не визначає Content-Type HTTP-відповіді. Сервер має явно вказати тип, наприклад:
Content-Type: image/pngдля PNG-зображення або:
Content-Type: text/html; charset=utf-8для HTML.
MIME-тип описує формат даних, якими обмінюються клієнт і сервер.
У HTTP MIME-тип передається через заголовок Content-Type.
Для звичайного тексту використовують text/plain.
Для HTML — text/html.
Для JSON — application/json.
Для зображень і файлів використовують відповідні типи, наприклад image/png або application/octet-stream.
Для тексту бажано вказувати charset=utf-8.
Об’єкти JavaScript потрібно перетворювати в JSON через JSON.stringify().
JSON із запиту потрібно розбирати через JSON.parse() після перевірки Content-Type.
Бінарні дані потрібно передавати як байти, не перетворюючи їх без потреби на текст.