2012-07-18 92 views
8

好的,扭曲得到POST請求的正文

這應該很簡單,因爲人們一直都這樣做。我想獲得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)方法來獲得的結果

我錯過了將這兩個組件連接在一起的位?

回答

13

好的,所以就像撥打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() 

我現在可以寫我收到的文件內容,並回讀結果。

+0

爲了將來的參考,我建議不要執行由os.stat()。st_size設置的內容長度的request.write(fd.read())。我遇到了一個問題,我在request.write(fd.read())處得到IOError設備上沒有剩餘空間。 如果你有內存,我建議首先將文件讀入緩衝區,然後根據len(buf)做內容長度。然後request.write(buf)。 – 2016-08-17 22:33:52

2

如果內容類型是application/x-www-form-urlencoded或multipart/form-data, 正文將被解析並放入request.args字典中。

如果主體太大,則寫入臨時文件,否則寫入StringIO。

正文被讀取後,方法finish()被調用。您可以繼承Request,並使用此方法削減身體或做其他事情。

0

,如果你想用身體一個簡單的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',] 
} 

我希望幫助