web-dev-qa-db-fra.com

Comment implémenter un serveur minimal pour AJAX en Python?

Je souhaite créer une interface graphique très simple basée sur HTML/AJAX pour un programme Python. Ainsi, l'interface est une page HTML qui communique avec le programme via AJAX. Pouvez-vous me donner une implémentation minimale pour le côté serveur en utilisant le python SimpleHTTPServer.SimpleHTTPRequestHandler?

Un exemple simple serait un champ de texte et un bouton. Lorsque le bouton est enfoncé, le contenu du champ est envoyé au serveur qui renvoie ensuite la réponse correspondante. Je suis conscient qu'il existe de nombreuses solutions puissantes pour cela en Python, mais je voudrais garder ceci très simple ... J'ai déjà trouvé quelques exemples intéressants pour un tel serveur (par exemple, ici ), mais Jusqu'à présent, je ne pouvais pas en proposer un vraiment minimal.

Au cas où vous vous demanderiez pourquoi je voulais implémenter l'interface graphique de cette manière: mon objectif est d'afficher de nombreuses données dans une mise en page agréable avec un minimum d'interaction - utiliser HTML + CSS semble donc plus pratique l’utiliser pour un affichage de données non interactif).

37
nikow

OK, je pense pouvoir maintenant répondre à ma propre question. Voici un exemple d'implémentation pour calculer le carré d'un nombre sur le serveur. S'il vous plaît laissez-moi savoir s'il y a des améliorations ou des idées fausses.

le fichier du serveur python:

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 __== "__main__":
    open_browser()
    start_server()

... et le fichier HTML (je l'appelle 'frontend.html', malheureusement, le nom doit également apparaître dans le code JavaScript):

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

Bien sûr, il serait beaucoup plus pratique d’utiliser jQuery pour la requête XML, mais par souci de simplicité, je vais le laisser comme ça.

Enfin, une autre implémentation utilisant WSGI (malheureusement, je n'ai pas trouvé de moyen de recourir au gestionnaire de fichiers standard si la requête n'est pas un 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 __== "__main__":
    open_browser()
    start_server()
50
nikow

Utilisez l’implémentation de référence WSGI . À long terme, vous serez plus heureux.

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

Demo_app est relativement facile à écrire; il gère vos demandes Ajax.

9
S.Lott

Voici un exemple simple pour Python 3 Basé sur l'exemple de @ nikow

Je sais que cela peut avoir des erreurs, commentez-les si vous les trouvez.

Le code envoie la chaîne "Je vous ai envoyé ce message" lorsque vous cliquez sur Exécuter, python répond par "Je l'ai eu"

Code HTML

(vous allez devoir utiliser la console js pour cela)

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

Code Python: importer 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()
0
Aditya Shankar

Merci pour un exemple très intuitif @nikow J'essayais de suivre votre exemple, mais j'ai obtenu une erreur:

(processus: 10281): GLib-CRITICAL **: g_slice_set_config: assertion 'sys_page_size == 0' a échoué

J'ai modifié votre code pour répondre à mes besoins.

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

mon fichier html doit-il être placé sur le serveur Web? Je ne l’ai pas encore mis là!.

0
JonB