【问题标题】:twisted get body of POST request扭曲获取 POST 请求的主体
【发布时间】:2012-07-18 19:15:21
【问题描述】:

好的,

这应该很简单,因为人们总是这样做。我想让一个 POST 请求的主体发送一个扭曲的Agent。这是使用扭曲的FileBodyProducer 创建的。在服务器端,我为我的render_POST 方法获得了一个request 对象。

如何找回尸体?

服务器:

from twisted.web import server, resource
from twisted.internet import reactor


class Simple(resource.Resource):
    isLeaf = True
    def render_GET(self, request):
        return "{0}".format(request.args.keys())
    def render_POST(self, request):
        return "{0}".format(request.data)
        with open(request.args['filename'][0], 'rb') as fd:
            fd.write(request.write())

site = server.Site(Simple())
reactor.listenTCP(8080, site)
reactor.run()

客户:

from StringIO import StringIO

from twisted.internet import reactor
from twisted.web.client import Agent
from twisted.web.http_headers import Headers

from twisted.web.client import FileBodyProducer
from twisted.internet.defer import Deferred
from twisted.internet.protocol import Protocol
from pprint import pformat

class BeginningPrinter(Protocol):
    def __init__(self, finished):
        self.finished = finished
        self.remaining = 1024 * 10

    def dataReceived(self, bytes):
        if self.remaining:
            display = bytes[:self.remaining]
            print 'Some data received:'
            print display
            self.remaining -= len(display)

    def connectionLost(self, reason):
        print 'Finished receiving body:', reason.getErrorMessage()
        self.finished.callback(None)

agent = Agent(reactor)
body = FileBodyProducer(StringIO("hello, world"))
d = agent.request(
    'POST',
    'http://127.0.0.1:8080/',
    Headers({'User-Agent': ['Twisted Web Client Example'],
             'Content-Type': ['text/x-greeting']}),
    body)

def cbRequest(response):
    print 'Response version:', response.version
    print 'Response code:', response.code
    print 'Response phrase:', response.phrase
    print 'Response headers:'
    print pformat(list(response.headers.getAllRawHeaders()))
    finished = Deferred()
    response.deliverBody(BeginningPrinter(finished))
    return finished
d.addCallback(cbRequest)

def cbShutdown(ignored):
    reactor.stop()
d.addBoth(cbShutdown)

reactor.run()

我能找到的唯一用于设置消费者端的文档leave something to be desired。首先,消费者如何使用write(data) 方法接收结果?

将这两个组件连接在一起我缺少哪一点?

【问题讨论】:

    标签: python http post twisted


    【解决方案1】:

    好的,就像调用request.content.read() 一样简单。据我所知,这在API 中没有记录。

    这是客户端的更新代码:

    from twisted.internet import reactor
    from twisted.web.client import Agent
    from twisted.web.http_headers import Headers
    
    from twisted.web.client import FileBodyProducer
    from twisted.internet.defer import Deferred
    from twisted.internet.protocol import Protocol
    from pprint import pformat
    
    class BeginningPrinter(Protocol):
        def __init__(self, finished):
            self.finished = finished
            self.remaining = 1024 * 10
    
        def dataReceived(self, bytes):
            if self.remaining:
                display = bytes[:self.remaining]
                print 'Some data received:'
                print display
                self.remaining -= len(display)
    
        def connectionLost(self, reason):
            print 'Finished receiving body:', reason.getErrorMessage()
            self.finished.callback(None)
    
    class SaveContents(Protocol):
        def __init__(self, finished, filesize, filename):
            self.finished = finished
            self.remaining = filesize
            self.outfile = open(filename, 'wb')
    
        def dataReceived(self, bytes):
            if self.remaining:
                display = bytes[:self.remaining]
                self.outfile.write(display)
                self.remaining -= len(display)
            else:
                self.outfile.close()
    
        def connectionLost(self, reason):
            print 'Finished receiving body:', reason.getErrorMessage()
            self.outfile.close()
            self.finished.callback(None)
    
    agent = Agent(reactor)
    f = open('70935-new_barcode.pdf', 'rb')
    body = FileBodyProducer(f)
    d = agent.request(
        'POST',
        'http://127.0.0.1:8080?filename=test.pdf',
        Headers({'User-Agent': ['Twisted Web Client Example'],
                 'Content-Type': ['multipart/form-data; boundary=1024'.format()]}),
        body)
    
    def cbRequest(response):
        print 'Response version:', response.version
        print 'Response code:', response.code
        print 'Response phrase:', response.phrase
        print 'Response headers:'
        print 'Response length:', response.length
        print pformat(list(response.headers.getAllRawHeaders()))
        finished = Deferred()
        response.deliverBody(SaveContents(finished, response.length, 'test2.pdf'))
        return finished
    d.addCallback(cbRequest)
    
    def cbShutdown(ignored):
        reactor.stop()
    d.addBoth(cbShutdown)
    
    reactor.run()
    

    这是服务器:

    from twisted.web import server, resource
    from twisted.internet import reactor
    import os
    
    # multi part encoding example: http://marianoiglesias.com.ar/python/file-uploading-with-multi-part-encoding-using-twisted/
    class Simple(resource.Resource):
        isLeaf = True
        def render_GET(self, request):
            return "{0}".format(request.args.keys())
        def render_POST(self, request):
            with open(request.args['filename'][0], 'wb') as fd:
                fd.write(request.content.read())
            request.setHeader('Content-Length', os.stat(request.args['filename'][0]).st_size)
            with open(request.args['filename'][0], 'rb') as fd:
                request.write(fd.read())
            request.finish()
            return server.NOT_DONE_YET
    
    site = server.Site(Simple())
    reactor.listenTCP(8080, site)
    reactor.run()
    

    我现在可以写我收到的文件内容,并读回结果。

    【讨论】:

    • 为了将来参考,我建议不要使用 os.stat().st_size 设置的内容长度执行 request.write(fd.read())。我遇到了一个问题,我在 request.write(fd.read()) 处收到 IOError no space left on device。如果你有内存,我建议先将文件读入缓冲区,然后根据 len(buf) 进行内容长度。然后 request.write(buf).
    【解决方案2】:

    如果内容类型是 application/x-www-form-urlencoded 或 multipart/form-data, 正文将被解析并放入 request.args 字典中。

    如果body太大,则写入temp文件,否则写入StringIO。

    读取主体后,调用方法finish()。你可以继承 Request 和 用这种方法去皮或做其他事情。

    【讨论】:

      【解决方案3】:

      如果你想用正文(不是文件)做一个简单的 POST,你可以这样做

      import urllib
      from twisted.internet import protocol
      from twisted.internet import defer
      from twisted.web.http_headers import Headers
      from twisted.internet import reactor
      from twisted.web.client import Agent
      from twisted.web.iweb import IBodyProducer
      from zope.interface import implements
      from twisted.internet.defer import succeed
      
      class StringProducer(object):
          implements(IBodyProducer)
      
          def __init__(self, body):
              self.body = body
              self.length = len(body)
      
          def startProducing(self, consumer):
              consumer.write(self.body)
              return succeed(None)
      
          def pauseProducing(self):
              pass
      
          def stopProducing(self):
              pass
      
      class SimpleReceiver(protocol.Protocol):
          def __init__(self, d):
              self.buf = ''; self.d = d
      
          def dataReceived(self, data):
              self.buf += data
      
          def connectionLost(self, reason):
              self.d.callback(self.buf)
      
      def httpRequest(url, values=None, headers=None, method='POST'):
      
          agent = Agent(reactor)
          data = urllib.urlencode(values) if values else None
      
          d = agent.request(method, url, Headers(headers) if headers else {},
              StringProducer(data) if data else None
              )
      
          def handle_response(response):
              if response.code == 204:
                  d = defer.succeed('')
              else:
                  d = defer.Deferred()
                  response.deliverBody(SimpleReceiver(d))
              return d
      
          d.addCallback(handle_response)
          return d
      

      现在可以在实际代码中使用上面的代码,即

      d = httpRequest('htpp://...', post_data_as_dictionary, some_headers, 'POST')
      d.addCallback(your_ok_callback_function)
      d.addErrback(your_errorback_function)
      

      示例标题应如下所示

      headers = {'Accept' : ['application/json',],
                  'Content-Type': ['application/x-www-form-urlencoded',] 
      }
      

      希望对你有帮助

      【讨论】:

        猜你喜欢
        • 2018-09-20
        • 2021-01-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-31
        • 1970-01-01
        • 2017-08-09
        • 2016-02-21
        相关资源
        最近更新 更多