Python Web 框架原理

Web Socket

所谓 Web 服务,本质上就是用户使用一个 socket 客户端(浏览器)去访问一个 socket 服务端。

下面是一个最基础的基于 socket 的 Python Web 服务端程序。

import socket


# 最简单的web程序
def handle_request(connection):
	content = connection.recv(1024)
	print(content)
	connection.send(bytes("HTTP/1.1 200 OK\r\n\r\n".encode("utf-8")))
	connection.send(bytes("hello, World!".encode("utf-8")))


def service():
	server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	server_address = ('127.0.0.1', 8000)
	server.bind(server_address)
	server.listen(5)

	while True:
		connection, client_address = server.accept()
		print(connection)
		handle_request(connection)
		connection.close()


if __name__ == '__main__':
	service()

浏览器发起请求

服务端接收请求

WSGI (Web Server Gateway Interface)

上面的例子展示了 Web 的本质。

但是对于现实中的 Python Web 程序来说,一般服务端程序会分为两部分:

  • 服务器程序(用来接收、整理客户端发送的请求)
  • 应用程序(处理服务器程序传递过来的请求)

而在服务端开发应用程序时,我们会把常用的功能函数封装起来,这就是各种 Web 框架了。

Python 常见的 Web 框架有 Django,Flask,Tornado 等等。

不同的框架提供了不同程度和方式的封装,方便程序员在其上进行二次开发。

但是,服务器程序应用程序 需要相互配合才能给用户提供服务,因此我们需要制定一个标准,只要双方同时遵守这个标准,就可以相互配合了。

在 Python 中,这个标准就是 WSGI 了。

WSGI 是一种规范,它规定了服务器程序和应用程序各自使用的接口和功能,实现了二者的解耦。

Python 标准库提供的独立 WSGI 服务器称之为 wsgiref

from wsgiref.simple_server import make_server


def application(environ, start_response):
	start_response('200 OK', [('Content-Type', 'text/html')])
	return [bytes('<h1>Hello, World</h1>'.encode("utf-8")), b'42']


def run_server():
	server = make_server("127.0.0.1", 8000, application)
	server.serve_forever()


if __name__ == '__main__':
	run_server()

路径处理

上面的例子虽然实现了一个正常的服务端功能,但却有一个巨大的缺陷,那就是不同的 url 也只能返回相同的内容,为了解决这个问题,我们对代码作出如下修改。

from wsgiref.simple_server import make_server


def application(environ, start_response):
    url = environ['PATH_INFO']
    print("url:", url)
    start_response('200 OK', [('Content-Type', 'text/html')])
    if url == "/index":
        return [bytes('<h1>index!</h1>'.encode("utf-8")), b'42']
    elif url == "/login":
        return [bytes('<h1>login !</h1>'.encode("utf-8")), b'42']
    elif url == "/logout":
        return [bytes('<h1>logout !</h1>'.encode("utf-8")), b'42']
    else:
        return [bytes('<h1>404 !</h1>'.encode("utf-8")), b'42']


def run_server():
    server = make_server("127.0.0.1", 8000, application)
    server.serve_forever()


if __name__ == '__main__':
    run_server()

通过从 environ 中获取存储着路径的变量 PATH_INFO。我们就可以对不同的 url 返回不同的结果了。

分层处理

上面的例子虽然做到了对不同的路径返回不同的结果,但是所有的逻辑都写在一个函数内,一旦体量增加就会混乱不堪。

因此我们需要对应用程序进行进一步的解耦。

from wsgiref.simple_server import make_server


def index():
	return [bytes('<h1>index!</h1>'.encode("utf-8")), b'42']


def login():
	return [bytes('<h1>login !</h1>'.encode("utf-8")), b'42']


def logout():
	return [bytes('<h1>logout !</h1>'.encode("utf-8")), b'42']


urlConf = [
	("/index", index),
	("/login", login),
	("/logout", logout),
]

# 不同的网址有不同的结果,但是所有的处理逻辑写到一起,很混乱
def application(environ, start_response):
	url = environ['PATH_INFO']
	print("url:", url)

	response_fun = None

	for item in urlConf:
		if url == item[0]:
			response_fun = item[1]
			break

	if response_fun:
		start_response('200 OK', [('Content-Type', 'text/html')])
		response_body = response_fun()
	else:
		start_response('404 Not Found', [('Content-Type', 'text/html')])
		response_body = [bytes('<h1>404 !</h1>'.encode("utf-8")), b'42']
	return response_body


def run_server():
	server = make_server("127.0.0.1", 8000, application)
	server.serve_forever()


if __name__ == '__main__':
	run_server()



怎么样是不是已经有点像我们平时使用的各种框架了。

urlConf用户根据不同的路径,配置不同的函数。

server程序中遍历urlConf来根据路径定位所要执行的函数。

文件拆分

上面的例子已经有了框架的雏形了,但所有的功能都在一个 Python 文件中,很不优雅。

这时你就可以将代码分成三个文件。

view.py:专门用户存放各种页面的处理函数

url.py 配置路径和函数的关系

server.py 执行web的主程序

将不同功能的代码分门别类存放,进行进一步的解耦。

posted @ 2019-01-06 21:54  暮晨  阅读(781)  评论(0编辑  收藏  举报

Aaron Swartz was and will always be a hero