2008-12-03 46 views
35

我想爲Python程序創建一個非常簡單的基於HTML/AJAX的GUI。所以前端是一個通過AJAX與程序通信的HTML頁面。你可以給我一個使用python SimpleHTTPServer.SimpleHTTPRequestHandler的服務器端的最小實現嗎?如何在Python中爲AJAX實現最小服務器?

一個簡單的例子是一個文本框和一個按鈕。當按鈕被按下時,字段的內容被髮送到服務器,然後服務器發回相應的答案。我知道在Python中有很多強大的解決方案,但我想保持這個非常簡單。 我已經找到了這樣的服務器的一些很好的例子(例如here),但到目前爲止,我不能拿出一個真正的最小的例子。

如果您想知道爲什麼我想要以這種方式實現GUI:我對此應用程序的關注點是以很少的交互顯示大量的數據,因此使用HTML + CSS似乎最爲方便(並且我已經將它用於非交互式數據顯示)。

回答

48

O.K.,我想我現在可以回答我自己的問題。以下是計算服務器上數字平方的示例實現。請讓我知道是否有任何改進或誤解。

蟒蛇服務器文件:

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

...和HTML文件(我稱之爲 'frontend.html',不幸的是,名稱必須出現在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> 

當然,爲XML請求使用jQuery會方便得多,但爲了簡單起見,我會這樣說。

最後使用WSGI替代實施(遺憾的是我沒有看到一個方法來依傍標準文件服務處理程序,如果請求不是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

僅用於比較,這裏是一個Ramaze示例:http://news.ycombinator.com/item?id=383960 – jfs 2008-12-03 20:19:56

9

使用WSGI reference implementation。從長遠來看,你會更開心。

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相對容易編寫;它處理你的Ajax請求。

+0

比ks,我終於設法建立了一個簡單的例子(見下面的答案)。 – nikow 2008-12-03 20:09:27

+0

聽起來很有趣。你能解釋爲什麼這種方式比BaseHTTPServer方法更可取? – FriendFX 2014-02-06 05:06:56

0

感謝一個非常直觀的例如@nikow 我試圖按照你的榜樣,但沒有得到一個錯誤:

(過程:10281):GLib的-CRITICAL **:g_slice_set_config:斷言 'sys_page_size == 0' 失敗

我修改你的代碼滿足米你需要。

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

我的html文件必須放在網絡服務器上嗎? 我還沒有把它放在那裏!

0

這裏是Python的3 一個簡單的例子基於@ nikow的例子

我知道這可能有錯誤,評論什麼的,如果你覺得他們是。

代碼發送字符串「我給你發這條消息」當您單擊運行,蟒蛇與「我知道了」響應

HTML代碼

(你得使用JS控制檯此)

<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代碼: 進口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()