2012-07-18 19 views
8

Ok,twisted get body of POST request

Esto debería ser simple, ya que las personas lo hacen todo el tiempo. Quiero que el cuerpo de una solicitud POST envíe un retorcido Agent. Esto se crea con un retorcido FileBodyProducer. En el lado del servidor, obtengo un objeto request para mi método render_POST.

¿Cómo recupero el cuerpo?

servidor:

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

Los únicos documentos que puedo encontrar para configurar el lado del consumidor leave something to be desired. Principalmente, ¿cómo puede un consumidor utilizar el método write(data) para recibir los resultados?

¿Qué bit me falta para conectar estos dos componentes?

Respuesta

13

Muy bien, así que es tan simple como llamar al request.content.read(). Esto, por lo que puedo decir, no está documentado en el API.

Aquí está el código actualizado para el 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() 

Y es el servidor aquí:

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

ahora puedo escribir el contenido del archivo que recibo, y vuelve a leer los resultados.

+0

Para referencia futura, recomendaría no hacer request.write (fd.read()) con content-length establecido por os.stat(). St_size. Tuve un problema en el que recibía un IOError sin espacio en el dispositivo en request.write (fd.read()). Si tienes la memoria, te recomiendo que primero leas el archivo en un búfer, y luego en contenido-duración basado en len (buf). luego request.write (buf). –

2

Si el tipo de contenido es application/x-www-form-urlencoded o multipart/form-data, el cuerpo se analizará y se colocará en request.args dict.

Si el cuerpo es demasiado grande, está escrito en archivo temporal, de lo contrario en StringIO.

Después de leer el cuerpo, se llama al método finish(). Puede hacer una solicitud de subclase y pares el cuerpo en este método o hacer algo más.

0

si desea hacer una simple post con el cuerpo (no un archivo) se puede hacer de la siguiente manera

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 

Ahora usar más arriba en código real se puede decir

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

Ejemplo encabezados debe ser similar

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

Espero que ayude