Web 框架本质解析
一 Web框架本质
1. 自己开发Web框架
- socket
- http协议
- HTML知识
- 数据库(pymysql,SQLAlchemy) HTTP:
无状态、短连接 TCP:
不断开 WEB应用(网站):
Http协议:
发送:
POST /index HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36
HTTPS: 1
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: csrftoken=hNmu2JOtntGMN0hSRSPmMQk2newEb3o8zb6pXW5Cc3m54IaA5VlTkUvqWsFezpni p=123 响应:
200 OK
Cache-Control:public, max-age=15
Connection:keep-alive
Content-Encoding:gzip
Content-Type:text/html; charset=utf-8
Date:Wed, 14 Jun 2017 01:21:17 GMT
Expires:Wed, 14 Jun 2017 01:21:33 GMT
Last-Modified:Wed, 14 Jun 2017 01:21:03 GMT
Transfer-Encoding:chunked
Vary:Accept-Encoding
X-Frame-Options:SAMEORIGIN
X-UA-Compatible:IE=10 用户在页面看到的内容“字符串”(看到页面效果,由于浏览器解析) 浏览器(socket客户端)
2. www.cnblogs.com(42.121.252.58,80)
sk.socket()
sk.connect((42.121.252.58,80)) sk.send('我想要xx')
5. 接收
6. 连接断开 博客园(socket服务端)
1. 监听ip和端口(42.121.252.58,80)
while True:
用户 = 等待用户连接
3. 收到'我想要xx'
4. 响应:“好”
用户断开 import socket sock = socket.socket()
sock.bind(('127.0.0.1',8080))
sock.listen(5) while True:
conn,addr = sock.accept() # hang住
# 有人来连接了
# 获取用户发送的数据
data = conn.recv(8096)
conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
conn.send(b'')
conn.close() 1. Http,无状态,短连接
2.
浏览器(socket客户端)
网站(socket服务端) 3. 自己写网站
a. socket服务端
b. 根据URL不同返回不同的内容
路由系统:
URL -> 函数
c. 字符串返回给用户
模板引擎渲染:
HTML充当模板(特殊字符)
自己创造任意数据
字符串 4. Web框架:
框架种类:
- a,b,c --> Tornado
- [第三方a],b,c --> wsgiref -> Django
- [第三方a],b,[第三方c] --> flask, 分类:
- Django框架(Web。。。。。。)
- 其他
web 框架本质
众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
import socket def handle_request(client):
buf = client.recv(1024)
client.send("HTTP/1.1 200 OK\r\n\r\n")
client.send("Hello, Seven") def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8000))
sock.listen(5) while True:
connection, address = sock.accept()
handle_request(connection)
connection.close() if __name__ == '__main__':
main()
上述通过socket来实现了其本质,而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
应用程序则负责具体的逻辑处理。
为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。
不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。
这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。
我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。
一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。
python标准库提供的独立WSGI服务器称为wsgiref。
from wsgiref.simple_server import make_server def RunServer(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] if __name__ == '__main__':
httpd = make_server('', 8000, RunServer)
print("Serving HTTP on port 8000...")
httpd.serve_forever()
二 自定义Web框架
框架
通过python标准库提供的wsgiref模块开发一个自己的Web框架
#!/usr/bin/env python
#coding:utf-8
from wsgiref.simple_server import make_server def index():
return 'index' def login():
return 'login' def routers(): urlpatterns = (
('/index/',index),
('/login/',login),
) return urlpatterns def RunServer(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('', 8000, RunServer)
print "Serving HTTP on port 8000..."
httpd.serve_forever()
模板引擎
在上一步骤中,对于所有的login、index均返回给用户浏览器一个简单的字符串,在现实的Web请求中一般会返回一个复杂的符合HTML规则的字符串,所以我们一般将要返回给用户的HTML写在指定文件中,然后再返回。如:
index.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Index</h1> </body>
</html>
login.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" />
<input type="text" />
<input type="submit" />
</form>
</body>
</html>
#!/usr/bin/env python
# -*- coding:utf-8 -*- from wsgiref.simple_server import make_server def index():
# return 'index'
f = open('index.html')
data = f.read()
return data def login():
# return '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('', 8000, run_server)
print "Serving HTTP on port 8000..."
httpd.serve_forever()
对于上述代码,虽然可以返回给用户HTML的内容以现实复杂的页面,但是还是存在问题:如何给用户返回动态内容?
- 1)自定义一套特殊的语法,进行替换
- 2)使用开源工具jinja2,遵循其指定语法
index.html
<!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>
#!/usr/bin/env python
# -*- coding:utf-8 -*- from wsgiref.simple_server import make_server
from jinja2 import Template def index():
# return 'index' # template = Template('Hello {{ name }}!')
# result = template.render(name='John Doe') f = open('index.html')
result = f.read()
template = Template(result)
data = template.render(name='John Doe', user_list=['alex', 'eric'])
return data.encode('utf-8') def login():
# return '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('', 8000, run_server)
print "Serving HTTP on port 8000..."
httpd.serve_forever()
遵循jinja2的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容,对于模板引擎的本质
三 自己开发web框架
实现静态网站
import socket def f1(request):
"""
处理用户请求,并返回相应的内容
:param request: 用户请求的所有信息
:return:
"""
f = open('index.fsw','rb')
data = f.read()
f.close()
return data def f2(request):
f = open('aricle.tpl','r',encoding='utf-8')
data = f.read()
f.close()
import time
ctime = time.time()
data = data.replace('@@sw@@',str(ctime))
return bytes(data,encoding='utf-8') def f3(request):
import pymysql # 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select id,username,password from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close() content_list = []
for row in user_list:
tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password'])
content_list.append(tp)
content = "".join(content_list) f = open('userlist.html','r',encoding='utf-8')
template = f.read()
f.close() # 模板渲染(模板+数据)
data = template.replace('@@sdfsdffd@@',content)
return bytes(data,encoding='utf-8') def f4(request):
import pymysql # 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select id,username,password from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close() f = open('hostlist.html','r',encoding='utf-8')
data = f.read()
f.close() # 基于第三方工具实现的模板渲染
from jinja2 import Template
template = Template(data)
data = template.render(xxxxx=user_list,user='sdfsdfsdf')
return data.encode('utf-8') routers = [
('/xxx', f1),
('/ooo', f2),
('/userlist.htm', f3),
('/host.html', f4),
] def run():
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1',8080))
sock.listen(5) while True:
conn,addr = sock.accept() # hang住
# 有人来连接了
# 获取用户发送的数据
data = conn.recv(8096)
data = str(data,encoding='utf-8')
headers,bodys = data.split('\r\n\r\n')
temp_list = headers.split('\r\n')
method,url,protocal = temp_list[0].split(' ')
conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func_name = None
for item in routers:
if item[0] == url:
func_name = item[1]
break if func_name:
response = func_name(data)
else:
response = b"" conn.send(response)
conn.close() if __name__ == '__main__':
run()
s1.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>用户登录</h1>
<form>
<p><input type="text" placeholder="用户名" /></p>
<p><input type="password" placeholder="密码" /></p>
</form>
</body>
</html> index.fsw
index.fsw
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<th>root</th>
<th>root@qq.com</th>
</tr>
</tbody>
</table>
</body>
</html> aricle.tpl
aricle.tpl
实现动态网站
import socket def f1(request):
"""
处理用户请求,并返回相应的内容
:param request: 用户请求的所有信息
:return:
"""
f = open('index.fsw','rb')
data = f.read()
f.close()
return data def f2(request):
f = open('aricle.tpl','r',encoding='utf-8')
data = f.read()
f.close()
import time
ctime = time.time()
data = data.replace('@@sw@@',str(ctime))
return bytes(data,encoding='utf-8') def f3(request):
import pymysql # 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select id,username,password from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close() content_list = []
for row in user_list:
tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password'])
content_list.append(tp)
content = "".join(content_list) f = open('userlist.html','r',encoding='utf-8')
template = f.read()
f.close() # 模板渲染(模板+数据)
data = template.replace('@@sdfsdffd@@',content)
return bytes(data,encoding='utf-8') def f4(request):
import pymysql # 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select id,username,password from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close() f = open('hostlist.html','r',encoding='utf-8')
data = f.read()
f.close() # 基于第三方工具实现的模板渲染
from jinja2 import Template
template = Template(data)
data = template.render(xxxxx=user_list,user='sdfsdfsdf')
return data.encode('utf-8') routers = [
('/xxx', f1),
('/ooo', f2),
('/userlist.htm', f3),
('/host.html', f4),
] def run():
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1',8080))
sock.listen(5) while True:
conn,addr = sock.accept() # hang住
# 有人来连接了
# 获取用户发送的数据
data = conn.recv(8096)
data = str(data,encoding='utf-8')
headers,bodys = data.split('\r\n\r\n')
temp_list = headers.split('\r\n')
method,url,protocal = temp_list[0].split(' ')
conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func_name = None
for item in routers:
if item[0] == url:
func_name = item[1]
break if func_name:
response = func_name(data)
else:
response = b"" conn.send(response)
conn.close() if __name__ == '__main__':
run() s1.py
s1.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>用户登录</h1>
<form>
<p><input type="text" placeholder="用户名" /></p>
<p><input type="password" placeholder="密码" /></p>
</form>
</body>
</html> index.fsw
index.fsw
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<th>@@sw@@</th>
<th>root@qq.com</th>
</tr>
</tbody>
</table>
</body>
</html> aricle.tpl
aricle.tpl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
{% for row in xxxxx %}
<tr>
<td>{{row.id}}</td>
<td>{{row.username}}</td>
<td>{{row.password}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{{user}}
</body>
</html> userlist.html
userlist.htmlWeb
Web 框架本质解析的更多相关文章
- WEB框架本质和第一个Django实例
Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 总的来说:Web框架的本质就是浏览 ...
- Django框架01 / http协议、web框架本质
Django框架01 / http协议.web框架本质 目录 Django框架01 / http协议.web框架本质 1.http协议 1.1 http协议简介 1.2 什么是http协议 1.3 H ...
- Python开发【第十四篇】:Web框架本质
Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...
- Web框架本质
Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:utf- ...
- Python自动化运维之26、Web框架本质、MVC与MTV
一.Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:ut ...
- Web框架本质及第一个Django实例
Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 impor ...
- Web框架本质及第一个Django实例 Web框架
Web框架本质及第一个Django实例 Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web ...
- Django之Web框架本质及第一个Django实例
Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 impor ...
- Django框架----Web框架本质
Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 impor ...
随机推荐
- struts2学习笔记(一)
1.搭建第一个struts2 app. web.xml <?xml version="1.0" encoding="UTF-8"?> <we ...
- 在Windows Python3.5 安装LightGBM
LightGBM是微软旗下DMTK推出的Gradient Boosting框架,因为其快速高效,以后或许会成为数据挖掘竞赛中的又一个大杀器.地址:https://github.com/Microsof ...
- java正则表达式基础知识(转)
1基础 2.1 简单字符类 构造 描述 [abc] a,b或c [^abc] 除a,b或c外的字符 [a-zA-Z] a至z 或 A至Z [a-d[m-p]] a至d 或 m至p [a-z&& ...
- Spring Boot(五):Spring Boot的启动器Starter大全及自定义Starter
现有启动器Starter目录 Spring Boot应用启动器基本的一共有44种,具体如下: 1)spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和 ...
- atitit.极光消息推送服务器端开发实现推送 jpush v3. 总结o7p
atitit.极光消息推送服务器端开发实现推送 jpush v3. 总结o7p 1. 推送所设计到底功能1 1.1. 内容压缩1 1.2. 多引擎1 2. reg ,设置appkey and pw ...
- [ci]持续集成系列
持续集成一直很蛋疼,感觉没底. 几个方面来写 1,搭建gitlab 配邮箱 域名等使之好用 2,搭建jenkins –yum,安装常见插件 3,搭建sonar,汉化 4,安装sonar-scanner ...
- _T("") vs L 到底用谁?L!
一直没有注意这个,今天突然纠结起来这个问题,代码写多了,难免这两个混用. 现在是时候有个结论了: 如果你的工程是unicode编译,那么请明确的使用L! 如果是多字节(ansi),那么请使用_T(&q ...
- Java时间类总结
java.util.Date 包含有年月日时分秒,精确到毫秒级别. 官方解释: // The class Date represents a specific instant in time, wit ...
- python 爬虫实战1 爬取糗事百科段子
首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把,这次我们尝试一下用爬虫把他们抓取下来. 本篇目标 抓取糗事百科热门段子 过滤带有图片的段子 实现每按一次回车显示一个段子的发布时间,发布人 ...
- python 爬虫2 Urllib库的高级用法
1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. import url ...