BaseHTTPRequestHandler Utf-8

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
TrayserCassa
User
Beiträge: 97
Registriert: Donnerstag 2. Mai 2013, 19:11

Hallo alle zusammen,

Ich habe zurzeit ein Problem welches ich nicht so einfach Lösen kann, da ich nicht genug Informationen, weder über Google noch über den Docs was finde.. Es geht um eine App und das ich eine "Fehler melden" Funktion hinzufügen möchte. Dabei muss ich Daten vom Android Gerät zu meinem Raspberry Pi senden. Dieser ist über Dyn DNS zu erreichen. Ich würde es einfacher finden, würde ich einen TCP Server schreiben, allerdings denke ich das ein Http Server besser und sicherer sind. Außerdem hab ich das senden von Http über Post schon integriert. :)

Der Server soll nur Empfangen und eine Antwort senden.

Erstmal die Daten die ich bekomme, wenn sonstiges keine UTF-8 zeichen beinhaltet.

Code: Alles auswählen

{b'lehrgang': [b'Truppmann1'], b'textSize': [b'false'], b'sonstiges': [b''], b'saveFalse': [b'false'], b'questsWrong': [b'false'], b'logs': [b'']}
Jetzt der Fehler der kommt, wenn ich UTF-8 zeichen sende.

Code: Alles auswählen

Exception happened during processing of request from ('192.168.2.176', 52046)
----------------------------------------
Traceback (most recent call last):
  File "S:\SDK\Python34\lib\socketserver.py", line 306, in _handle_request_noblock
    self.process_request(request, client_address)
  File "S:\SDK\Python34\lib\socketserver.py", line 332, in process_request
    self.finish_request(request, client_address)
  File "S:\SDK\Python34\lib\socketserver.py", line 345, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "S:\SDK\Python34\lib\socketserver.py", line 666, in __init__
    self.handle()
  File "S:\SDK\Python34\lib\http\server.py", line 400, in handle
    self.handle_one_request()
  File "S:\SDK\Python34\lib\http\server.py", line 388, in handle_one_request
    method()
  File "C:/Users/Patrick/Desktop/untitled/report_server.py", line 26, in do_POST
    postvars = self.parse_POST()
  File "C:/Users/Patrick/Desktop/untitled/report_server.py", line 20, in parse_POST
    keep_blank_values=1)
  File "S:\SDK\Python34\lib\urllib\parse.py", line 553, in parse_qs
    encoding=encoding, errors=errors)
  File "S:\SDK\Python34\lib\urllib\parse.py", line 605, in parse_qsl
    value = _coerce_result(value)
  File "S:\SDK\Python34\lib\urllib\parse.py", line 92, in _encode_result
    return obj.encode(encoding, errors)
UnicodeEncodeError: 'ascii' codec can't encode character '\ufffd' in position 50: ordinal not in range(128)
192.168.2.176 - - [26/Jun/2016 15:17:44] "POST / HTTP/1.1" 200 -
und nun zum Code: (Version 3.4)

Code: Alles auswählen

from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs
from cgi import parse_header, parse_multipart


class ReportHandler(BaseHTTPRequestHandler):
    def do_HEAD(self):
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()

    def parse_POST(self):
        ctype, pdict = parse_header(self.headers['content-type'])
        if ctype == 'multipart/form-data':
            postvars = parse_multipart(self.rfile, pdict)
        elif ctype == 'application/x-www-form-urlencoded':
            length = int(self.headers['content-length'])
            postvars = parse_qs(
                self.rfile.read(length),
                keep_blank_values=1)
        else:
            postvars = {}
        return postvars

    def do_POST(self):
        postvars = self.parse_POST()
        print(postvars)
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()



def main():
    server = HTTPServer(('', 21606), ReportHandler)
    try:
        print('Started http server')
        server.serve_forever()
    except KeyboardInterrupt:
        print('^C received, shutting down server')
        server.socket.close()

if __name__ == "__main__":
    main()

PS; Der Code ist "geklaut", da ich wirklich keine Ahnung habe wie ich eine http request parsen muss.. In Php ist das so einfach, deswegen wundert es mich, das Python das nicht so einfach kann ..

1. Möglichkeit zum empfangen von UTF-8?
2. Kann ich den Code soweit auch dem Internet zugänglich machen? Verbesserungsvorschläge nehme ich gerne entgegen :)


Ich sag schon mal danke fürs Lesen :)

Mit freundlichen Grüßen
Trayser

PS: wie kann ich dem Code Block sagen, dass das Python ist? :?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@TrayserCasse: HTTP arbeitet implizit mit ASCII. Alles außerhalb wird irgendwie verarbeitet, im besten Fall durchgereicht.
Python 3 ist wie bei so vielen Dingen den strikten Weg gegangen und wirft lieber einmal zu viel einen Fehler, als irgendetwas unkontrolliert durchzulassen.

Wenn Du weißt, dass Du immer utf8-codierte Parameter bekommst, dann hilft selbst zu decodieren:

Code: Alles auswählen

self.rfile.read(length).decode('utf8')
BlackJack

@Allgemeinheit: Sollte bei einem POST nicht in den HTTP-Headern irgendwo stehen wie der zu dekodieren ist?

@TrayserCassa: PHP ist eine Sprache für Webanwendungen, und zwar im Grunde nur dafür. Python ist eine allgemeine Programmiersprache für alles mögliche. Wenn man damit eine Webanwendung schreiben möchte, würde ich empfehlen ein Webrahmenwerk zu verwenden. Das muss ja nichts grosses sein, Bottle oder Flask reichen da sicher aus. Dann geht das auch einfacher als sich da selbst was zu programmieren oder von woanders zu übernehmen.

Edit: Mal Dein Beispiel, soweit ich das verstanden habe, mit `bottle`:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import bottle
from bottle import request, route


@route('/report/')
def process_report():
    data = dict(request.params.decode()) 
    print(data)
    return 'Thank you for your report: {0}'.format(data)


def main():
    bottle.run(host='', port=21606, debug=True, reloader=True)


if __name__ == '__main__':
    main()
Dem ist das jetzt gerade egal ob GET oder POST, wenn man das hart auf POST einschränken möchte, kann man das mit einem einfachen ``if`` machen. Das sendet kaputtes HTML zurück, weil der HTTP-Header als 'text/html' deklariert wird, die Daten aber keine HTML-Tags enthalten. Und < und & in `data` können so natürlich Probleme machen. `bottle` hat aber auch eine kleine Template-Engine dabei mit der man diese Probleme leicht umgehen kann. Wenn die Daten von dem POST als 'x-application/json' reinkommen, dann hat `request` ein `json`-Attribut wo die bereits geparst vorliegen, und wenn man statt einer Zeichenkette ein Wörterbuch zurück gibt, dann wird das automatisch JSON kodiert und als 'x-application/json' an den Client zurück geschickt.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@BlackJack: freundlicherweise wurde HTTP erfunden, als es noch kein Encoding gab. Vor HTML5 war auch noch nicht definiert, was für ein Encoding ein Browser verwenden sollte, falls es ein Formular verschickt. Zum Glück ist das jetzt meistens UTF8 (hab keine alte IE-Version zur Hand). Als Server bleibt einem aber sowieso nur Hoffen und Raten.
TrayserCassa
User
Beiträge: 97
Registriert: Donnerstag 2. Mai 2013, 19:11

Ich werde zwar nicht immer utf-8 bekommen, aber in den meisten Fällen leider ja.. Ich konnte auch sicher stellen, das auf der App alle utf-8 zeichen umgewandelt werden. Allerdings denke ich wird das nicht nötig sein :)

Hatte mir jetzt Flask angeschaut, war natürlich klar, dass das Beispiel jetzt mit Bottle ist :D
Aber ja das trifft es ziemlich genau ^^. Ich denke ich richte auch gleich noch eine Seite zum anschauen der Daten ein.

Im Prinzip muss ich nur ein True oder False zurück geben, da die Daten entweder ankommen oder nicht .. Ich lese mich da rein :) Da sende ich ja einen status code, wenn ich das richtig hab.

Vielen Dank euch zwei :) Wie immer schnelle und detaillierte Hilfe :)
BlackJack

@TrayserCassa: Bottle ist halt *eine* Datei die man nicht mal unbedingt installieren muss, sondern einfach als Modul dazu packen kann. Deswegen nehme ich das gerne für so kleine Sachen.
Antworten