Nginx возвращать в json-ответе данные полученные POST-запросом
На сервере nginx 1.10.3
Создана тестовая заглушка, которая на любой запрос отвечает 200 и статичный json
cat mockserver.test.conf
server {
listen 80;
server_name mockserver.test;
location / {
return 200 '{"name": "qwerty", "phone": "+123"}';
}
}
Как можно настроить nginx, чтобы при отправке POST-запросом данных
POST http://mockserver.test/
{"token": "+444"}
В ответ приходил json
{"name": "qwerty", "phone": "+444"}
То есть в значение ключа phone
ответа подставлялось значение ключа token
из POST-запроса.
https://nginx.org/en/docs/varindex.html
Пробовал использовать:
return 200 '{"phone": "$request_body"}'
- вернуло пустую строку
return 200 '{"phone": "$request"}'
- вернуло только POST / HTTP/1.1
Обновил с учетом комментариев.
Ответы (1 шт):
На той фазе обработки запроса, на которой отрабатывает директива rewrite
, тело запроса ещё не прочитано, так что ничего удивительного. А оно действительно содержит только те данные, которые надо вставить в JSON, без какой-либо их обработки? В принципе это можно решить дополнительным проксированием на самого себя, и передавать содержимое тела запроса через кастомный хедер, например, proxy_set_header X-Request-Body $request_body;
, тогда во втором серверном блоке оно будет доступно через переменную $http_x_request_body
:
server {
listen 80;
server_name mockserver.test;
location / {
proxy_set_header Host local.mockserver.test;
proxy_set_header X-Request-Body $request_body;
proxy_pass http://127.0.0.1;
}
}
server {
listen 127.0.0.1:80;
server_name local.mockserver.test;
return 200 '{"phone": "$http_x_request_body"}';
}
А ещё при использовании подобной техники (дополнительного слоя проксирования) для несколько большей эффективности можно слушать отдельный сокет:
server {
listen 80;
server_name mockserver.test;
location / {
proxy_set_header X-Request-Body $request_body;
proxy_pass http://unix:/tmp/nginx.sock;
}
}
server {
listen unix:/tmp/nginx.sock;
return 200 '{"phone": "$http_x_request_body"}';
}
Дополнение 1
Прошу прощения, как-то я на ночь глядя и не заметил, что в вопросе приведен пример входящего JSON. Наверное меня сбила с толку попытка вставить в исходящий JSON переменную $request_body
целиком. Вообще для таких случаев хорошо подходит модуль njs (пример разбора входящего JSON с использованием модуля njs можно посмотреть здесь). Но для простых случаев можно попытаться обойтись блоком map
с регуляркой (взята отсюда):
map $request_body $phone {
'~"phone":\s*"((?:(?=\\\\)\\\\(?:["\\\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\\\\0-\x1F\x7F])*)"' $1;
}
server {
listen 80;
server_name mockserver.test;
location / {
proxy_set_header X-Phone $phone;
proxy_pass http://unix:/tmp/nginx.sock;
}
}
server {
listen unix:/tmp/nginx.sock;
return 200 '{"phone": "$http_x_phone"}';
}
Использованное регулярное выражение предназначено для получения строковых значений JSON по заданному имени ключа (в данном случае phone
) и в более привычном синтаксисе PCRE может быть записано так:
/"phone":\s*"((?:(?=\\)\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\\0-\x1F\x7F])*)"/
Дополнение 2
По неизвестной мне причине nginx версии 1.10.3, которым почему-то (в 2025 году!) пользуется автор вопроса, ругается на использование нумерованной группы в регулярном выражении из блока map
:
nginx: [emerg] unknown "1" variable
(хотя в современных версиях nginx эта конфигурация вполне работоспособна). Зато тот же самый nginx 1.10.3 прекрасно понимает то же самое регулярное выражение с использованием именованной группы:
map $request_body $phone {
'~"phone":\s*"(?<value>(?:(?=\\\\)\\\\(?:["\\\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\\\\0-\x1F\x7F])*)"' $value;
}
Разбираться в том, какие именно версии nginx затрагивает этот баг, и когда именно он был исправлен, у меня никакого желания нет :) Главное, что workaround, приведенный выше, вполне работоспособен.
Дополнение 3
Конфигурация nginx в соответствии с уточнёнными условиями вопроса:
map $request_body $phone {
'~"token":\s*"(?<value>(?:(?=\\\\)\\\\(?:["\\\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\\\\0-\x1F\x7F])*)"' $value;
}
server {
listen 80;
server_name mockserver.test;
location / {
proxy_set_header X-Phone $phone;
proxy_pass http://unix:/tmp/nginx.sock;
}
}
server {
listen unix:/tmp/nginx.sock;
return 200 '{"name": "qwerty", "phone": "$http_x_phone"}';
}
А также! Для того, чтобы вышестоящий сервис корректно обработал отданный нами JSON, к последнему блоку server { ... }
может понадобиться добавить директиву
default_type application/json;