Ergebnis 1 bis 8 von 8

Python-CGI Refresh unterbinden

  1. #1 Zitieren
    Ritter Avatar von Krone
    Registriert seit
    Aug 2009
    Ort
    Wien
    Beiträge
    1.718
    Hey,
    ich mach gerade eine Formularbearbeitung via CGI und als Sprache Python. Es speichert das ausgefüllte Formular in einer Datei und sucht die Email heraus und sendet einen Bestätigungslink an die Adresse.
    Im Grunde funktioniert es. Es gibt nur ein major problem, nämlich, wenn man F5 drückt, kommt die Meldung des Browser bzgl. ob man das Formular nochmal abschicken will etc. und es wird dann nochmal abgeschickt. Und beim nochmaligen F5 drücken nochmal, und soweiter. Es ist zwar nicht so ein stark frequentierter Server, aber wer weiß, ob irgendwem fad wird und der dann die ganze Zeit F5 hämmert. Ein bisschen Serveraufwand kostet es halt schon und verschwendet unnötig Speicher (die Dateien bleiben bis zu 24h auf dem Server, wenn man nicht auf den Link klickt).
    Vlt könnt ihr mir helfen, wie man das lösen kann. Ich hab schon ein bisschen gegoogelt und auch diesen "http status code 205" gefunden, der irgendwie so das zu tun scheint, was ich will, aber ich hab keine Ahnung wie der funktioniert.
    LG
    Krone
    Krone ist offline

  2. #2 Zitieren
    Legende Avatar von jabu
    Registriert seit
    Jul 2011
    Beiträge
    7.383
    205 ist nicht, was du willst, siehe 205 im RFC 7231 zu HTTP/1.1.
    Du hättest damit nicht die erwünschte Aktualisierung der Seite und damit kein Feedback für den Benutzer, weil der Standard eine Aktualisierung durch den Client nicht vorsieht und keine Nutzdaten gesendet werden dürfen, also nichts weiter als der Header selbst, und selbst für den gibt es Beschränkungen.

    303 ist, was du suchst, siehe 303 im RFC 7231 zu HTTP/1.1 .
    Hier ist das Laden der Ressource durch den Client Pflicht. 302 ist nur noch bei hoffnungslos veralteten Browsern sinnvoll. Da es aus Kompatibilitätsgründen entgegen dem Standard von den gängigen Browsern ähnlich wie 303 gehandhabt wird, funktioniert es trotzdem.

    Dann müsstest du deinem Skript nur noch beibringen (praktischerweise das, welches die Post-Daten erhält), nichts weiter als den zu erstellenden Redirect-Header zu senden. Edit: Es hat sich herausgestellt, dass die Header im späteren Prozess, also sprachunabhängig, automatisch erkannt und ergänzt werden, was sehr komfortabel ist. Dafür muss man sie präzise angeben. Einmal erkannt, werden die standardmäßigen Header einfach weggelassen und ggf. Ergänzungen eingefügt.

    Die letzte Zeile der Headers ist eine Leerzeile. Sie ist Pflicht, bevor der erste Inhalt (z. B. HTML) kommt; wenn keiner kommt, sollte man sie weglassen können. Edit: Das Geht aber nicht, wie sich herausgestellt hat, weil der Apache sie auf jeden Fall braucht. Wenn kein Inhalt folgt oder wenn man keine Header angibt, muss trotzdem eine Leerzeile ausgegeben werden!

    Code:
    # Edit: Funktioniert so nicht, weil die Zeile nicht als Header erkannt wird,
    # obwohl das als Ouput richtig wäre bzw. so im Browser ankommen sollte:
    print("HTTP/1.1 303 See Other")
    # Es hat sich herausgestellt, dass die Zeichenkette ausgewertet wird,
    # indem ein Name/Key identifiziert wird, welcher dort oben fehlt. So kann es lauten:
    print("Status: 303 See Other")
    # Der Header wird automatisch ergänzt, insbesondere mit dem Protokollnamen.
    # Es ist noch nicht geklärt, ob print() auf allen Betriebssystemen und unter allen Umständen
    # die Zeile mit CR LF beendet und dieses auch in Zukunft tun wird, weshalb wir sicher gehen:
    print("Status: 303 See Other", end="\r\n")
    # Damit funktioniert es. Eine rohere Ausgabefunktion wäre zu prüfen,
    # print() macht eigentlich zu viel und könnte ein Wrapper um Folgendes sein:
    import sys 
    sys.stdout.write("...")
    # Damit hätte man auch nicht die aufgezwungenen Newlines, welche sich der Kontrolle entziehen,
    # bzw. die man, um sicher zu gehen, umdefinieren muss. Das schwebt mir vor:
    sys.stdout.write("...\r\n")
    # Vielleicht ist das performanter als print(), da man direkter an der Standardausgabe arbeitet.
    # Zudem könnten alle Header in eine Zeile gepackt werden, falls die konstant sind.
    # Desweiteren wäre zu prüfen, wann der richtige Zeitpunkt ist, um den Puffer zu leeren
    # und wie das am besten zu bewerkstelligen ist.
    
    # Der Location-Header und die Leerzeile (wobei das auch mit nur einem print() ginge):
    print("Location: http://meinehomepage.de/zielseite_der_umleitung", end="\r\n")
    print("", end="\r\n")
    
    # Hier könnte eine kleine HTML-Seite per print() mit einer Redirect-Meldung
    ausgegeben werden, aber eigentlich braucht man das bei den heutigen Geschwindigkeiten
    eher selten, weshalb ich mich auf den Header mit der Leerzeile beschränken würde.
    Die Zielseite gehört hier nicht hin, da der Browser wegen Status 303 das Get mit
    der unter "Location: ..." angegebenen Ressource von sich aus veranlasst.

    Im Firefox lässt sich z. B. mit der Erweiterung "Live HTTP headers" bequem überprüfen, welche Header tatsächlich ausgegeben werden und was automatisch ergänzt wird. Edit: Wie sich herausgestellt hat, werden die Header, falls richtig angegeben (da liegt der Teufel im Detail, s. o.), automatisch erkannt, sodass es nicht zu Doppelungen kommen sollte.

    In PHP gibt es extra die Funktion header(), was äußerst praktisch ist und kaum Fragen aufwirft. Bisher habe ich kein Äquivalent in Python finden können.

    Hier wird der allgemeine Vorgang zur Vermeidung von doppelten Posts anschaulich beschrieben:
    http://en.wikipedia.org/wiki/Post/Redirect/Get

    Solange der Browser den Status Code 303 einwandfrei implementiert, ist kein Doppelpost zu befürchten, falls ein Redirect auf die ursprüngliche Formularseite erfolgt (die dann ein Update erfahren hat), weil der Browser dann mit einem Get und nicht mit einem Post antwortet. Falls nichts veranlasst wird, sollte das Formular nach dem Laden der Seite wieder blank sein, wobei man die Rechnung ohne den Browser machen könnte, wenn der eine Autofill-Funktion aktiv hat. Aber das nochmal zu schicken, dürfte dann in den meisten Fällen kein Versehen mehr sein.

    Validieren würde ich die Formulare trotzdem, bevor sie weiterverarbeitet, gespeichert oder gar Mails aus ihnen gestrickt werden. Zudem würde ich wenigstens eine simple zufallsgenerierte Rechenaufgabe vorschalten, die von jedem leicht zu lösen ist, z. B. 2+3=_

    Möglicherweise musst du mit Sessions arbeiten, um auf Benutzer individuell reagieren zu können. Aber es geht auch ohne, wenn es genügt, dass einfach auf eine vom Status abhängige Bestätigungsseite (Fehler vs. Erfolg) umgeleitet wird, von der aus man per Link entweder zum Formular oder zur Homepage zurückkehren kann. Das wäre schnell und würde den Server kaum belasten, hätte aber den Nachteil, dass der Nutzer nicht ganz auf den Kopf gefallen sein darf. Zwei eindeutige Links, evtl. als Pfeile, sollten hoffentlich noch verstanden werden. Wenn man immer denselben Navi-Bereich sieht, müsste es auch einfach so gehen, weshalb ich das mit der 205 schon nachvollziehen kann. Aber Feedback allein vom Client (JavaScript) abhängig zu machen, wäre eine schlechte Idee.

    Dann gibt es noch diesen alten Trick, den Browser beim Versenden der Formulardaten zu veranlassen, anstelle einer separaten Seite dieselbe Formularseite noch einmal aufzurufen, wobei man ihr die Daten übergibt, was dazu führt, dass sie im selben Skriptdurchlauf zur Verfügung stehen und ausgewertet werden, in dem auch das Formular erzeugt wird, was passende Fehlerausgaben bzw. Dynamisierung ohne Umleitung und ohne Sessions ermöglicht. Aber das allein löst dein Problem nicht, nur die Abwesenheit von Sessions, weil die Daten stets mitgesendet werden. Falls du so ein an sich selbst sendendes Formular hast: Mit einer 205 funktioniert das Prinzip nicht, weil keine Nutzdaten (PHP- bzw. Python-Datei) mitgesendet werden dürfen, s.o. Dann wäre aus Anwendersicht das Formular bloß im Browser zurückgesetzt und wieder kein Feedback vorhanden.

    Wie man es auch dreht und wendet, man kommt mit 205 nicht weit, weil die angezeigte Seite zwar zurückgesetzte Eingabefelder hat, aber immer noch dieselbe ist, weshalb keine Abhängigkeit von den gesendeten Daten bestehen kann. Da bliebe vielleicht noch JavaScript im Browser übrig, um ein Feedback zu vermitteln, wenn man unbedingt 205 verwenden möchte. Aber das ist doof, weil das Skript beim Client zunächst nicht wissen kann, ob der Erfolg überhaupt eingetreten ist, sondern lediglich, ob die Eingaben plausibel waren. Man könnte damit den Server von einigen Berechnungen entlasten, aber ein echtes Feedback ist es eben nicht. Und wenn man das mit JavaScript haben will, stellt sich dasselbe Persistenzproblem wie sonst auch, wobei die Nachteile angesichts gestiegener Komplexität in Verbindung mit Timing noch viel größer ausfallen. Entweder bekommt der Nutzer ein Feedback oder keines, wobei echtes Feedback nur vom Server generiert werden kann. Alles andere ist Makulatur (bestenfalls Korrelation und ein rein grafischer Effekt), weil es keinen kausalen Zusammenhang gibt. Ein "Formular wurde gesendet", welches nicht lügt, möchte man als Benutzer gerne haben.
    jabu ist offline Geändert von jabu (17.12.2014 um 12:26 Uhr) Grund: Bessere Lesbarkeit, weniger Vermutungen, mehr Fakten. Der TE ist darüber informiert.

  3. #3 Zitieren
    Ritter Avatar von Krone
    Registriert seit
    Aug 2009
    Ort
    Wien
    Beiträge
    1.718
    Spoiler:(zum lesen bitte Text markieren)
    Zitat Zitat von jabu Beitrag anzeigen
    205 ist nicht, was du willst, siehe 205 im RFC 7231 zu HTTP/1.1.
    Du hättest damit nicht die erwünschte Aktualisierung der Seite und damit kein Feedback für den Benutzer, weil der Standard eine Aktualisierung durch den Client nicht vorsieht und keine Nutzdaten gesendet werden dürfen, also nichts weiter als der Header selbst, und selbst für den gibt es Beschränkungen.

    303 ist, was du suchst, siehe 303 im RFC 7231 zu HTTP/1.1 .
    Hier ist das Laden der Ressource durch den Client Pflicht. 302 ist nur noch bei hoffnungslos veralteten Browsern sinnvoll. Da es aus Kompatibilitätsgründen entgegen dem Standard von den gängigen Browsern ähnlich wie 303 gehandhabt wird, funktioniert es trotzdem.

    Dann müsstest du deinem Skript nur noch beibringen (praktischerweise das, welches die Post-Daten erhält), nichts weiter als den zu erstellenden Redirect-Header zu senden. Wie man in Python einzelne Headerzeilen überschreibt, müsstest du selbst sehen, da ich das nur in PHP kenne, wo es sehr einfach ist. In Python müsste das mit irgendeiner sperrigen Library gehen, ist ja im Gegensatz zu PHP nicht in erster Linie ein Homepage-Tool.
    Prinzipiell kannst du Header aber auch selbst erzeugen (die letzte ist eine Leerzeile, sie ist Pflicht, bevor der erste Inhalt (z.B. HTML) kommt; wenn keiner kommt, sollte man sie weglassen; ob das CGI-Modul sie bei Bedarf automatisch einfügt, weiß ich nicht, weshalb sie vorsichtshalber da steht):
    Code:
    print("HTTP/1.1 303 See Other")
    print("Location: http://meinehomepage.de/zielseite_der_umleitung")
    print
    # Hier könnte eine kleine HTML-Seite per print() mit einer Redirect-Meldung
    ausgegeben werden, aber eigentlich braucht man das bei den heutigen
    Geschwindigkeiten eher selten, weshalb ich mich auf den Header (ohne Leerzeile)
    beschränken würde.
    Die Zielseite gehört hier nicht hin, da der Browser wegen Status 303 das Get mit
    der unter "Location: ..." angegebenen Ressource von sich aus veranlasst.
    Was dein CGI-Modul daraus fabriziert, weiß ich nicht. Es sind ja noch einige andere Header üblich, vielleicht werden die automatisch eingefügt, könntest du mal ausprobieren, indem du sie im Browser anzeigen lässt, z.B. im Firefox mit der Erweiterung "Live HTTP headers". Insbesondere "Content-Length: 0" sowie "Connection: close" sind wichtig und dass es zu keinen Doppelungen kommt. Sollte es dazu kommen, ist vermutlich ein Kniff (oder eine Library) nötig, um das Überschreiben zu erzwingen. Oder man lässt einfach den Status-Header weg, falls der alleine doppelt ist und spekuliert darauf, dass das CGI-Modul den Redirect automatisch erkennt. Dann dürfte anstelle des 303 wegen Kompatibilität mir Uraltbrowsern eher ein 302 eingefügt werden, jedenfalls ist es bei PHP so, allerdings unter Verwendung von header().

    Ist alles ein wenig mit Vorsicht zu genießen, weil ich mit Python-CGIs nicht vertraut bin, aber im Endeffekt sollten diese Headerzeilen produziert werden. In PHP gibt es dafür extra header(), was kaum Fragen aufwirft.
    Vielleicht meldet sich hier noch jemand, der tiefer in der Materie drin steckt und die Fragen nach Ersetzung von Headern mit Python beantwortet. Sonst würde ich es einfach ausprobieren.
    Habe in diesem Zusammenhang noch dieses gefunden, vielleicht kann man damit einfach seine Response Header setzen, habe es aber nur überflogen. Sinngemäß müsste man der Funktion übergeben können, was sonst per print() ausgegeben würde, zzgl. evtl. weiterer Parameter.

    Hier wird der allgemeine Vorgang zur Vermeidung von doppelten Posts anschaulich beschrieben:
    http://en.wikipedia.org/wiki/Post/Redirect/Get

    Solange der Browser den Status Code 303 einwandfrei implementiert, ist kein Doppelpost zu befürchten, falls ein Redirect auf die ursprüngliche Formularseite erfolgt (die dann ein Update erfahren hat), weil der Browser dann mit einem Get und nicht mit einem Post antwortet. Falls nichts veranlasst wird, sollte das Formular nach dem Laden der Seite wieder blank sein, wobei man die Rechnung ohne den Browser machen könnte, wenn der eine Autofill-Funktion aktiv hat.

    Validieren würde ich die Formulare trotzdem, bevor sie weiterverarbeitet, gespeichert oder gar Mails aus ihnen gestrickt werden. Zudem würde ich wenigstens eine simple zufallsgenerierte Rechenaufgabe vorschalten, die von jedem leicht zu lösen ist, z.B. 2+3=_
    Möglicherweise musst du mit Sessions arbeiten, um auf Benutzer individuell reagieren zu können. Aber es geht auch ohne, wenn es genügt, dass einfach auf eine vom Status abhängige Bestätigungsseite (Fehler vs. Erfolg) umgeleitet wird, von der aus man per Link entweder zum Formular oder zur Homepage zurückkehren kann. Das wäre schnell und würde den Server kaum belasten, hätte aber den Nachteil, dass der Nutzer nicht ganz auf den Kopf gefallen sein darf. Zwei eindeutige Links, evtl. als Pfeile, sollten hoffentlich noch verstanden werden. Wenn man immer denselben Navi-Bereich sieht, müsste es auch einfach so gehen, weshalb ich das mit der 205 schon nachvollziehen kann. Aber Feedback von JavaScript abhängig zu machen, ist nicht so gut.

    Dann gibt es noch diesen alten Trick, den Browser beim Versenden der Formulardaten zu veranlassen, anstelle einer separaten Seite dieselbe Formularseite noch einmal aufzurufen, wobei man ihr die Daten übergibt, was dazu führt, dass sie im selben Skriptdurchlauf zur Verfügung stehen und ausgewertet werden, in dem auch das Formular erzeugt wird, was passende Fehlerausgaben bzw. Dynamisierung ohne Umleitung und ohne Sessions ermöglicht. Aber das löst dein Problem nicht, nur die Abwesenheit von Sessions, weil die Daten stets mitgesendet werden. Falls du so ein an sich selbst sendendes Formular hast: Mit einer 205 funktioniert das Prinzip nicht, weil keine Nutzdaten (PHP- bzw. Python-Datei) mitgesendet werden dürfen, s.o. Dann wäre aus Anwendersicht das Formular bloß im Browser zurückgesetzt und wieder kein Feedback vorhanden.

    Wie man es auch dreht und wendet, man kommt mit 205 nicht weit, weil die angezeigte Seite zwar zurückgesetzte Eingabefelder hat, aber immer noch dieselbe ist, weshalb keine Abhängigkeit von den gesendeten Daten bestehen kann. Da bliebe vielleicht noch JavaScript im Browser übrig, um ein Feedback zu vermitteln, wenn man unbedingt 205 verwenden möchte. Aber das ist doof, weil das Skript beim Client zunächst nicht wissen kann, ob der Erfolg überhaupt eingetreten ist, sondern lediglich, ob die Eingaben plausibel waren. Man könnte damit den Server von einigen Berechnungen entlasten, aber ein echtes Feedback ist es eben nicht. Und wenn man das mit JavaScript haben will, stellt sich dasselbe Persistenzproblem wie sonst auch, wobei die Nachteile angesichts gestiegener Komplexität in Verbindung mit Timing noch viel größer ausfallen. Entweder bekommt der Nutzer ein Feedback oder keines, wobei echtes Feedback nur vom Server generiert werden kann. Alles andere ist Makulatur (bestenfalls Korrelation und ein rein grafischer Effekt), weil es keinen kausalen Zusammenhang gibt. Ein "Formular wurde gesendet", welches nicht lügt, möchte man als Benutzer gerne haben.


    Okay, einmal vielen Dank, dass du dir die Zeit genommen hast, mir so genau zu antworten.
    Also ich glaube, ich weiß worauf du hinaus willst, bin mir nur bei der Implementierung nicht sicher.
    Ich habe jetzt urllib.request.HTTPRedirectHandler.redirect_request gefunden, dass irgendwie so aussieht, als ob es das richtige sein könnte, aber die Argumente sind nicht erklärt, da steht nur (req, fp, code, msg, hdrs). Also code wäre dann wohl 303, msg irgendwas wie 'sie werden weitergeleitet', aber der Rest? req? fp? hdrs könnte ich mir als headers vorstellen?
    Krone ist offline Geändert von Krone (14.01.2015 um 09:04 Uhr)

  4. #4 Zitieren
    Legende Avatar von jabu
    Registriert seit
    Jul 2011
    Beiträge
    7.383
    Zitat Zitat von Krone Beitrag anzeigen
    Okay, einmal vielen Dank, dass du dir die Zeit genommen hast, mir so genau zu antworten.
    Also ich glaube, ich weiß worauf du hinaus willst, bin mir nur bei der Implementierung nicht sicher.
    Ich habe jetzt urllib.request.HTTPRedirectHandler.redirect_request gefunden, dass irgendwie so aussieht, als ob es das richtige sein könnte, aber die Argumente sind nicht erklärt, da steht nur (req, fp, code, msg, hdrs). Also code wäre dann wohl 303, msg irgendwas wie 'sie werden weitergeleitet', aber der Rest? req? fp? hdrs könnte ich mir als headers vorstellen?
    Das sieht mir - ich habe es nur überflogen - eher danach aus, dass man auf Clientseite einen Handler registieren kann, welcher auf betimmte Statuscodes oder Redirects hin irgendwelche Aktionen bzw. Fehlerbehandlungen auslöst.

    Wir befinden uns jedoch auf der anderen Seite. Grundsätzlich kannst du mit print() ausgeben, was du willst, auch alle Header, wie ich es beschrieben habe.

    print() ist eine Funktion, welche auf der Standardausgabe arbeitet, so wie die Konsole von Windows. Warum das ohne "\r\n" funktionieren soll, weiß ich nicht, denn direkt auf der Standardausgabe wäre jeweils ein Zeilenumbruch nötig. Edit: Das Zeilenende wird jeweils implizit mit ausgegeben, aber unter Linux müsste es sich um ein '\n' handeln, was bei den Headern den Standard verletzen würde. Seltsamerweise scheint es in den meisten Diskussionen nicht zu stören, was mich ziemlich irritiert, da der Standard eindeutig ist.

    Ein CGI hat nun die Aufgabe, all diese Ausgaben, welche der Skriptinterpreter erzeugt, aufzufangen und an den Webserver, oftmals ein Apache, weiterzugeben. (Umgekehrt nimmt es vom Webserver entgegen, was der vom Browser erhalten hat, um es dem Skiptinterpreter als Eingabedatenstrom zur Verfügung zu stellen, etwa so, als würdest du in eine Konsole hineintippen.)

    Also kannst du mit allgemeinen Ausgabefunktionen dem Webserver etwas schicken, weshalb mein Vorschlag, das mal zu probieren, durchaus sinnvoll ist. So werden ja auch die Webseiten in den CMSen dynamisch generiert.

    So besonders ist ein Header nicht, da es sich nur um einige Textzeilen handelt, denen dann, durch eine Leerzeile getrennt, bereits der Dateiinhalt folgt. Du kannst also einfach mit einem print() nach dem anderen alle benötigten Zeilen herunterschreiben, welche der Webbrowser erhalten soll, angefangen mit den Zeilen des Headers, dann einer Leerzeile und dann dem optionalen HTML-Inhalt.

    Nun hast du mit dem CGI aber ein Problem, denn du kommunizierst nicht über einen in Python erstellten Server mit dem Browser, sondern eben über das CGI und den Apache. Das hat erst mal gar nichts mit der Auswahl der Skriptsprache zu tun, weshalb direkte Zugriffsmöglichkeiten auf das Setzen der Header im weiteren Prozess fehlen.
    (Obwohl heutzutage die CGI-Module aus Performancegründen (bessere Anbindung als mit Standardeingabe/-ausgabe, Caching etc.) durch sprachspezifische Servermodule ersetzt werden, ist aber nicht immer so gewesen. Ein reines CGI nimmt auch Ausgaben von in C geschriebenen Programmen an, denn es fängt ja die Standarddatenströme ab.)

    Das eigentliche Problem besteht nicht darin, irgendwelche Header zu schreiben, denn das geht einfach mit print(), sondern die automatisch erzeugten zu verdrängen oder zu überschreiben. Falls das einfach mit print() per nackter Standardausgabe funktioniert, muss es CGI- oder serverspezifisch und sprachunabhängig sein. Unter PHP gibt es header(), was dazu führt, dass man von der Problematik isoliert wird. Also müsste man in Erfahrung bringen, wie man Header überschreibt. Eine Google-Suche nach "python cgi set header" liefert z. B. dieses:
    https://docs.python.org/3.2/library/...t=cgi%20header
    http://webpython.codepoint.net/cgi_tutorial

    Grundsätzlich machen die auch nichts anderes, als ich im letzten Beitrag geschrieben hatte, indem sie die Header einfach auf der Standardausgabe ausgeben. Edit: Man müsste sich auch Dokus zu CGIs und Apache ansehen, insbesondere wenn Python, im Gegensatz zu PHP, das nicht kapselt oder wegabstrahiert.

    Man muss unterscheiden, ob man mit Python einen Webserver erzeugt oder ob man den vorhandenen per CGI nutzt. Dann habe ich noch dieses gefunden, wo es den Anschein erweckt, als ob dieselbe Library, welche eigentlich einen Webserver beinhaltet, nebenbei auch für CGI tauglich sei. Angeblich habe man das alles ge-'merged'. In der Hoffnung, dass es stimmt:
    https://docs.python.org/3.2/search.h...s&area=default

    Dort findet man folgendes Paar:

    send_header(keyword, value)
    end_headers()

    Falls das mit reinen CGIs ebenfalls funktioniert, wie es angedeutet wurde, wäre das möglicherweise der erhoffte Ersatz für header() in PHP. Edit: Vermutlich ist es das nicht und bezieht sich lediglich auf einen in einer PHP-Library enthaltenen Server.
    jabu ist offline Geändert von jabu (17.12.2014 um 14:39 Uhr) Grund: Bessere Lesbarkeit, weniger Vermutungen, mehr Fakten. Der TE ist darüber informiert.

  5. #5 Zitieren
    Ritter Avatar von Krone
    Registriert seit
    Aug 2009
    Ort
    Wien
    Beiträge
    1.718
    Also ich habe es jetzt versucht, einfach mit print reinzuschreiben, so wie du es angegeben hast, aber ich denke ich habe es falsch verstanden. So sieht es jetzt mal bei mir aus:
    Code:
    #!C:/python34/python.exe
    #-*- coding: utf-8 -*-
    
    import sys, os
    import urllib.parse;
    import emails;
    import random;
    
    dict_html = {ord('ä'): 'ä', ord('Ä'): 'Ä',
                 ord('ö'): 'ö', ord('Ö'): 'Ö',
                 ord('ü'): 'ü', ord('Ü'): 'Ü',
                 ord('ß'): 'ß'}
    
    
    print("HTTP/1.1 303 See Other")
    print("Location: http://localhost/index.html")
    print("""
    <!DOCTYPE HTML>
    <html>
        <head>
            <title>Danke</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        </head>
        <body>
            Danke
        </body>
    </html>
    """);
    #hier wird das ganze dann verarbeitet
    Also Fehler krieg ich mit xampp:
    malformed header from script 'absenden.py': Bad header: HTTP/1.1 303 See Other.
    Bei send_header bin ich nicht sicher, was denn jetzt der key wäre. Außerdem scheitere ich schon dran, dass es send_header nicht mehr zu geben scheint oder so, da ich beim Testen immer den Fehler bekomme:
    AttributeError: 'module' object has no attribute 'send_header' (und ja ich schreib immer http.server.send_header und es funktioniert trotzdem nicht )
    Krone ist offline

  6. #6 Zitieren
    Legende Avatar von jabu
    Registriert seit
    Jul 2011
    Beiträge
    7.383
    Zu der Fassung mit print():

    1. [...] Edit: Oh, sorry, ich sehe gerade, dass du dreifache Anführungszeichen verwendet hast und dort der Zeilenumbruch bereits am Anfang steht!
      (Allerdings ist mir das dennoch heikel, weil man vermutlich ein Windows braucht, damit immer ein \r\n bzw. CR LF ankommt. Für den Fall der Fälle würde ich das also mit einem separaten print("...", end="\r\n") machen, denn sonst könnte das verwendete Zeichen vom Format des Dokumentes oder vom Betriebssystem abhängig sein, denn unixoide Betriebssysteme verwenden nur ein LF bzw. '\n' (Windows verwendet CR LF bzw. "\r\n"). Aber auch mancher Texteditor unter Windows beherrscht Unix-Zeilenenden. Es kann also sein, dass der Zeilenumbruch unter dem Linux eines Webhosters fehlerhaft ist, aber möglicherweise dennoch von den gängigen Browsern toleriert wird. Aber was print() produziert, wäre mal mit einem Hex-Editor zu überprüfen. Nach diversen Tutorials und Foren zu urteilen, sind sich viele der Problematik nicht bewusst oder tolerieren Verletzungen des HTTP-1.1-Standards und haben einfach Glück. Das kann unter dem Linux des Webhosters oder bei Verwendung eines weniger toleranten Browsers schon anders aussehen.

    2. Hier steht, dass man mindestens die Library "cgi" importieren soll und dann, optional, noch etwas für Fehlerausgaben (Edit: Es hat sich herausgestellt, dass es ohne geht, wenn man nicht explizit Libraryfunktionen nutzt bzw. ohne die Fehlerausgaben auskommt).
      Falls dein Problem noch bestehen sollte: Beispielsweise ist es bei mir (unter PHP) nicht nötig gewesen, den Status Header einzufügen, weil im Fall eines erkannten Location Headers automatisch ein Status Header mit 302 (anstatt 303) erzeugt wurde, was nicht ganz korrekt ist, aber wegen der Kompatibilität mit alten Browsern bevorzugt wird. Aber immerhin funktioniert es in allen großen Browsern, weil die absichtlich den HTTP-1.1-Standard verletzen, um das Verhalten von 303 bei 302 zu erzwingen.
      Vielleicht bekommst du ebenfalls den Status Header 302 automatisch gesetzt, indem du den Location-Header angibst.
      Mit dem Wegfall der Fehlermeldung wüsstest du schon mal, ob der Location Header überhaupt ausgegeben wird und ob er zudem interpretiert wird. Mit dem Firefox-Plugin, welches ich angeregt hatte, lässt sich das überprüfen.

    Was diese send_header-Sache angeht, müsste in jedem Fall ein Objekt instantiiert weren, wenn ich das richtig verstehe. Für mich weist alles darauf hin, dass es immer nur darum geht, einen Server in Python zu erzeugen und nicht etwa den bereits vorhandenen per CGI zu verwenden. Trotzdem steht in einer Doku zu einer 2.x-Version, sie hätten das jetzt alles zusammengeführt. Sonst wäre ich gar nicht darauf eingestiegen. Für mich ergibt das keinen Sinn. Was hat denn ein in Python zu implementierender Webserver mit CGI zu tun? Erst mal gar nichts. Also würde ich mich auf die spezielle CGI-Doku stützen, die das mit print() macht und mal versuchen, damit alles auszureizen.

    Falls eine Key-Value-Semantik hinter dem Erkennen und Ersetzen der Header stecken sollte, könnte man bei Misserfolg mit
    print("HTTP/1.1 303 See Other")
    mal folgendes probieren:
    print("Status: 303 See Other")
    obwohl es nicht der auszugebenden Zeile entspricht.
    Edit: Es hat sich inzwischen herausgestellt, dass letzteres funktioniert.
    jabu ist offline Geändert von jabu (17.12.2014 um 13:13 Uhr) Grund: Bessere Lesbarkeit, weniger Vermutungen, mehr Fakten. Der TE ist darüber informiert.

  7. #7 Zitieren
    Ritter Avatar von Krone
    Registriert seit
    Aug 2009
    Ort
    Wien
    Beiträge
    1.718
    Spoiler:(zum lesen bitte Text markieren)
    Zitat Zitat von jabu Beitrag anzeigen
    Zu der Fassung mit print():

    1. Die Leerzeile hinter dem Location Header fehlt, dort einfach print ohne etwas dahinter in die nächste Zeile schreiben. Zwischen Header und Daten muss immer eine Leerzeile stehen.
      Edit: Da ich nicht weiß, wie das weiterverarbeitet wird, kann ich nicht mit Sicherheit sagen, dass man die Leerzeile einfügen muss. Aber im Output, den der Browser bekommt, muss sie drin sein, weil sonst alles ein großer Header wäre. Wenn etwas nicht funktioniert, sollte es daher einen Versuch wert sein. In PHP brauche ich das mit header() natürlich nicht, bei einer rohen Ausgabe schon.

      Oh, sorry, ich sehe gerade, dass du dreifache Anführungszeichen verwendet hast und dort der Zeilenumbruch bereits am Anfang steht!
      (Allerdings ist mir das dennoch heikel, weil ich nicht weiß, ob wirklich ein \r\n bzw. CR LF ankommt. Für den Fall der Fälle würde ich das also mit einem separaten print machen, denn so könnte das verwendete Zeichen vom Format deines Dokumentes abhängig sein, welches vielleicht nur ein \n bzw. LF verwendet, denn unixoide Betriebssystem verwenden nur ein LF (Windows verwendet CR LF), aber auch mancher Texteditor unter Windows kann das. Es kann also sein, dass der Zeilenumbruch fehlerhaft ist, aber u.U. dennoch von den gängigen Browsern toleriert wird. Aber was print() produziert, wäre mal mit einem Hex-Editor zu überprüfen. Anscheinend vertrauen alle blindlings darauf, dass es geht, aber die Python-Doku ist auch hier eine Zumutung. In C sowie in C++ weiß man immer ganz genau, dass eine Zeichenkette mit einem '\0' endet, und wenn man was anderes haben will, muss man es einfügen. Das ist mir zigmal sympathischer als was Skriptsprachen fabrizieren. Auch die Doku ist besser, weil klar und verbindlich. Vielleicht findet eine automatische Ersetzung statt, aber bei einer Leerzeile? Die Doku zu Python-CGI ist schlecht, sonst würde ich nicht fragen.)
    2. Hier steht, dass man mindestens
      import cgi
      einfügen soll und dann, optional, noch etwas für Fehlerausgaben. Ob dein Problem damit behoben ist, weiß ich nicht. Falls es das nicht ist: Beispielsweise ist es bei PHP nicht nötig, den Status Header einzufügen, weil im Fall eines erkannten Location Headers automatisch ein Status Header mit 302 erzeugt wird, was eigentlich nicht ganz korrekt ist, aber wegen der Kompatibilität mit alten Browsern bevorzugt wird. Aber immerhin funktioniert es in allen großen Browsern, weil die absichtlich den HTTP-1.1-Standard verletzen, um das Verhalten von 303 bei 302 zu erzwingen. Vielleicht bekommst du ebenfalls den Status Header 302 automatisch gesetzt, probieren würde ich es. Mit dem Wegfall der Fehlermeldung wüsstest du schon mal, ob der Location Header überhaupt ausgegeben wird und ob er zudem interpretiert wird. Und mit dem Firefox-Plugin, was ich angeregt hatte, kannst du prüfen, ob der Status Header automatisch gesetzt wurde (oder eben doch nicht).

    Zuerst würde ich die Library bzw. Libraries einbinden und die Leerzeile einfügen und erst wenn die Fehlermeldung bleibt, den Statusheader testweise auskommentieren.

    Was diese send_header-Sache angeht, müsste in jedem Fall ein Objekt instantiiert weren, wenn ich das richtig verstehe. Für mich weist alles darauf hin, dass es immer nur darum geht, einen Server in Python zu erzeugen und nicht etwa den bereits vorhandenen per CGI zu verwenden. Trotzdem steht in einer Doku zu einer 2.x-Version, sie hätten das jetzt alles zusammengeführt. Sonst wäre ich gar nicht darauf eingestiegen. Für mich ergibt das keinen Sinn. Was hat denn ein in Python zu implementierender Webserver mit CGI zu tun? Erst mal gar nichts.
    Also würde ich mich auf die spezielle CGI-Doku stützen, die das mit print() macht und mal versuchen, damit alles auszureizen.

    Falls eine Key-Value-Semantik hinter dem Erkennen und Ersetzen der Header stecken sollte, könnte man bei Misserfolg mit
    print("HTTP/1.1 303 See Other")
    mal folgendes probieren:
    print("Status: 303 See Other")
    obwohl es nicht der auszugebenden Zeile entspricht. Aber vielleicht braucht der Parser das einfach, um eine Abgrenzung von x-beliebigen Strings vorzunehmen.

    Mal so ein Gedanke am Rande:
    Es wird oft gesagt, PHP sei unsauber. Aber dieses ist alles noch viel schlimmer, betrifft zwar nicht die Sprache an sich, aber die Maintainer müssten doch Krämpfe bekommen, wenn so viel Unnützes miserabel beigeflickt wird und Essentielles in den Libs nicht sauber implementiert ist. Ich würde solche Commits abweisen, und zwar radikal. Zudem ist die Python-Doku bereits von ihrem Aufbau her Bullshit, und es wird nichts explizit erklärt, sondern nur selbstreferentiell. Das kann man im Code machen, wenn ihn kein anderer ansieht, nicht aber in einer Doku. Selbst Microsoft ist denen um Längen voraus. Die einfachsten Fragen sind wesentlich und nicht etwa, was ich mit fertig implementierten Servern und Clients anstellen kann. Das sind Fertigprogramme, die einem nur das Gefühl geben, etwas programmiert zu haben, indem man sie als Objekte instantiiert. Sowas ist sekundär zu behandeln, steht hier aber ganz im Vordergrund, während Grundlegendes sträflich vernachlässigt wird. Sonst hätte der TE vermutlich nicht fragen müssen. Gut, es ist alles umsonst, take it or leave it. Aber wo bleibt der Ehrgeiz, wenn - gefühlt - die halbe Welt es verwendet?


    Deine zweite Idee (also ohne dem print("HTTP/1.1 303 See Other")) hat das Problem gelöst.
    Danke vielmals für deine Hilfe .
    LG
    Krone
    Krone ist offline Geändert von Krone (14.01.2015 um 09:04 Uhr)

  8. #8 Zitieren
    Legende Avatar von jabu
    Registriert seit
    Jul 2011
    Beiträge
    7.383
    Freut mich, dass es nun geht. Inzwischen habe ich das installiert:

    • Python 3.4.1
      In Bytecode kompiliert, arbeitet bisher reibungslos und schnell, danke liebe Entwickler.
    • XAMPP 5.6.3
      Hier gibt es etwas Reibung:
      Bei der Installation wird vorgeschlagen, die UAC zu deaktivieren, ein absolutes No-Go, ganz gleich, ob das dem einen oder anderen "hilft"!!!
      Irgendwas muss eine ältere VC++ Runtime benötigen, die ihre Installationsdateien schön auf C:\ ergießt.
      Die Begrüßungs- und Statusseiten werden als UTF-8 ausgeliefert, sind aber nach ANSI und mit Codepages kodiert, vermutlich aus Kompatibilitätsgründen. Man kann sie einfach umkodieren, z. B. mit Notepad++.
      Die Performance ist in diesen Konfigurationsseiten (in PHP) schlecht, keine Ahnung, wie sie sonst ist.
      Ansonsten macht das einen brauchbaren Eindruck, auch hier mein Dank an die Entwickler!

    Dann habe ich das Diskutierte ausprobiert, es funktioniert:
    Code:
    #!C:/Python34/python.exe
    #-*- coding: utf-8 -*-
    
    print("Status: 303 See Other", end="\r\n")
    print("Location: http://localhost/index.html", end="\r\n")
    # Folgende Zeile anpassen, falls nicht UTF-8;
    # kann entfallen, wenn standardmäßig der passende Header gesetzt wird
    # oder wenn es sich um eine reine Umleitung handelt:
    print("Content-Type: text/html; charset=UTF-8", end="\r\n")
    print("", end="\r\n")
    
    # Hier kommt der HTML-Kram, kann bei Umleitung entfallen.
    Das geht ohne irgendwelche Imports. Vermutlich braucht man die nur für Zusatzfunktionen, Diagnosen etc. aus den Libs. Das ist hier anscheinend ein ganz gewöhnliches CGI (kein Python-Modul), dem man alles übergeben kann. Das werde ich mal in C oder C++ probieren, müsste auch gehen.
    Der Content-Type-Header muss ganz genau stimmen, auch die Leerzeichen. Firefox reagiert sonst mit Verweigerung. Der IE ist wesentlich toleranter. Firefox gewinnt den Vergleich, weil er ein kaputtes Web bekämpft. Tests im IE sind sinnvoll, aber nicht als primär geeignet.

    Beobachtungen:

    1. Es muss zumindest für einfachen Output nichts inkludiert werden.
    2. Die Leerzeile muss immer mitgegeben werden, auch wenn nichts weiter folgt, z. B. bei einer Umleitung, weil sich sonst der Apache beschwert. Vielleicht fordert das auch der Standard, bin mir gerade nicht sicher.
    3. Wegen der Einhaltung des HTTP-1.1-Standards bzgl. Linebreaks nach Headern:
      Mein Hex-Editor sagt, dass Python unter Windows bei print() automatisch CR LF bzw. "\r\n" produziert und sich damit konform verhält, ganz gleich, ob das Dokument Windows- oder Unix-Zeilenumbrüche verwendet.
      Bei genauerer Betrachtung wird mir klar, weshalb das auch innerhalb von mehrzeiligen Zeichenketten sein muss, denn das Dokument würde sonst mit seiner Struktur die Ebene der Syntax verletzen. Ohne Escaping handelt es sich um Steuerzeichen auf Dokumentebene und nicht um Inhalt, weshalb es egal ist, welche Konvention zu dem Zeilenende geführt hat.
      Es ist allgemein erforderlich, die Dokumente vor dem Hochladen auf ein unixoides BS zu konvertieren, schon wegen des (anzupassenden) Shebangs in der ersten Zeile, der mit einem angehängten '\r' unter Linux fehlerhaft wäre.
      Die Zeilenenden sind explizit festzulegen, um Portabilität zu erhalten bzw. weil sonst nach dem Konvertieren für Linux auch in den Headern das "\r\n" durch '\n' ersetzt wäre, was sich leicht vermeiden lässt:
      Code:
      print("Irgendein Header", end="\r\n") #siehe obenstehenden Code.
      Oder man verwendet eine direktere Ausgabefunktion und fügt "\r\n" ein:
      Code:
      import sys
      sys.stdout.write("Status: 303 See Other\r\n"
                       "Location: http://localhost/index.html\r\n"
                       "Content-Type: text/html; charset=UTF-8\r\n\r\n")
      # hier nur der Übersichtlichkeit wegen auf drei Zeilen verteilt
    4. Fehlende Header werden automatisch eingefügt.
    5. Gesetzte Header werden automatisch erkannt und sogar ergänzt.
    6. Wenn man Header durcheinanderwürfelt, werden sie automatisch sortiert, das gilt nicht für die Leerzeile, weil sie das Ende signalisiert. Eine gute Praxis ist ein Vertauschen sicher nicht, aber es gibt einigen Aufschluss darüber, was später (CGI+Apache) noch passiert.
    7. Jedes Headerfeld wird anhand seines Namens innerhalb der Zeichenkette identifiziert, auch der Status-Header, obwohl er im Output ohne Namen steht.
    8. Die Header sagen nichts über die Länge des Inhalts aus und lassen auch nicht die Verbindung schließen. Zumindest ersteres könnte an der direkten Ausgabe per CGI liegen. Sprachspezifische Servermodule führen ihr eigenes Caching durch und lassen sich separat konfigurieren, was wir hier nicht haben. Der Apache weiß ja nicht, was er bekommen wird, während er die Header verarbeitet. Gut, was fehlt, kann man ergänzen. Andererseits hat Caching auch Nachteile, wenn ein Server selten besucht wird. Dann dauert der erste Seitenaufruf sogar länger. Sonst ist es eher vorteilhaft. Falls anstelle eines CGIs ein Servermodul verwendet wird, müsste das eine bessere Leistung ergeben.
    9. Die Performance von zu Bytecode kompiliertem Python ist mindestens gut.


    Eine kleine Bitte meinerseits, nur falls es dir nichts ausmacht:
    Könntest du vielleicht die Fullquotes, also diese Komplettzitate, löschen oder wenigstens in Spoiler packen? Dann könnte dieser Thread wegen besserer Lesbarkeit leichter anderen helfen. Das wäre sehr nett von dir!
    Deine Antworten würdest du natürlich stehen lassen. Ich meine, dass auch ohne Fullquotes klar ist, worauf du dich beziehst.
    Dann hätten wir hier schon ein kleines Tutorial. Denn während meiner Recherche musste ich feststellen, dass es nur wenig klare Aussagen zum Thema gibt, obwohl der Gegenstand doch ziemlich simpel ist. Auf einschlägigen Seiten gibt es zwar korrekte Hinweise, aber es ist etwa ebensoviel falsch wie richtig, wenn es manchmal auch nur Kleinigkeiten sind, die den Erfolg verhindern, aber immer wieder andere. Und vieles hat nichts mit CGIs zu tun, obwohl es den Anschein erweckt. Da fehlt eine klare Sprache.
    jabu ist offline Geändert von jabu (17.12.2014 um 18:36 Uhr)

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •