Как скопировать wsgi.input, если я хочу обрабатывать данные POST более одного раза?

В WSGI пост-данные потребляются при чтении файлового объекта environ['wsgi.input']. Если второй элемент в стеке также хочет прочитать пост-данные, он может повесить программу на чтение, когда больше нечего читать.

Как мне скопировать данные POST, чтобы их можно было обрабатывать несколько раз?


person joeforker    schedule 23.11.2009    source источник


Ответы (3)


Взгляните на пакет WebOb. Он предоставляет функциональные возможности, которые позволяют указать, что wsgi.input должен быть доступен для поиска. Это позволяет вам перематывать входной поток, чтобы содержимое можно было воспроизвести через другой обработчик. Даже если вы не используете WebOb, способ, которым он это делает, должен быть поучительным, поскольку вы можете доверять Яну, чтобы он сделал это надлежащим образом. Чтобы просмотреть результаты поиска в документации, перейдите здесь.

person Graham Dumpleton    schedule 23.11.2009

Вы можете попробовать поместить файловую копию потока обратно в среду:

from cStringIO import StringIO

length = int(environ.get('CONTENT_LENGTH', '0'))
body = StringIO(environ['wsgi.input'].read(length))
environ['wsgi.input'] = body

Однако необходимость сделать это немного пахнет. В идеале только один фрагмент кода должен анализировать строку запроса и тело сообщения и доставлять результаты другим компонентам.

person bobince    schedule 23.11.2009
comment
Вы не должны полагаться на возможность установить длину содержимого по умолчанию равной -1. В спецификации WSGI нет ничего, что говорило бы о том, что реализация должна принимать -1 в качестве аргумента для read(), чтобы означать чтение всего ввода. Реализация может решить создать исключение в этом случае. На самом деле в спецификации, вероятно, даже говорится, что если CONTENT_LENGTH отсутствует или пуст, это должно интерпретироваться как означающее «0» или недоступный ввод. - person Graham Dumpleton; 24.11.2009
comment
Ах да... не совсем понимаю, почему я это поставил, мой собственный фактический код использует 0 :-) Вы намерены изменить это поведение в WSGI, верно? - person bobince; 24.11.2009
comment
Я несколько сомневаюсь, что сейчас WSGI увидит какие-либо изменения. - person Graham Dumpleton; 24.11.2009
comment
Обратите внимание, что если вы хотите использовать wsgi.input дважды, вам нужно сделать body.seek(0) перед вторым использованием (очевидно) - person leszek.hanusz; 30.03.2016
comment
Гарантирует ли WSGI наличие длины содержимого? (Поскольку HTTP этого не делает, если передача разбита на части) - person falstro; 24.08.2016

Если вы собираетесь прочитать его одним махом, вы всегда можете прочитать его, создать файлоподобный объект CStringIO из материала, который вы прочитали, а затем назначить его обратно, например так:

import cStringIO
import copy
lines = []
for line in environ['wsgi.input']:
    lines.append(line)
newlines = copy.copy(lines)
environ['wsgi.input'] = cStringIO.StringIO(''.join(newlines))

Скорее всего, есть более эффективный способ сделать это, но в целом я нахожу публикацию wsgi довольно хрупкой, если вы хотите сделать что-то нетривиальное (например, несколько раз прочитать данные публикации)...

person Paul Huff    schedule 23.11.2009
comment
И bobince является более эффективным способом сделать это :) - person Paul Huff; 23.11.2009
comment
Использование цикла for/in для wsgi.input может быть очень неэффективным с точки зрения использования памяти/времени. Это связано с тем, что если бы в худшем случае у вас был большой файл, в котором все данные состояли бы из пустых строк, вы бы в конечном итоге создали очень большой список, где каждая запись представляет собой один символ. Также не уверен, почему вы беспокоитесь о copy.copy(), если вы все равно сразу же соедините его вместе. - person Graham Dumpleton; 24.11.2009