Requests: Создавать составные сообщения без файла

Созданный на 3 янв. 2013  ·  36Комментарии  ·  Источник: psf/requests

В настоящее время единственный способ получить составной запрос формы - это r = requests.post(url, data=payload, files=files)
который может иметь компонент

Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain

content
--3eeaadbfda0441b8be821bbed2962e4d--

Однако я сталкиваюсь с случаями, когда сообщения должны быть в многостраничном формате без связанного файла, например:

Content-Disposition: form-data; name="key1"

value1
--3eeaadbfda0441b8be821bbed2962e4d

но второе невозможно создать без первого.

Возможно, мы можем добавить флаг вроде r = requests.post(url, data=payload, multipart=True) который заставляет сообщение быть составным даже без файла.

Я счастлив работать над реализацией этого, если это кажется хорошей идеей.

Все 36 Комментарий

Об этом уже говорилось ранее. Это будет значительным изменением API, чего, я не уверен, хотел бы

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

Возможно, @kennethreitz больше

Действительно, текущий API не поддерживает отправку составных данных, кроме файлов, и это плохо. См. Проблему № 935 и shazow / urllib3 / issues / 120.

Может, я чего-то упускаю, но изменения мне кажутся незначительными. Я раздвоил репозиторий и предлагаю изменение в моей версии здесь: https://github.com/spacecase/requests/commit/45b0b3ce1e76b241b323570a5fc88ae2089c3c3d
(если есть лучший способ сделать это, дайте мне знать? Я новичок в github).

Может потребоваться пара юнит-тестов и изменение строки документации в api.py, но разумно ли это?

Проблема с изменением не в том, что его сложно реализовать, а в расхождении в API. Я как бы нахожусь на заборе в этом продолжающемся обсуждении: я думаю, что, вероятно, было бы полезно иметь хороший способ загрузки составных данных формы, но я также считаю, что текущий files API очень хороший . Я _не_ думаю, что добавление multipart к Request API - это правильный путь.

Честно говоря, я лично предпочел бы контролировать это через заголовок типа контента, но это, вероятно, в 100 раз больше подвержено ошибкам и сбивает с толку новых пользователей, чем то, что мы делаем сейчас. Я тоже сомневаюсь в этом, но я все равно ошибусь, если не буду этого делать.

Почему? Если мы примем этот запрос функции, то возрастет вероятность того, что кто-то будет жаловаться на отсутствие параметра json. И я уверен, что другие люди могли бы придумать еще больше параметров, которые они хотели бы добавить. В настоящее время API делает именно то, что должен, и практически не имеет никакого круфта. Один из способов взглянуть на это - KISS. Это делает запросы и возвращает отличный объект, который делает использование ответа простым и естественным. Он делает то, что рекламируется, а вы делаете все остальное. Он действительно рекламирует многостраничное кодирование и делает это посредством документированного дизайна. Это может показаться неудобным, но это задокументировано и работает.

_ (...) кто-то будет жаловаться на отсутствие параметра json_

Для такой жалобы нет оснований, потому что json является подтипом application media type ( rfc4627 ), а не multipart media type ( httpbis draft 21 ).

_Это может выглядеть неловко (...) _

Это неловко, хотя и быть не должно.

После прочтения предыдущих комментариев я хотел бы повторить свою позицию: multipart/form-data - это наиболее распространенный многостраничный тип MIME, используемый в настоящее время (необходима ссылка :)), и отсутствие его поддержки является грубым упущением.

@ piotr-dobrogost: Несмотря на то, что я сказал выше, я не считаю приведенный вами аргумент даже немного убедительным.

Запросы не поддерживают типы MIME, они поддерживают варианты использования. Эта точка зрения делает оба приведенных выше комментария странными. Например, жалоба на отсутствие параметра JSON будет связана с тем, что загрузка данных в формате JSON очень распространена - вероятно, более распространена среди пользователей запросов, чем загрузка составных данных, не являющихся файлами. Утверждать, что мы не будем предоставлять его, потому что «мы только подтипы особого случая multipart », кажется странным.

Тем не менее, суть проблемы заключается в следующем: API - это весь смысл этой библиотеки. Если вы не можете придумать _ красивый_ способ реализовать эту функциональность, этого не произойдет.

Для такой жалобы не было бы оснований [sic]

@ piotr-dobrogost у вас больше надежд на будущее разработчиков, чем у меня. Кроме того, запрошенный параметр неточен. Его нужно было бы назвать как-то вроде form_data чем multipart . Как вы заметили, (хотя и косвенно) multipart может относиться к множеству различных типов мультимедиа.

Это неловко, хотя и быть не должно.

Не все может быть элегантным (даже на питоне).

и не поддерживая это

Но это поддерживается. Но помимо этого, у нас есть data для application/x-www-form-urlencoded , files (или files+data ) для multipart/form-data , зачем нам еще один параметр всего за multipart/form-data когда он у нас есть?

Вам не нужно использовать комбинацию data и files чтобы получить желаемый результат. Вы можете сделать что-то вроде: requests.post('http://example.com/', files=[('key1', 'param1'), ('key2', 'param2')]) не имея там фактического файла.

И если быть точными, form_data может быть не совсем очевидным, так почему бы не использовать параметр multipart_form_data , но теперь это тоже неуклюже. Это явно, да, и PEP 8 требует объяснения, но существующее поведение хорошо задокументировано . Если @kennethreitz все же решит принять этот запрос функции, все, что параметр должен будет сделать, это действовать как псевдоним для параметра files. Но, учитывая, что такое поведение уже поддерживается, я не думаю, что это необходимо.

@ sigmavirus24 и @Lukasa прекрасно подытожили это.

@ piotr-dobrogost ценится ваш вклад, но не ваш тон. Пожалуйста, перестаньте твердо заявлять о нашем проекте и его целях.

С моей точки зрения, довольно неприятно, что запросы уже имеют поддержку постов, состоящих из нескольких частей, но не дают пользователю доступа к этому без использования файла. Предложение @ sigmavirus24 просто вставить туда файл неадекватно. Веб-приложения, с которыми я работаю, будут возвращать коды ошибок, если попробовать что-то подобное.

Я подозреваю, что это по-прежнему будет проблемой для пользователей в будущем, и меня немного смущает, почему, похоже, не предпринимаются попытки решить эту проблему.

См. Мой ответ на № 935

Ой ну спасибо. Я с нетерпением жду этого!

@Lukasa

_Requests не поддерживает типы MIME, он поддерживает варианты использования. _

Отправка multipart/form-data data - распространенный вариант использования.

_ (...) загрузка данных в формате JSON очень распространена (...) _

Мы не можем сравнивать отправку json с отправкой multipart/form-data . Отправить json просто; вы устанавливаете Content-type , кодируете данные в одну строку с помощью встроенного модуля и все. Отправка multipart/form-data более сложна, потому что тело запроса должно иметь определенную структуру. Другими словами, отправка json прозрачна настолько, насколько это касается HTTP, а отправка multipart/form-data - нет. Поскольку Requests - это HTTP-библиотека, она должна позаботиться о создании такой структуры.

_Если вы не можете придумать красивый способ реализовать эту функциональность, этого не произойдет. _

Имея текущий параметр files для отправки multipart/form-data который НИЧЕГО не имеет отношения к файлам, никоим образом не красиво (это уродливо), но каким-то образом удалось попасть в кодовую базу :). Придумать что-то менее уродливое действительно легко :)

@ sigmavirus24

_ (...) но существующее поведение хорошо задокументировано.

Никакая документация не позволяет сделать хороший API из плохого. Чем лучше API, тем меньше требуется документации.

_Вы можете сделать что-то вроде (...) _

Верно, но это вводит в заблуждение и не интуитивно. Я описал, что не так с использованием параметра files для этого в моем предыдущем комментарии.

Итог: чтобы придумать что-то лучшее, нам нужно признать, что текущий api плох в отношении отправки данных multipart/form-data .

@spacecase

_ С моей точки зрения, довольно неприятно, что запросы уже имеют поддержку постов, состоящих из нескольких частей, но не дают пользователю доступа к этому без использования файла _

Я согласен и поэтому создал выпуск №935.

Этот вопрос закрыт.

Простите меня @kennethreitz, но @spacecase я нигде в этом примере не использовал файл. Я использовал параметр files, чтобы делать именно то, что вы хотите. Если бы я использовал файл, вы бы увидели open('filename') .

@ sigmavirus24 , К сожалению, я не этого хочу. Дело не в том, читает клиент из файла или нет. Это то, что говорят серверу. В вашем примере тело сообщения

Content-Disposition: form-data; name="key1"; filename="key1"
Content-Type: application/octet-stream

param1
--2f8732ee35564115a6c6e0c1032773e8
Content-Disposition: form-data; name="key2"; filename="key2"
Content-Type: application/octet-stream

param2
--2f8732ee35564115a6c6e0c1032773e8--

Обратите внимание на использование filename= . Он сообщает серверу, что файл отправляется. В веб-приложениях, с которыми я работаю, это неправильное поведение приводит к ошибке.

Мне очень жаль, но то, что ты так самонадеянно сказал мне, что это именно то, что я хочу, оскорбляет меня. Я пришел, чтобы предложить идеи и помочь, и, похоже, в данном случае вам не нужно ни того, ни другого. Это нормально, но, пожалуйста, не заставляйте меня чувствовать, что с вами разговаривают.

Хм, кажется, я неправильно запомнил свое поведение. Прости за это. Я вовсе не собирался обидеть вас или заставить вас чувствовать, что с вами разговаривают свысока. Этого определенно достаточно, чтобы заставить меня склониться к поиску лучшего способа обработки multipart/form-data , но пока я все еще не согласен с тем, что идеи для API совсем не изящны.

@spacecase, пожалуйста, посмотрите мой ответ на # 935. Будет лучше. Ваш отзыв важен и очень важен. :)

@spacecase в качестве жеста, чтобы показать, что ваше мнение имеет значение, проверьте sigmavirus24 / requests-data- временной меры. Вам нужно будет установить свой собственный заголовок Content-Type , но это может немного раздражать.

Спасибо @ sigmavirus24 , попробую. Похоже, это поможет.
Я ценю, что вы не хотели меня обидеть. Я чувствую себя лучше, и я ничего не имею против вас или проекта.

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

У меня есть файл и несколько пар "ключ-значение" для публикации. Итак, как правильно отправить такой запрос в форме mutilpart? Я надеюсь, что вы можете мне помочь.

Content-Disposition: form-data; name="up"; filename="aa.PNG"
Content-Type: image/png

file data
---------------------------7dee5302248e
Content-Disposition: form-data; name="exp"


-----------------------------7dee5302248e
Content-Disposition: form-data; name="ptext"

text
-----------------------------7dee5302248e
Content-Disposition: form-data; name="board"

DV_Studio
-----------------------------7dee5302248e--

I tried this way but only got a 504 error.
myfile=[('file',open('bb.jpg')),('exp','python'),('ptext',''),('board','DV_Studio')]
r = requests.post(url,files=myfile)

@deerstalker, если у вас есть вопросы, используйте StackOverflow . Тем не менее, чтобы ответить на ваш вопрос, есть проект по решению этой проблемы sigmavirus24 / requests-toolbelt

Также обратите внимание, что я ранее отвечал на этот вопрос в StackOverflow, как вы можете видеть здесь .

У меня такая же проблема с созданием составных сообщений без файла. На данный момент не имеет значения, сделаете ли вы requests.post(url, data=data_dict) или requests.post(url, data=data_dict, files={}) . Но поскольку ключевое слово files умолчанию равно None , у нас должно быть два разных поведения. multipart/form-data если указан files={} , и application/x-www-form-urlencoded если нет.

Я что-то пропустил?

@jwoillez Вы

Я так думаю, но ваш ответ там касается Multipart POST с одним файлом. Я вернулся к исходной проблеме: составной POST без файла.

Файловый объект может быть строкой. =) Это должно предоставить вам необходимую информацию.

Но если я последую вашему совету, не получу ли я что-то вроде этого:

Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain

content
--3eeaadbfda0441b8be821bbed2962e4d--

где content - строка, которую вы предлагаете мне использовать вместо файла?

Я правда после этого только:

Content-Disposition: form-data; name="key1"

value1
--3eeaadbfda0441b8be821bbed2962e4d

Поля в кортеже, которые вам не нужны, можно оставить по умолчанию:

files = {'name': ('', 'content')}

В будущем, пожалуйста, направляйте свои вопросы в StackOverflow. Все сопровождающие регулярно отслеживают это, и это более подходящее место, чтобы задавать эти вопросы.

Это ответ, который я искал, спасибо. Простите за шум.

Может быть, последний вопрос, возможно ли следующее (указанное пустое имя файла, пустое содержимое)?

Content-Disposition: form-data; name="file"; filename=""
Content-Type: text/plain


--3eeaadbfda0441b8be821bbed2962e4d--

Вы можете предоставить пустое содержимое, используя пустую строку в разделе содержимого кортежа. Вы не можете указать буквальное пустое имя файла, но с отсутствующим именем файла следует обращаться точно так же.

Иногда мне приходилось делать это по разным странным причинам.
Я бы предложил этот подход всем, кто хочет усилить API в этом случае использования:

class ForceMultipartDict(dict):
    def __bool__(self):
        return True


FORCE_MULTIPART = ForceMultipartDict()  # An empty dict that boolean-evaluates as `True`.


client.post("/", data={"some": "data"}, files=FORCE_MULTIPART)

Или вы можете использовать пояс с инструментами и не прибегать к хитростям.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

justlurking picture justlurking  ·  3Комментарии

xsren picture xsren  ·  3Комментарии

ghtyrant picture ghtyrant  ·  3Комментарии

eromoe picture eromoe  ·  3Комментарии

NoahCardoza picture NoahCardoza  ·  4Комментарии