Регистрация данных POST из $ request_body

У меня есть настройка конфигурации для обработки кучи запросов GET, которые отображают пиксели, которые отлично работают для обработки аналитики и синтаксического анализа строк запроса для ведения журнала. С дополнительным сторонним потоком данных мне нужно обработать запрос POST на заданный URL-адрес, который имеет JSON в ожидаемом регистрируемом формате внутри тела запроса. Я не хочу использовать вторичный сервер с proxy_pass и просто хочу записывать весь ответ в связанный файл журнала, как это происходит с запросами GET. Фрагмент кода, который я использую, выглядит следующим образом:

Запрос GET (который отлично работает):

location ^~ /rl.gif {
  set $rl_lcid $arg_lcid;
  if ($http_cookie ~* "lcid=(.*\S)")
  {
    set $rl_lcid $cookie_lcid;
  }
  empty_gif;
  log_format my_tracking '{ "guid" : "$rl_lcid", "data" : "$arg__rlcdnsegs" }';
  access_log  /mnt/logs/nginx/my.access.log my_tracking;
  rewrite ^(.*)$ http://my/url?id=$cookie_lcid? redirect;
}

Вот что я пытаюсь сделать: запрос POST (который не работает):

location /bk {
  log_format bk_tracking $request_body;
  access_log  /mnt/logs/nginx/bk.access.log bk_tracking;
}

Curling curl http://myurl/bk -d name=example дает мне страницу 404, которая не найдена.

Потом попробовал:

location /bk.gif {
  empty_gif;
  log_format bk_tracking $request_body;
  access_log  /mnt/logs/nginx/bk.access.log bk_tracking;
}

Керлинг curl http://myurl/bk.gif -d name=example дает мне 405 Not Allowed.

Моя текущая версия - nginx/0.7.62. Мы очень ценим любую помощь в правильном направлении! Спасибо!

ОБНОВЛЕНИЕ Итак, теперь мой пост выглядит так:

location /bk {
  if ($request_method != POST) {
    return 405;
  }
  proxy_pass $scheme://127.0.0.1:$server_port/dummy;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my.access.log my_tracking;
}
location /dummy { set $test 0; }

Он правильно регистрирует данные публикации, но возвращает 404 на стороне запрашивающего. Если я изменю приведенный выше код, чтобы вернуть 200, например:

location /bk {
  if ($request_method != POST) {
    return 405;
  }
  proxy_pass $scheme://127.0.0.1:$server_port/dummy;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my.access.log my_tracking;
  return 200;
}
location /dummy { set $test 0; }

Затем он правильно возвращает 200, но больше не записывает данные публикации.

ДРУГОЕ ОБНОВЛЕНИЕ Вроде нашла рабочее решение. Надеюсь, это поможет другим на их пути.


person Chris Barretto    schedule 08.02.2011    source источник
comment
ты чего-нибудь добился с этим? Я столкнулся с аналогичной проблемой.   -  person James C    schedule 12.06.2011
comment
Вместо того, чтобы делать set $test 0; попытку сделать return 200; из фиктивного местоположения, для меня он затем возвращает 200, как и ожидалось, и я все еще регистрирую тело.   -  person Elven Spellmaker    schedule 19.05.2018


Ответы (7)


Это решение работает как шарм (обновлено в 2017 году с учетом того, что log_format должен находиться в http-части конфигурации nginx):

log_format postdata $request_body;

server {
    # (...)

    location = /post.php {
       access_log  /var/log/nginx/postdata.log  postdata;
       fastcgi_pass php_cgi;
    }
}

Я думаю, что уловка заключается в том, чтобы заставить nginx поверить, что вы вызовете cgi-скрипт.

person ahofmann    schedule 19.09.2011
comment
Мне нужно переместить log_format postdata $ request_body; к серверной части, потому что nginx говорит, что директива log_format здесь не разрешена - person Oleg Neumyvakin; 15.02.2013
comment
Это правильный ответ. Кроме того, как упомянул @OlegNeumyvakin, мне пришлось переместить часть log_format. Но в моем случае мне пришлось переместить его внутрь блока http, а не внутри блока сервера, чтобы он работал. Но это сработало! - person Matt; 07.05.2013
comment
@OlegNeumyvakin & Matt: Я вложил ваши замечания в сам ответ (в настоящее время ожидает рассмотрения). Спасибо! - person Greg Dubicki; 09.04.2015
comment
Мне даже пришлось переместить строку log_format над серверной частью. Но потом это сработало. (nginx / 1.9.3) - person TheFisch; 13.01.2016
comment
да, log_format разрешен только в http-части конфигурации nginx. Я соответственно изменил пример. - person ahofmann; 20.12.2017

Попробуйте echo_read_request_body.

"echo_read_request_body ... Явно считывает тело запроса, так что переменная $ request_body всегда будет иметь непустую значения (если тело не настолько велико, что оно было сохранено Nginx в локальный временный файл) ".

location /log {
  log_format postdata $request_body;
  access_log /mnt/logs/nginx/my_tracking.access.log postdata;
  echo_read_request_body;
}
person boqapt    schedule 25.12.2012
comment
Это сработало лучше, чем принятый ответ, поскольку установка fastcgi не требовалась. - person Fluffy; 09.01.2014
comment
установить apt-get install nginx-full - person firelynx; 17.08.2015
comment
@ user4 Я вижу тире только в моих логах. В чем проблема? Я использую echo_read_request_body. и установлен эхо-модуль - person Vladimir Kovalchuk; 23.07.2016
comment
для меня, если я перенаправляю (например, 301/302), request_body исчезает - person deceleratedcaviar; 18.09.2017
comment
@dcousens, в какой момент вы обслуживаете 301/302? Если вы устанавливаете перенаправление 301/302 прямо в директиве nginx location, то наблюдаемое поведение имеет смысл. Если бы это произошло после некоторой обработки php, это было бы немного странно. Вам следует задать новый вопрос о переполнении стека, чтобы кто-нибудь мог вам помочь. - person Dale Anderson; 19.09.2017
comment
в директиве местоположения. Я без колебаний сообщаю, что мой опыт работы с nginx относительно невелик, но мне было не сразу понятно, почему исчезнет request_body или почему это будет разумно. - person deceleratedcaviar; 20.09.2017

В порядке. Итак, наконец, я смог зарегистрировать данные публикации и вернуть 200. Это своего рода хакерское решение, которым я не слишком горжусь, которое в основном переопределяет естественное поведение для error_page, но моя неопытность в nginx плюс временные шкалы привели меня к этому решению. :

location /bk {
  if ($request_method != POST) {
    return 405;
  }
  proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_redirect off;
  proxy_pass $scheme://127.0.0.1:$server_port/success;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my_tracking.access.log my_tracking;
}
location /success {
  return 200;
}
error_page   500 502 503 504  /50x.html;
location = /50x.html {
  root   /var/www/nginx-default;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my_tracking.access.log my_tracking_2;
}

Теперь, в соответствии с этой конфигурацией, могло показаться, что проход прокси будет все время возвращать 200. Иногда я получал 500, но когда я вставлял error_log, чтобы увидеть, что происходит, все мои данные request_body были там, и я не видел проблемы. Я поймал это и написал в тот же журнал. Поскольку nginx не любит одно и то же имя для переменной отслеживания, я просто использовал my_tracking_2 и записал в тот же журнал, что и когда он возвращает 200. Определенно не самое элегантное решение, и я приветствую любое лучшее решение. Я видел почтовый модуль, но в моем сценарии я не мог перекомпилировать из исходников.

person Chris Barretto    schedule 05.07.2011
comment
Решение, которое вы придумали, очень похоже на принятый здесь ответ: stackoverflow.com/questions/17609472/ Поскольку nginx не анализирует тело, если только не отправляет его на прокси или сервер fast-cgi, проксирование себе работает. - person turtlemonvh; 28.10.2015
comment
мы используем аналогичное решение, путем proxy_pass POST-запроса в другое место. - person arganzheng; 31.05.2016

Приведенное ниже решение было лучшим форматом, который я нашел.

log_format postdata escape=json '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $bytes_sent '
                       '"$http_referer" "$http_user_agent" "$request_body"';
server {
        listen 80;

        server_name api.some.com;

        location / {
         access_log  /var/log/nginx/postdata.log  postdata;
         proxy_pass      http://127.0.0.1:8080;
        }

}

Для этого входа

curl -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST http://api.deprod.com/postEndpoint

Добейтесь отличного результата

201.23.89.149 -  [22/Aug/2019:15:58:40 +0000] "POST /postEndpoint HTTP/1.1" 200 265 "" "curl/7.64.0" "{\"key1\":\"value1\", \"key2\":\"value2\"}"
person Bruno Lee    schedule 22.08.2019
comment
Также обратите внимание, что использование escape=none будет просто выводить JSON в том виде, в каком он был, без экранирования кавычек (если это желательно для вашего варианта использования) - person Fonnae; 21.01.2021
comment
Не работает. Есть ли что-то особенное, что нужно сделать? Например, нужно добавить какой-то конкретный модуль? - person lifeisshubh; 16.07.2021
comment
Я тестировал новый nginx, если вы хотите протестировать, просто создайте необработанный образ докера из hub.docker.com / _ / nginx и попробуйте. Возможно, у вашего nginx другая конфигурация - person Bruno Lee; 16.07.2021

FWIW, у меня сработал этот конфиг:

location = /logpush.html {
  if ($request_method = POST) {
    access_log /var/log/nginx/push.log push_requests;
    proxy_pass $scheme://127.0.0.1/logsink;
    break;
  }   
  return 200 $scheme://$host/serviceup.html;
}   
#
location /logsink {
  return 200;
}
person user2096933    schedule 21.02.2013

nginx формат журнала взят отсюда: http://nginx.org/en/docs/http/ngx_http_log_module.html

не нужно ничего устанавливать

у меня работал для GET и POST запросов:

upstream my_upstream {
   server upstream_ip:upstream_port;
}

location / {
    log_format postdata '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $bytes_sent '
                       '"$http_referer" "$http_user_agent" "$request_body"';
    access_log /path/to/nginx_access.log postdata;
    proxy_set_header Host $http_host;
    proxy_pass http://my_upstream;
    }
}

просто измените upstream_ip и upstream_port

person NoamG    schedule 13.01.2015

У меня была аналогичная проблема. Запросы GET работали, и их (пустые) тела запросов записывались в файл журнала. Запросы POST не удались с ошибкой 404. Немного поэкспериментировав, я обнаружил, что все запросы POST не работают. Я нашел сообщение на форуме с вопросом о запросах POST, и решение там сработало для меня. Это решение? Добавьте строку proxy_header прямо перед строкой proxy_pass, точно так же, как в примере ниже.

server {
    listen       192.168.0.1:45080;
    server_name  foo.example.org;

    access_log  /path/to/log/nginx/post_bodies.log post_bodies;
    location / {
      ### add the following proxy_header line to get POSTs to work
      proxy_set_header Host $http_host;
      proxy_pass   http://10.1.2.3;
    }
}

(Это с nginx 1.2.1, чего он стоит.)

person epicsmile    schedule 14.06.2012