2012-07-18 16 views
8

Ok,twisted get body of POST request

Questo dovrebbe essere semplice, dal momento che le persone lo fanno tutto il tempo. Voglio ottenere il corpo di una richiesta POST inviato un contorto Agent. Questo viene creato con un twistato FileBodyProducer. Sul lato server, ottengo un oggetto request per il mio metodo render_POST.

Come recuperare il corpo?

server:

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() 

cliente:

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() 

Gli unici documenti che posso trovare per impostare il lato del consumatore leave something to be desired. In primo luogo, come può un consumatore utilizzare il metodo write(data) per ricevere i risultati?

Quale bit mi manca per collegare questi due componenti insieme?

risposta

13

Va bene, quindi è semplice come chiamare request.content.read(). Questo, per quanto posso dire, non è documentato nel API.

Ecco il codice aggiornato per il cliente:

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() 

Ed ecco il server:

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() 

ora posso scrivere il contenuto del file che ricevo, e leggere i risultati.

+0

Per riferimento futuro, suggerirei di non eseguire request.write (fd.read()) con la lunghezza del contenuto impostata da os.stat(). St_size. Ho avuto un problema in cui stavo ottenendo un errore IOE non lasciato spazio sul dispositivo su request.write (fd.read()). Se si dispone della memoria, si consiglia di leggere prima il file in un buffer, quindi eseguire la lunghezza del contenuto in base a len (buf). quindi request.write (buf). –

2

Se il tipo di contenuto è application/x-www-form-urlencoded o multipart/form-data, il corpo verrà analizzato e inserito nel request.args dict.

Se il corpo è troppo grande, è scritto in un file temporaneo, altrimenti in StringIO.

Dopo la lettura del corpo, viene chiamato il metodo finish(). È possibile sottoclasse richiesta e pares il corpo in questo metodo o fare altro.

0

se si vuole fare un semplice POST con il corpo (non è un file) si può fare come segue

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 

Ora per utilizzare al di sopra nel codice reale è possibile cioè

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

intestazioni Esempio dovrebbe essere simile

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

mi auguro che aiuta