2008-12-03 8 views
35

Ich möchte eine sehr einfache HTML/AJAX-basierte GUI für ein Python-Programm erstellen. Das Frontend ist also eine HTML-Seite, die über AJAX mit dem Programm kommuniziert. Kannst du mir eine minimale Implementierung für die Server-Seite mit dem Python SimpleHTTPServer.SimpleHTTPRequestHandler geben?Wie implementiert man einen minimalen Server für AJAX in Python?

Ein einfaches Beispiel wäre ein Textfeld und eine Schaltfläche. Wenn die Taste gedrückt wird, wird der Inhalt des Feldes an den Server gesendet, der dann eine entsprechende Antwort zurücksendet. Mir ist bewusst, dass es in Python viele leistungsfähige Lösungen dafür gibt, aber ich möchte das sehr einfach halten. Ich habe bereits einige nette Beispiele für einen solchen Server gefunden (z. B. here), aber bisher konnte ich mir keinen wirklich minimalen vorstellen.

Wenn Sie sich wundern, warum ich die GUI so implementieren möchte: Mein Fokus für diese Anwendung ist es, viele Daten in einem schönen Layout mit nur minimalen Interaktion anzuzeigen - so HTML + CSS scheint am bequemsten Ich habe es bereits für nicht interaktive Datenanzeige verwendet).

Antwort

48

O.K., ich denke, ich kann jetzt meine eigene Frage beantworten. Hier ist eine Beispielimplementierung zum Berechnen des Quadrats einer Zahl auf dem Server. Bitte lassen Sie mich wissen, wenn es Verbesserungen oder Missverständnisse gibt.

die Python-Server-Datei:

import threading 
import webbrowser 
import BaseHTTPServer 
import SimpleHTTPServer 

FILE = 'frontend.html' 
PORT = 8080 


class TestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 
    """The test example handler.""" 

    def do_POST(self): 
     """Handle a post request by returning the square of the number.""" 
     length = int(self.headers.getheader('content-length'))   
     data_string = self.rfile.read(length) 
     try: 
      result = int(data_string) ** 2 
     except: 
      result = 'error' 
     self.wfile.write(result) 


def open_browser(): 
    """Start a browser after waiting for half a second.""" 
    def _open_browser(): 
     webbrowser.open('http://localhost:%s/%s' % (PORT, FILE)) 
    thread = threading.Timer(0.5, _open_browser) 
    thread.start() 

def start_server(): 
    """Start the server.""" 
    server_address = ("", PORT) 
    server = BaseHTTPServer.HTTPServer(server_address, TestHandler) 
    server.serve_forever() 

if __name__ == "__main__": 
    open_browser() 
    start_server() 

... und die HTML-Datei (ich nenne es 'frontend.html' leider der Name in der JavaScript-Code erscheinen hat auch):

<html> 
<head> 
<title>AJAX test</title> 
</head> 
<body> 
<script type="text/javascript"> 

function xml_http_post(url, data, callback) { 
    var req = false; 
    try { 
     // Firefox, Opera 8.0+, Safari 
     req = new XMLHttpRequest(); 
    } 
    catch (e) { 
     // Internet Explorer 
     try { 
      req = new ActiveXObject("Msxml2.XMLHTTP"); 
     } 
     catch (e) { 
      try { 
       req = new ActiveXObject("Microsoft.XMLHTTP"); 
      } 
      catch (e) { 
       alert("Your browser does not support AJAX!"); 
       return false; 
      } 
     } 
    } 
    req.open("POST", url, true); 
    req.onreadystatechange = function() { 
     if (req.readyState == 4) { 
      callback(req); 
     } 
    } 
    req.send(data); 
} 

function test_button() { 
    var data = document.test_form.test_text.value;   
    xml_http_post("frontend.html", data, test_handle) 
} 

function test_handle(req) { 
    var elem = document.getElementById('test_result') 
    elem.innerHTML = req.responseText 
} 

</script> 

<form name=test_form> 
sqr(
<input type="text" name="test_text" value="0" size="4"> 
) = 
<span id="test_result">0</span> 
<input type=button onClick="test_button();" value="start" title="start"> 
</form> 

</body> 
</html> 

Natürlich wäre es viel bequemer, jQuery für die XML-Anfrage zu verwenden, aber im Interesse der Einfachheit werde ich es so lassen.

schließlich eine alternative Implementierung WSGI mit (leider habe ich sehe nicht einen Weg zurück auf den Standard-File-Serving-Handler fallen, wenn die Anforderung keine POST):

import threading 
import webbrowser 
from wsgiref.simple_server import make_server 

FILE = 'frontend.html' 
PORT = 8080 

def test_app(environ, start_response): 
    if environ['REQUEST_METHOD'] == 'POST': 
     try: 
      request_body_size = int(environ['CONTENT_LENGTH']) 
      request_body = environ['wsgi.input'].read(request_body_size) 
     except (TypeError, ValueError): 
      request_body = "0" 
     try: 
      response_body = str(int(request_body) ** 2) 
     except: 
      response_body = "error" 
     status = '200 OK' 
     headers = [('Content-type', 'text/plain')] 
     start_response(status, headers) 
     return [response_body] 
    else: 
     response_body = open(FILE).read() 
     status = '200 OK' 
     headers = [('Content-type', 'text/html'), 
        ('Content-Length', str(len(response_body)))] 
     start_response(status, headers) 
     return [response_body] 

def open_browser(): 
    """Start a browser after waiting for half a second.""" 
    def _open_browser(): 
     webbrowser.open('http://localhost:%s/%s' % (PORT, FILE)) 
    thread = threading.Timer(0.5, _open_browser) 
    thread.start() 

def start_server(): 
    """Start the server.""" 
    httpd = make_server("", PORT, test_app) 
    httpd.serve_forever() 

if __name__ == "__main__": 
    open_browser() 
    start_server() 
+2

Nur zum Vergleich, hier ist ein Ramaze-Beispiel: http://news.ycombinator.com/item?id=383960 – jfs

+0

"Gibt es eine Möglichkeit, auf den Standard-Python-Handler zurückzugreifen, wenn die Anfrage kein POST ist?" Bedeutet nicht viel. Was denkst du ist der "Standard" -Handler? –

+0

S.Lott: In der ersten Server-Implementierung wird das Verhalten von SimpleHTTPRequestHandler nur für POST-Anfragen geändert. Das Laden der HTML-Datei erfordert daher keinen zusätzlichen Code. In der WSGI-Implementierung muss ich explizit den HTML-Code senden, das GET wird nicht automatisch behandelt. – nikow

9

Verwenden Sie die WSGI reference implementation. Auf lange Sicht wirst du glücklicher sein.

from wsgiref.simple_server import make_server, demo_app 

httpd = make_server('', 8000, demo_app) 
print "Serving HTTP on port 8000..." 

# Respond to requests until process is killed 
httpd.serve_forever() 

Die demo_app ist relativ einfach zu schreiben; Es verarbeitet Ihre Ajax-Anfragen.

+0

als ks, ich habe es schließlich geschafft, ein einfaches Beispiel zu erstellen (siehe meine Antwort unten). – nikow

+0

Klingt interessant. Können Sie erklären, warum dies dem 'BaseHTTPServer'-Ansatz vorzuziehen ist? – FriendFX

0

Dank für ein sehr intuitiven Beispiel @nikow ich habe versucht, Ihrem Beispiel zu folgen, aber hat einen Fehler:

(Prozess: 10.281): Glib-KRITISCH **: g_slice_set_config: Behauptung 'sys_page_size == 0' ist fehlgeschlagen

I geändert Dein Code, um m zu treffen y braucht.

webbrowser.open('file:///home/jon/workspace/webpages/frontend_example/%s' % FILE) 
// skipped the port part 
httpd = make_server("", 8080, test_app) 
// hardcoded it here. 

Muss meine HTML-Datei auf den Webserver gestellt werden? Ich habe es noch nicht dort hingelegt!

+0

Ja, in meinem Beispiel wird die Datei vom Webserver bereitgestellt, daher muss sie dort verfügbar sein. – nikow

0

Hier ist ein einfaches Beispiel für Python 3 basierend auf beispielsweise der @ nikow

Ich weiß, dass diese Fehler haben können, äußern, was diese sind, wenn Sie sie finden.

Der Code die Zeichenfolge sendet „Ich habe Ihnen diese Nachricht“, wenn Sie auf Ausführen, Python antwortet mit „Ich habe es“

Html-Code

(Sie Gonna haben die js Konsole verwenden hierfür)

<body> 
<button id="runButton">Run</button> 
<script type="text/javascript"> 
function xml_http_post(url, data) { 
var req = new XMLHttpRequest(); 
req.open("POST", url, true); 
req.onreadystatechange = function() { 
    if (req.readyState == 4) { 
    console.log(req.responseText); 
    } 
} 
req.send(data); 
} 

function runbuttonfunc() { 
    xml_http_post("frontend.html", "I sent you this message") 
} 

document.getElementById("runButton").onclick = runbuttonfunc; 
</script> 
</body> 

Python-Code: Import http.server

FILE = 'frontend.html' 
PORT = 8000 


class TestHandler(http.server.SimpleHTTPRequestHandler): 
    """The test example handler.""" 

    def do_POST(self): 
     """Handle a post request by returning the square of the number.""" 
     print(self.headers) 
     length = int(self.headers.get_all('content-length')[0]) 
     print(self.headers.get_all('content-length')) 
     data_string = self.rfile.read(length) 
     print(data_string) 
     self.send_response(200) 
     self.send_header("Content-type", "text/plain") 
     self.end_headers() 
     self.flush_headers() 
     self.wfile.write("I got it!".encode()) 


def start_server(): 
    """Start the server.""" 
    server_address = ("", PORT) 
    server = http.server.HTTPServer(server_address, TestHandler) 
    server.serve_forever() 

start_server() 
Verwandte Themen