前端Web框架的实现过程
一、Web框架的本质:
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。
半成品自定义web框架
import socket server = socket.socket()
server.bind(("127.0.0.1", 8080))
server.listen() while True:
conn, addr = server.accept()
data = conn.recv(8096)
conn.send(b"OK")
conn.close()可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码是web服务的核心。
那么用户的浏览器输入一个网址,会给服务端发送数据,那么服务端会发送什么格式的数据来进行浏览器与服务端直接的收发通信呢?
必须有一个统一的规则,让双方发送消息、接收消息的时候有个格式依据,这样就不会出乱子。
这个规则就是HTTP-超文本传输协议
先看看浏览器访问服务端会发生什么:import socket server = socket.socket()
server.bind(("127.0.0.1", 8080))
server.listen() while True:
conn, addr = server.accept()
data = conn.recv(8096)
print(data)
conn.send(b"OK")
conn.close()输出:
b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n'
这就是浏览器给服务端发送的请求数据,接下来得知道,HTTP请求和相应的格式:
数据格式之GET请求:
请求首行
请求头(一堆k,v键值对)请求体(post请求携带的数据)
数据格式之响应:
响应首行
响应头(一堆k,v键值对)响应体(post请求携带的数据)
接下来继续完善web框架:(根据不同路径返回不同的内容):
'''
根据URL中不同的路径返回不同的内容
''' import socket # 创建server服务连接
server = socket.socket()
server.bind(('127.0.0.1', 8080)) # 绑定服务端IP和端口(本机做服务端则为127.0.0.1,这样socket就会自动识别。
server.listen(5) # 半连接池 # 下面是通信循环:
while True:
conn, addr = server.accept() # 等待客户的连接,这个地方是阻塞点
data_bytes = conn.recv(1024) # 接收客户的发来的请求数据
data = data_bytes.decode('utf-8') # 解码收到的数据
data1 = data.split('\r\n')[0] # 分离出第一行数据(第一个\r\n之前的数据)
url = data1.split()[1] # 继续分离,用空格来分割,然后取索引为1的元素就是请求的访问路径
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
# 根据不同的路径返回不同的内容:
if url == '/index':
response = b'index'
elif url == '/home':
response = b'home'
else:
response = b'404 not found!'
conn.send(response)
conn.close()继续优化:用函数来返回不同的内容:
import socket # 创建server服务连接
server = socket.socket()
server.bind(('127.0.0.1', 8080)) # 绑定服务端IP和端口(本机做服务端则为127.0.0.1,这样socket就会自动识别。
server.listen(5) # 半连接池 def index(url):
response = b'index'
return response def home(url):
response = b'home'
return response url_list = [
('/index', index),
('/home', home),
] # 下面是通信循环:
while True:
conn, addr = server.accept() # 等待客户的连接,这个地方是阻塞点
data_bytes = conn.recv(1024) # 接收客户的发来的请求数据
data = data_bytes.decode('utf-8') # 解码收到的数据
data1 = data.split('\r\n')[0] # 分离出第一行数据(第一个\r\n之前的数据)
url = data1.split()[1] # 继续分离,用空格来分割,然后取索引为1的元素就是请求的访问路径
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
# 根据不同的路径返回不同的内容:
func = None
for i in url_list:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b'404 not found!' conn.send(response)
conn.close()返回具体的HTML文件
完美解决了不同URL返回不同内容的问题。 但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?
没问题,不管是什么内容,最后都是转换成字节数据发送出去的。 我们可以打开HTML文件,读取出它内部的二进制数据,然后再发送给浏览器。
import socket # 创建server服务连接
server = socket.socket()
server.bind(('127.0.0.1', 8080)) # 绑定服务端IP和端口(本机做服务端则为127.0.0.1,这样socket就会自动识别。
server.listen(5) # 半连接池 def index(url):
# 读取index.html页面的内容
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
# 返回字节数据
return s.encode('utf-8') def home(url):
# 读取index.html页面的内容
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
# 返回字节数据
return s.encode('utf-8') url_list = [
('/index', index),
('/home', home),
] # 下面是通信循环:
while True:
conn, addr = server.accept() # 等待客户的连接,这个地方是阻塞点
data_bytes = conn.recv(1024) # 接收客户的发来的请求数据
data = data_bytes.decode('utf-8') # 解码收到的数据
data1 = data.split('\r\n')[0] # 分离出第一行数据(第一个\r\n之前的数据)
url = data1.split()[1] # 继续分离,用空格来分割,然后取索引为1的元素就是请求的访问路径
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
# 根据不同的路径返回不同的内容:
func = None
for i in url_list:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b'404 not found!' conn.send(response)
conn.close()让网页动态起来
这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。
没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)
import socket
import time # 创建server服务连接
server = socket.socket()
server.bind(('127.0.0.1', 8080)) # 绑定服务端IP和端口(本机做服务端则为127.0.0.1,这样socket就会自动识别。
server.listen(5) # 半连接池 def index(url):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = str(time.time())
s = s.replace("@@oo@@", now) # 在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号
return s.encode('utf-8') def home(url):
# 读取index.html页面的内容
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
# 返回字节数据
return s.encode('utf-8') url_list = [
('/index', index),
('/home', home),
] # 下面是通信循环:
while True:
conn, addr = server.accept() # 等待客户的连接,这个地方是阻塞点
data_bytes = conn.recv(1024) # 接收客户的发来的请求数据
data = data_bytes.decode('utf-8') # 解码收到的数据
data1 = data.split('\r\n')[0] # 分离出第一行数据(第一个\r\n之前的数据)
url = data1.split()[1] # 继续分离,用空格来分割,然后取索引为1的元素就是请求的访问路径
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
# 根据不同的路径返回不同的内容:
func = None
for i in url_list:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b'404 not found!' conn.send(response)
conn.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,而在正式假设服务上线用uWSGI
接下来利用wsgiref模块来替换上面写的web框架的socket server部分:import time
from wsgiref.simple_server import make_server def index(url):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = str(time.time())
# 在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号
s = s.replace("@@oo@@", now)
return s def home(url):
# 读取index.html页面的内容
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
return s def err():
return '404 not found!' url_list = [
('/index', index),
('/home', home),
] def run(env, response):
''' :param env: 请求相关信息,一个大字典,里面装了一堆处理好了的键值对数据
:param response: 相应相关信息
:return:
'''
# 固定写法 后面列表里面一个个元祖会以响应头kv键值对的形式返回给客户端
response('200 ok', [('username', 'sgt'), ('password', '')])
# 获取用户访问路径:
current_path = env.get('PATH_INFO')
# 定义一个存储函数名的变量:
func = None
for i in url_list:
if i[0] == current_path:
func = i[1]
break
if func:
res = func()
else:
res = err() return [res.encode('utf-8')] if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
server.serve_forever()jinja2
上面的代码实现了一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具:
jinja2
下载jinja2:
pip install jinja2
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<h1>姓名:{{name}}</h1>
<h1>爱好:</h1>
<ul>
{% for hobby in hobby_list %}
<li>{{hobby}}</li>
{% endfor %}
</ul>
</body>
</html>index2 html文件
使用jinja2渲染index2.html文件:
from wsgiref.simple_server import make_server
from jinja2 import Template def index():
with open("index2.html", "r") as f:
data = f.read()
template = Template(data) # 生成模板文件
ret = template.render({"name": "Alex", "hobby_list": ["烫头", "泡吧"]}) # 把数据填充到模板里面
return [bytes(ret, encoding="utf8"), ] def home():
with open("home.html", "rb") as f:
data = f.read()
return [data, ] # 定义一个url和函数的对应关系
URL_LIST = [
("/index/", index),
("/home/", home),
] def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
url = environ['PATH_INFO'] # 取到用户输入的url
func = None # 将要执行的函数
for i in URL_LIST:
if i[0] == url:
func = i[1] # 去之前定义好的url列表里找url应该执行的函数
break
if func: # 如果能找到要执行的函数
return func() # 返回函数的执行结果
else:
return [bytes("404没有该页面", encoding="utf8"), ] if __name__ == '__main__':
httpd = make_server('', 8000, run_server)
print("Serving HTTP on port 8000...")
httpd.serve_forever()现在的数据是我们自己手写的,那可不可以从数据库中查询数据,来填充页面呢?
使用pymysql连接数据库:
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select name, age, department_id from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close()创建一个测试的user表:
CREATE TABLE user(
id int auto_increment PRIMARY KEY,
name CHAR(10) NOT NULL,
hobby CHAR(20) NOT NULL
)engine=innodb DEFAULT charset=UTF8;模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。
前端Web框架的实现过程的更多相关文章
- 前端技术之:常见前端Web框架
Express 声称是快速.自由.小巧的Node.js Web框架,官网地址如下: https://expressjs.com/ https://github.com/expressjs/expres ...
- 开源的前端web框架推荐
B-JUI前端框架:http://demo.b-jui.com/ gentelella :https://colorlib.com/polygon/gentelella/ admui(收费):http ...
- python 实现web框架simfish
python 实现web框架simfish 本文主要记录本人利用python实现web框架simfish的过程.源码github地址:simfish WSGI HTTP Server wsgi模块提供 ...
- Python学习 - 编写一个简单的web框架(一)
自己动手写一个web框架,因为我是菜鸟,对于python的一些内建函数不是清楚,所以在写这篇文章之前需要一些python和WSGI的预备知识,这是一系列文章.这一篇只实现了如何处理url. 参考这篇文 ...
- 二、Web框架实现
一.简单web(socket) 在前一篇WEB框架概述一文中已经详细了解了:从浏览器键入一个URL到返回HTML内容的整个过程.说到底,本质上其实就是一个socket服务端,用户的浏览器其实就是一个s ...
- 浅析Java Web框架技术
一.Java Web框架技术的概念 所谓的Java框架,简单理解是一个可复用的设计构件,它规定了应用的体系结构,阐明了整个设计.协作构件之间的依赖关系.责任分配和控制流程,表现为一组抽象类以及其实例之 ...
- 从使用传统Web框架到切换到Spring Boot后的总结
1.前言 其实我接触 Spring Boot 的时间并不长,所以还算一个初学者,这篇文章也算是我对 Spring Boot 学习以及使用过程中的复盘,如果文章出现描述错误或表达不清晰的地方,欢迎大家在 ...
- 2015年最全的移动WEB前端UI框架
目前,众多互联网公司APP都嵌入了大量的HTML5,移动端的开发越来越重视,HTML5的运用场景也越来越多了.在移动WEB开发的过程中,使用合适的移动WEB UI框架可以大大提升我们的开发效率.下面P ...
- web前端UI框架
分类:WEB前端 时间:2016年1月13日 目前,众多互联网公司APP都嵌入了大量的HTML5,移动端的开发越来越重视,HTML5的运用场景也越来越多了.在移动WEB开发的过程中,使用合适的移动WE ...
随机推荐
- Jquery | 外部插入节点
after(content) : //在 span 元素外部的后面 插入 "<span><b>Write Less Do More</b><span ...
- UVa-11582:Colossal Fibonacci Numbers!(模算术)
这是个开心的题目,因为既可以自己翻译,代码又好写ヾ(๑╹◡╹)ノ" The i’th Fibonacci number f(i) is recursively defined in the f ...
- 自定义xml配置文件之dtd文件校验
用了很多第三方库,也看了些源码,总是想如果自己写一个类似的库,读取xml配置文件(properties配置文件比较简单) 该如何给配置文件添加头,添加校验,因为xml配置文件相对于properties ...
- SQL server函数
一般在开发中用到的函数 标量函数用的比较多 标量函数:就是返回一个单一的结果值 下面介绍一下标量函数的语法 create function GetFunction --创建函数 ( @name ...
- spring boot & mybatis集合的坑
因为是使用的mybatis逆向工程自动生成的实体类和dao层,然后在读取某一个表的content字段时出现问题. 问题描述:在mysql数据库里可以直接查询到这个字段的内容,但是使用java相关的方法 ...
- 初学者应该怎么学习前端?web前端的发展路线大剖析!
写在最前: 优秀的Web前端开发工程师要在知识体系上既要有广度和深度!应该具备快速学习能力. 前端开发工程师不仅要掌握基本的Web前端开发技术,网站性能优化.SEO和服务器端的基础知识,而且要学会运用 ...
- cvLoadImage,cvCloneImage的内存泄露问题
本文转自: http://hi.baidu.com/%C3%A8%D1%DB%D3%E3/blog/item/9d947e1b2b05555742a9adfd.html/cmtid/9872c2260 ...
- uvm_env——UVM大环境(UVM Environment )
1 What is uvm_env? uvm_env is used to create and connect the uvm_components like driver, monitors , ...
- Hadoop2.6.2的Eclipse插件的使用
欢迎转载,且请注明出处,在文章页面明显位置给出原文连接. 本文链接:http://www.cnblogs.com/zdfjf/p/5178197.html 首先给出eclipse插件的下载地址:htt ...
- 你知道现在的.net是什么样的吗,一张图告诉你
Here are these concepts used in an example sentence, for context: Application Framework - “Are you u ...