2008-12-03 44 views
35

Tôi muốn tạo một GUI dựa trên HTML/AJAX rất đơn giản cho một chương trình Python. Vì vậy, giao diện là một trang HTML giao tiếp với chương trình thông qua AJAX. Bạn có thể cho tôi một triển khai tối thiểu cho phía máy chủ bằng cách sử dụng python SimpleHTTPServer.SimpleHTTPRequestHandler?Làm thế nào để triển khai một máy chủ tối thiểu cho AJAX bằng Python?

Ví dụ đơn giản sẽ là một trường văn bản và một nút. Khi nhấn nút, nội dung của trường sẽ được gửi đến máy chủ, sau đó gửi lại câu trả lời tương ứng. Tôi biết rằng có rất nhiều giải pháp mạnh mẽ cho điều này trong Python, nhưng tôi muốn giữ điều này rất đơn giản. Tôi đã tìm thấy một số ví dụ hay cho một máy chủ như vậy (ví dụ: here), nhưng cho đến nay tôi không thể đưa ra một ví dụ thực sự tối thiểu. Trong trường hợp bạn tự hỏi tại sao tôi muốn triển khai GUI theo cách như vậy: Trọng tâm của tôi cho ứng dụng này là hiển thị nhiều dữ liệu trong bố cục đẹp chỉ với tương tác tối thiểu - vì vậy việc sử dụng HTML + CSS có vẻ thuận tiện nhất (và Tôi đã sử dụng nó để hiển thị dữ liệu không tương tác).

Trả lời

48

O.K., tôi nghĩ giờ đây tôi có thể trả lời câu hỏi của riêng mình. Đây là một ví dụ thực hiện để tính toán bình phương của một số trên máy chủ. Vui lòng cho tôi biết nếu có bất kỳ cải tiến hoặc quan niệm sai lầm nào.

tập tin máy chủ 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 __name__ == "__main__": 
    open_browser() 
    start_server() 

... và các tập tin HTML (tôi gọi nó là 'frontend.html', không may tên đã xuất hiện trong mã JavaScript cũng):

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

Tất nhiên sẽ thuận tiện hơn khi sử dụng jQuery cho yêu cầu XML, nhưng vì lợi ích của sự đơn giản, tôi sẽ để nó như thế.

Cuối cùng một thực hiện thay thế sử dụng WSGI (tiếc là tôi đã không nhìn thấy một cách để rơi trở lại vào trình điều khiển file phục vụ tiêu chuẩn nếu yêu cầu không phải là một 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

Để so sánh, đây là ví dụ về Ramaze: http://news.ycombinator.com/item?id=383960 – jfs

+0

"có cách nào để quay trở lại trình xử lý Python chuẩn nếu yêu cầu không phải là POST không?" Không có nghĩa là nhiều. Bạn nghĩ bộ xử lý "chuẩn" là gì? –

+0

S.Lott: Trong lần triển khai máy chủ đầu tiên, hành vi của SimpleHTTPRequestHandler chỉ thay đổi đối với các yêu cầu POST. Tải tệp HTML do đó không yêu cầu bất kỳ mã bổ sung nào. Trong triển khai WSGI, tôi phải gửi một cách rõ ràng HTML, GET không được xử lý tự động. – nikow

9

Sử dụng WSGI reference implementation. Về lâu dài, bạn sẽ hạnh phúc hơn.

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

Bản demo_app tương đối dễ viết; nó xử lý các yêu cầu Ajax của bạn.

+0

Thần ks, cuối cùng tôi đã xây dựng một ví dụ đơn giản (xem câu trả lời của tôi bên dưới). – nikow

+0

Âm thanh thú vị. Bạn có thể giải thích tại sao đó là thích hợp hơn với phương pháp 'BaseHTTPServer'? – FriendFX

0

Cám ơn rất trực quan Ví dụ @nikow tôi đã cố gắng để làm theo ví dụ của bạn, nhưng đã nhận được một lỗi:

(quá trình: 10.281): GLib-QUAN tRỌNG **: g_slice_set_config: khẳng định 'sys_page_size == 0' thất bại

tôi sửa đổi mã của bạn để đáp ứng m y cần.

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

tệp html của tôi có được đặt trên máy chủ web không? Tôi chưa đặt nó ở đó!

+0

Có, trong ví dụ của tôi tệp được máy chủ web phân phối, vì vậy tệp phải có sẵn ở đó. – nikow

0

Dưới đây là một ví dụ đơn giản cho Python 3 dựa trên ví dụ @ nikow của

Tôi biết điều này có thể có sai sót, bình luận những gì những người đang có nếu bạn tìm thấy chúng.

Mã này gửi chuỗi "Tôi đã gửi cho bạn tin nhắn này" khi bạn nhấp chạy, trăn trả lời với "Tôi đã nhận nó"

Mã Html

(bạn đang gonna phải sử dụng js console cho điều này)

<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 Mã số: nhập khẩu 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() 
Các vấn đề liên quan