2010-12-13 17 views
9

Sto provando a scrivere un codice Python che può creare richieste HTTP multi-mime nel client e quindi interpretare in modo appropriato sul server. Ho, credo, in parte riuscito sul lato client con questo:Creare e analizzare richieste HTTP multipart in Python

from email.mime.multipart import MIMEMultipart, MIMEBase 
import httplib 
h1 = httplib.HTTPConnection('localhost:8080') 
msg = MIMEMultipart() 
fp = open('myfile.zip', 'rb') 
base = MIMEBase("application", "octet-stream") 
base.set_payload(fp.read()) 
msg.attach(base) 
h1.request("POST", "http://localhost:8080/server", msg.as_string()) 

L'unico problema è che la biblioteca di posta elettronica include anche il Content-Type e le intestazioni MIME-Version, e non sono sicuro come stanno andando essere correlato alle intestazioni HTTP inclusi da httplib:

Content-Type: multipart/mixed; boundary="===============2050792481==" 
MIME-Version: 1.0 

--===============2050792481== 
Content-Type: application/octet-stream 
MIME-Version: 1.0 

questo può essere il motivo che, quando la richiesta viene ricevuta da mia applicazione web.py, ottengo solo un messaggio di errore. Il gestore web.py POST:

class MultipartServer: 
    def POST(self, collection): 
     print web.input() 

Getta questo errore:

Traceback (most recent call last): 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 242, in process 
    return self.handle() 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 233, in handle 
    return self._delegate(fn, self.fvars, args) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 415, in _delegate 
    return handle_class(cls) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/application.py", line 390, in handle_class 
    return tocall(*args) 
    File "/home/richard/Development/server/webservice.py", line 31, in POST 
    print web.input() 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/webapi.py", line 279, in input 
    return storify(out, *requireds, **defaults) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 150, in storify 
    value = getvalue(value) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 139, in getvalue 
    return unicodify(x) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 130, in unicodify 
    if _unicode and isinstance(s, str): return safeunicode(s) 
    File "/usr/local/lib/python2.6/dist-packages/web.py-0.34-py2.6.egg/web/utils.py", line 326, in safeunicode 
    return obj.decode(encoding) 
    File "/usr/lib/python2.6/encodings/utf_8.py", line 16, in decode 
    return codecs.utf_8_decode(input, errors, True) 
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 137-138: invalid data 

La mia linea di codice è rappresentato dalla linea di errore di circa metà strada verso il basso:

File "/home/richard/Development/server/webservice.py", line 31, in POST 
    print web.input() 

Sta arrivando lungo , ma non sono sicuro di dove andare da qui. Si tratta di un problema con il mio codice cliente o con una limitazione di web.py (forse non è in grado di supportare richieste multipart)? Eventuali suggerimenti o suggerimenti di librerie di codici alternativi sarebbero stati ricevuti con gratitudine.

EDIT

L'errore precedente è stato causato dai dati non vengono automaticamente Base64 codificati. Aggiunta

encoders.encode_base64(base) 

Elimina questo errore e ora il problema è chiaro. richiesta HTTP non viene interpretata correttamente nel server, presumibilmente perché la libreria e-mail è compreso che dovrebbero essere le intestazioni HTTP nel corpo invece:

<Storage {'Content-Type: multipart/mixed': u'', 
      ' boundary': u'"===============1342637378=="\n' 
      'MIME-Version: 1.0\n\n--===============1342637378==\n' 
      'Content-Type: application/octet-stream\n' 
      'MIME-Version: 1.0\n' 
      'Content-Transfer-Encoding: base64\n' 
      '\n0fINCs PBk1jAAAAAAAAA.... etc 

Quindi qualcosa non è proprio lì.

Grazie

Richard

+0

cosa diavolo è una richiesta multipart http? Questo concetto è effettivamente usato? – SingleNegationElimination

+0

@TokenMacGuy - sì. sì. –

risposta

1

Dopo un po 'di esplorazione, la risposta a questa domanda è diventata chiara. La risposta breve è che sebbene lo Content-Disposition is optional in un messaggio codificato da Mime, web.py lo richieda per ogni mime-parte per analizzare correttamente la richiesta HTTP.

Contrariamente ad altri commenti su questa domanda, la differenza tra HTTP ed Email è irrilevante, in quanto sono semplicemente meccanismi di trasporto per il messaggio Mime e nulla più. I messaggi multipart/related (non multipart/form-data) sono comuni nei contenuti che scambiano i webservices, che è il caso d'uso qui. I frammenti di codice forniti sono accurati, tuttavia, e mi hanno portato a una soluzione leggermente più breve del problema.

# open an HTTP connection 
h1 = httplib.HTTPConnection('localhost:8080') 

# create a mime multipart message of type multipart/related 
msg = MIMEMultipart("related") 

# create a mime-part containing a zip file, with a Content-Disposition header 
# on the section 
fp = open('file.zip', 'rb') 
base = MIMEBase("application", "zip") 
base['Content-Disposition'] = 'file; name="package"; filename="file.zip"' 
base.set_payload(fp.read()) 
encoders.encode_base64(base) 
msg.attach(base) 

# Here's a rubbish bit: chomp through the header rows, until hitting a newline on 
# its own, and read each string on the way as an HTTP header, and reading the rest 
# of the message into a new variable 
header_mode = True 
headers = {} 
body = [] 
for line in msg.as_string().splitlines(True): 
    if line == "\n" and header_mode == True: 
     header_mode = False 
    if header_mode: 
     (key, value) = line.split(":", 1) 
     headers[key.strip()] = value.strip() 
    else: 
     body.append(line) 
body = "".join(body) 

# do the request, with the separated headers and body 
h1.request("POST", "http://localhost:8080/server", body, headers) 

Questo viene prelevato benissimo da web.py, quindi è chiaro che email.mime.multipart è adatto per creare messaggi MIME per essere trasportati da HTTP, ad eccezione della sua gestione intestazione.

Il mio altro conern globale è in scalabilità. Né questa soluzione né le altre proposte qui si adattano bene, poiché leggono il contenuto di un file in una variabile prima di raggruppare nel messaggio mime. Una soluzione migliore sarebbe quella che può serializzare su richiesta mentre il contenuto viene convogliato tramite la connessione HTTP. Non è urgente per me risolverlo, ma tornerò qui con una soluzione se ci arriverò.

+0

1. Penso che il modo migliore per impostare l'intestazione sia qualcosa come 'base.add_header ('Content-Disposition', 'file', name = 'package', .. .) '. 2. È meglio cercare '\ n \ n' (e anche' \ r \ n \ r \ n', ad esempio 're.search ('\ r? \ N \ r? \ N', ...) ') in modo da non dover dividere e unire il corpo. 3. Le linee di intestazione possono essere piegate. 4. Tecnicamente il '\ n' che termina le intestazioni non fa parte del corpo, anche se questo non è dannoso. 5. Non sono del tutto sicuro che le grammatiche RFC 5322 e RFC 2316 siano compatibili al 100% (in particolare i "personaggi" WRT rispetto agli ottetti). –

0

C'è un certo numero di cose sbagliate con la vostra richiesta. Come suggerisce TokenMacGuy, multipart/mixed non è utilizzato in HTTP; utilizzare invece multipart/form-data. Inoltre, le parti dovrebbero avere un'intestazione Content-disposition. Un frammento di python può essere trovato nello Code Recipes.

+0

grazie - come puoi vedere, ci sto ancora lavorando; Non ho capito dove è stato impostato ancora multipart/mixed. Allo stesso modo, non ho ancora utilizzato l'intestazione Content-Disposition, poiché sto ancora lavorando per ottenere una richiesta HTTP in primo luogo. La mia domanda riguarda come costruire una tale richiesta in primo luogo. Saluti, R. –

+0

Vedere la ricetta. Dimenticati di email.mime - HTTP non è email. –

+0

Ciao Martin; A proposito, ho dimostrato che la differenza tra HTTP ed e-mail è irrilevante qui - sono semplicemente trasporti e il mimo è lo stesso in entrambi i casi. Vedi la mia risposta alternativa. Grazie per i suggerimenti. R –

Problemi correlati