Как реализовать минимальный сервер для AJAX в Python?
Я хочу создать очень простой графический интерфейс на основе HTML/AJAX для программы на Python. Таким образом, интерфейс - это HTML-страница, которая связывается с программой через AJAX. Можете ли вы дать мне минимальную реализацию для серверной части с использованием Python SimpleHTTPServer.SimpleHTTPRequestHandler
?
Простым примером будет текстовое поле и кнопка. Когда кнопка нажата, содержимое поля отправляется на сервер, который затем отправляет обратно соответствующий ответ. Я знаю, что в Python есть много мощных решений для этого, но я хотел бы сделать это очень простым. Я уже нашел несколько хороших примеров для такого сервера (например, здесь), но до сих пор не смог придумать действительно минимальный.
В случае, если вы удивляетесь, почему я хочу реализовать GUI таким образом: моя задача для этого приложения - отображать много данных в удобной компоновке с минимальным взаимодействием - поэтому использование HTML+CSS кажется наиболее удобным (и я уже используя его для неинтерактивного отображения данных).
4 ответа
Хорошо, теперь я могу ответить на свой вопрос. Вот пример реализации для вычисления квадрата числа на сервере. Пожалуйста, дайте мне знать, если есть какие-либо улучшения или заблуждения.
файл сервера 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()
... и 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>
Конечно, было бы гораздо удобнее использовать jQuery для XML-запроса, но в интересах простоты я оставлю это так.
Наконец, альтернативная реализация, использующая 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()
Используйте эталонную реализацию WSGI. В конце концов, вы будете счастливее.
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.
Вот простой пример для Python 3, основанный на примере @ nikow
Я знаю, что это может иметь ошибки, прокомментируйте, что это такое, если вы их найдете.
Код отправляет строку "Я отправил вам это сообщение", когда вы нажимаете "Выполнить", python отвечает "Я получил это"
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()
Спасибо за очень интуитивный пример @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-файл должен быть помещен на веб-сервер? Я еще не положил его туда!