一 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‘123123‘) ???????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应用,本质上其实就是一个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-8from 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_serverfrom 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 socketdef f1(request): ???""" ???处理用户请求,并返回相应的内容 ???:param request: 用户请求的所有信息 ???:return: ???""" ???f = open(‘index.fsw‘,‘rb‘) ???data = f.read() ???f.close() ???return datadef 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"404" ???????conn.send(response) ???????conn.close()if __name__ == ‘__main__‘: ???run()
<!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
<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<title></title></head><body> ???<table border="1"> ???????<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
实现动态网站
import socketdef f1(request): ???""" ???处理用户请求,并返回相应的内容 ???:param request: 用户请求的所有信息 ???:return: ???""" ???f = open(‘index.fsw‘,‘rb‘) ???data = f.read() ???f.close() ???return datadef 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"404" ???????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
<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<title></title></head><body> ???<table border="1"> ???????<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
<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<title></title></head><body> ???<table border="1"> ???????<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
Web 框架本质解析
原文地址:https://www.cnblogs.com/bigtreei/p/8366349.html