รับเนื้อหา POST แบบดิบใน Python Flask โดยไม่คำนึงถึงส่วนหัวของ Content-Type


135

ก่อนหน้านี้ฉันถามวิธีรับข้อมูลที่ได้รับในคำขอขวดเพราะว่างrequest.dataเปล่า คำตอบอธิบายว่าrequest.dataเป็นเนื้อหาโพสต์ดิบ แต่จะว่างเปล่าหากข้อมูลฟอร์มถูกแยกวิเคราะห์ ฉันจะได้รับโพสต์เนื้อหาดิบโดยไม่มีเงื่อนไขได้อย่างไร

@app.route('/', methods=['POST'])
def parse_request():
    data = request.data  # empty in some cases
    # always need raw data here, not parsed form data

คำตอบ:


224

ใช้request.get_data()เพื่อรับข้อมูลดิบโดยไม่คำนึงถึงประเภทเนื้อหา ข้อมูลจะถูกเก็บไว้ชั่วคราวและคุณก็สามารถเข้าถึงภายหลังrequest.data, request.json, request.formที่จะ

หากคุณเข้าถึงrequest.dataก่อนมันจะเรียกget_dataด้วยอาร์กิวเมนต์เพื่อแยกวิเคราะห์ข้อมูลฟอร์มก่อน หากคำขอมีชนิดเนื้อหารูปแบบ ( multipart/form-data, application/x-www-form-urlencodedหรือapplication/x-url-encoded) แล้วข้อมูลดิบจะถูกบริโภค request.dataและrequest.jsonจะปรากฏเป็นช่องว่างในกรณีนี้


2
ดูเหมือนว่าจะพังเมื่อใช้ raven-python (Sentry) ข้อผิดพลาดและวิธีแก้ปัญหาที่นี่: github.com/getsentry/raven-python/issues/457
dequis

36

request.streamคือกระแสข้อมูลดิบที่ส่งผ่านไปยังแอ็พพลิเคชันโดยเซิร์ฟเวอร์ WSGI ไม่มีการแยกวิเคราะห์เมื่ออ่านแม้ว่าคุณจะต้องการrequest.get_data()แทนก็ตาม

data = request.stream.read()

สตรีมจะว่างเปล่าหากก่อนหน้านี้อ่านโดยrequest.dataหรือแอตทริบิวต์อื่น


ขอขอบคุณที่ชี้ให้เห็นว่าสตรีมจะว่างเปล่าหากอ่านผ่าน request.data มาก่อน! เกือบจะมีฉันระหว่างการดีบัก
user1767754

15

ฉันสร้างมิดเดิลแวร์ WSGI ที่เก็บเนื้อหาดิบจากenviron['wsgi.input']สตรีม ฉันบันทึกค่าในสภาพแวดล้อม WSGI ดังนั้นฉันจึงสามารถเข้าถึงได้จากrequest.environ['body_copy']ภายในแอปของฉัน

สิ่งนี้ไม่จำเป็นใน Werkzeug หรือ Flask เนื่องจากrequest.get_data()จะได้รับข้อมูลดิบโดยไม่คำนึงถึงประเภทเนื้อหา แต่มีการจัดการพฤติกรรม HTTP และ WSGI ที่ดีขึ้น

สิ่งนี้จะอ่านเนื้อหาทั้งหมดลงในหน่วยความจำซึ่งจะเป็นปัญหาหากมีการโพสต์ไฟล์ขนาดใหญ่ สิ่งนี้จะไม่อ่านอะไรเลยหากContent-Lengthส่วนหัวหายไปดังนั้นจะไม่จัดการคำขอสตรีมมิ่ง

from io import BytesIO

class WSGICopyBody(object):
    def __init__(self, application):
        self.application = application

    def __call__(self, environ, start_response):
        length = int(environ.get('CONTENT_LENGTH') or 0)
        body = environ['wsgi.input'].read(length)
        environ['body_copy'] = body
        # replace the stream since it was exhausted by read()
        environ['wsgi.input'] = BytesIO(body)
        return self.application(environ, start_response)

app.wsgi_app = WSGICopyBody(app.wsgi_app)
request.environ['body_copy']

7

request.dataจะว่างเปล่าถ้าจำได้ว่าเป็นข้อมูลแบบฟอร์มซึ่งจะถูกแยกออกเป็นrequest.headers["Content-Type"] เพื่อให้ได้ข้อมูลดิบคำนึงถึงประเภทของเนื้อหาการใช้งานrequest.formrequest.get_data()

request.dataการโทรrequest.get_data(parse_form_data=True)ซึ่งส่งผลให้เกิดพฤติกรรมที่แตกต่างกันสำหรับข้อมูลแบบฟอร์ม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.