web框架的本质
所有的web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,这样我们就可以实现一个web框架了.
- 半成品自定义的web框架
用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?
所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。
这个规则就是HTTP协议,以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。
HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?
让我们首先打印下我们在服务端接收到的消息是什么。
这样访问网页的时候,会出现如下界面:
这是因为响应的时候,必须按照一定的格式才可以.
- 可以响应成功的简单版本的自定义的Web框架,带响应行的
- 根据不同的路径返回不同的内容
这样并不能很好的响应不同的请求,可以根据解析请求的内容,根据请求不同的URL,返回不同的内容.
- 用函数实现不同的路径,不同的处理方式.可以使用一个列表,不同的路径,对应不同的函数处理方法.
# encoding:utf-8
__author__ = 'Fioman'
__date__ = '2018/11/2 17:08'
import socket
sock = socket.socket()
sock.bind(('127.0.0.1', 8000))
sock.listen()
def index(url):
s = "这是{}页面!".format(url)
return bytes(s, encoding='utf-8')
def home(url):
s = "这是{}页面!".format(url)
return bytes(s, encoding='utf-8')
func_urls = [
('/index/', index),
('/home/', home),
]
while True:
conn, addr = sock.accept()
data = conn.recv(8096)
data = str(data, encoding='utf-8')
requestLine = data.split('\r\n')[0]
url = requestLine.split(' ')[1]
# 路径和函数的对应关系
func = None
for i in func_urls:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b'404 not found!'
conn.send(b'HTTP/1.1 200 OK')
conn.send(response)
conn.close()
sock.close()
- 返回不同的HMTL文件
本质就是读取本地的HTML文件,然后以二进制数据的形式发送给客户端
# encoding:utf-8
__author__ = 'Fioman'
__date__ = '2018/11/2 17:08'
import socket
sock = socket.socket()
sock.bind(('127.0.0.1', 8000))
sock.listen()
def index(url):
with open('index.html', 'rb', encoding='utf-8') as f:
ret = f.read()
return ret
def home(url):
with open('home.html', 'rb', encoding='utf-8') as f:
ret = f.read()
return ret
func_urls = [
('/index/', index),
('/home/', home),
]
while True:
conn, addr = sock.accept()
data = conn.recv(8096)
data = str(data, encoding='utf-8')
requestLine = data.split('\r\n')[0]
url = requestLine.split(' ')[1]
# 路径和函数的对应关系
func = None
for i in func_urls:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b'404 not found!'
conn.send(b'HTTP/1.1 200 OK')
conn.send(response)
conn.close()
sock.close()
- 返回动态网页
动态网页的实现,无非就是字符串的替换
- 服务器程序和应用程序
对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。
这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。
常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
wsgiref
我们利用wsgiref模块来替换我们自己写的web框架的socket server部分
# encoding:utf-8
__author__ = 'Fioman'
__date__ = '2018/11/2 17:35'
import time
from wsgiref.simple_server import make_server
# 将返回不同的内容部分封装为函数
def index(url):
with open('index.html', 'r', encoding='utf-8') as f:
s = f.read()
now = str(time.time())
s = s.replace("@@xx@@", now)
return bytes(s, encoding='utf-8')
def home(url):
with open('home.html', 'rb', encoding='utf-8') as f:
ret = f.read()
return ret
# 定义一个url和实际要执行的函数的对应关系
func_urls = [
('/index/', index),
('/home/', home),
]
def run_server(environ, start_response):
# 设置HTTP响应的状态码和头消息
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
# 取到用户输入的url
url = environ['PATH_INFO']
func = None
for i in func_urls:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b'404 not found'
return [response,]
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8000, run_server)
print('我在8000端口等你哦.........')
# 启动服务
httpd.serve_forever()
-
Jinja2模板
虽然可以返回给用户HTML的内容以实现复杂的页面,但是如何返回动态的页面? -
自定义一套特殊的语法,进行替换
-
使用开源工具jinja2,遵循其指定语法
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>{{name}}</h1>
<ul>
{% for item in user_list %}
<li>{{item}}</li>
{% endfor %}
</ul>
</body>
</html>
# encoding:utf-8
__author__ = 'Fioman'
__date__ = '2018/11/2 18:59'
from wsgiref.simple_server import make_server
from jinja2 import Template
def index():
with open('index.html')
ret = f.read()
template = Template(ret)
data = template.render(name='John Doe', user_list=['alex', 'eric'])
return data.encode('utf-8')
def login():
f = open('login.html')
data = f.read()
return data
def routers():
urlpatterns = (
('/index/', index),
('/login/', login),
)
return urlpatterns
def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
url = environ['PATH_INFO']
urlpatterns = routers()
func = None
for item in urlpatterns:
if item[0] == url:
func = item[1]
break
if func:
return func()
else:
return '404 not found'
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8000, run_server)
print("Serving HTTP on port 8000....")
httpd.serve_forever()
总结一下
web框架的本质
socket服务端 和 浏览器客户端的通信
socket服务端功能划分:
-
a. 负责和浏览器收发消息(socket通信) -> wsgiref/uWsgi/gunicorn...
-
b. 根据用户访问的不同的URL的路径,执行不同的处理函数
-
c. 从HTML读取内容,并且完成字符串的替换. -> Jinja2模板语言
Web框架的分类
-
a. 框架自带a,b,c -> Tornado
-
b. 框架自带b和c,使用第三方的a -> Django
-
c. 框架自带b,使用第三方的a和c -> Flask