一、Web框架
1.概念:
CS架,也就是Client/Server模式。每个用户的电脑上安装一个Client,就像QQ这种终端软件
BS架构(Browser/Server模式)越来越流行,也就是用户只需要一个浏览器就足够了
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。
这样我们就可以自己实现Web框架了
2、HTTP协议
(1)应用背景:
用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定?
你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?
所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。
这个规则就是HTTP协议,以后你发送请求信息也好,回复响应信息也罢,都要按照这个规则来。
HTTP是短连接,无状态,不会保存客户端的信息,只是单纯的一发一收
(2)HTTP组成
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。
HTTP响应的Header中有一个Content-Type
表明响应的内容格式。如text/html
表示HTML网页。
(3)请求和响应
请求::浏览器——>服务端
响应::服务端——>浏览器
》》》HTTP GET请求格式
》》》HTTP POST请求格式
》》》HTTP响应格式
总结:
》》》\r\n是header里的分界线同时也是body里的分界线
》》》\r\n\r\n是header和body的分界线,可以依据这个分割来分别得到header和body
》》》/path 指的是路径(就是 127.0.0.1地址:8080服务端端口号/index/,这个/index/就是路径,),可以通过空格分割得到路径,从而再进行进一步的处理来给客户端返回指定的数据
》》》200是标志位
二、web框架本质和WSGI
对于真实开发中的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框架
1、最最基础简单的Web框架
import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen(5) while True: conn, addr = sk.accept() # 夯在此地,等待链接 data = conn.recv(8096) print(data) conn.send(b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n") # 响应头 conn.send(bytes("<h1>hello...你好啊~</h1>", encoding="utf-8")) # 响应体 conn.close()
2、不同的URL,返回不同的内容
""" 不同的URL,返回不同的内容 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen(5) while True: conn, addr = sk.accept() # 夯在此地,等待链接 data = conn.recv(8096) # 类型转换 字节类型-->字符串 data_str = str(data, encoding="utf-8") # 取到请求头 header = data_str.split("\r\n\r\n")[0] # 再取到 “GET / HTTP/1.1” tmp = header.split("\r\n")[0] # 再按照空格分割,得到列表,取索引值是1的 url = tmp.split(" ")[1] conn.send(b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n") # 响应头 # 根据请求URL的不同,我返回不同的内容 if url == "/index/": response = "这是主页。。。" else: response = "404" conn.send(bytes(response, encoding="utf-8")) # 响应体 conn.close()
3、通过循环和函数的方式实现不同的URL返回不同的内容
""" 不同的URL,返回不同的内容 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen(5) def index(): return "这是主页" def info(): return "这是个人信息页" url_func_map = [ ("/index/", index), ("/info/", info) ] while True: conn, addr = sk.accept() # 夯在此地,等待链接 data = conn.recv(8096) # 类型转换 字节类型-->字符串 data_str = str(data, encoding="utf-8") # 取到请求头 header = data_str.split("\r\n\r\n")[0] # 再取到 “GET / HTTP/1.1” tmp = header.split("\r\n")[0] # 再按照空格分割,得到列表,取索引值是1的 url = tmp.split(" ")[1] conn.send(b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n") # 响应头 # 根据请求URL的不同,我返回不同的内容 func_name = None # 定义一个存放将要执行函数的变量 # for循环url_func_map for i in url_func_map: if i[0] == url: func_name = i[1] break if func_name: response = func_name() else: response = "404" conn.send(bytes(response, encoding="utf-8")) # 响应体 conn.close()
4、根据不同的URL返回不同的html文件
""" 不同的URL,返回不同的内容 返回的内容要是我写好的html文件 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen(5) def login(): with open("login.html", encoding="utf-8") as f: data = f.read() return data def index(): with open("index.html", encoding="utf-8") as f: data = f.read() return data def info(): return "这是个人信息页" url_func_map = [ ("/login/", login), ("/index/", index), ("/info/", info) ] while True: conn, addr = sk.accept() # 夯在此地,等待链接 data = conn.recv(8096) # 类型转换 字节类型-->字符串 data_str = str(data, encoding="utf-8") # 取到请求头 header = data_str.split("\r\n\r\n")[0] # 再取到 “GET / HTTP/1.1” tmp = header.split("\r\n")[0] # 再按照空格分割,得到列表,取索引值是1的 url = tmp.split(" ")[1] conn.send(b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n") # 响应头 # 根据请求URL的不同,我返回不同的内容 func_name = None # 定义一个存放将要执行函数的变量 # for循环url_func_map for i in url_func_map: if i[0] == url: func_name = i[1] break if func_name: response = func_name() else: response = "404" conn.send(bytes(response, encoding="utf-8")) # 响应体 conn.close()
5、4的基础上,html的内容要动起来
""" 不同的URL,返回不同的内容 返回的内容要是我写好的html文件 html里面的内容要动起来 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen(5) def login(): with open("login.html", encoding="utf-8") as f: data = f.read() import time time_s = str(time.time()) data_new = data.replace("@@xx@@", time_s) return data_new def index(): with open("index.html", encoding="utf-8") as f: data = f.read() return data def info(): return "这是个人信息页" url_func_map = [ ("/login/", login), ("/index/", index), ("/info/", info) ] while True: conn, addr = sk.accept() # 夯在此地,等待链接 data = conn.recv(8096) # 类型转换 字节类型-->字符串 data_str = str(data, encoding="utf-8") # 取到请求头 header = data_str.split("\r\n\r\n")[0] # 再取到 “GET / HTTP/1.1” tmp = header.split("\r\n")[0] # 再按照空格分割,得到列表,取索引值是1的 url = tmp.split(" ")[1] conn.send(b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n") # 响应头 # 根据请求URL的不同,我返回不同的内容 func_name = None # 定义一个存放将要执行函数的变量 # for循环url_func_map for i in url_func_map: if i[0] == url: func_name = i[1] break if func_name: response = func_name() else: response = "404" conn.send(bytes(response, encoding="utf-8")) # 响应体 conn.close()
6、5的基础上,数据从数据库中提取
""" 不同的URL,返回不同的内容 返回的内容要是我写好的html文件 html里面的内容要动起来 数据要从数据库中查找 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen(5) def login(): with open("login.html", encoding="utf-8") as f: data = f.read() import time time_s = str(time.time()) data_new = data.replace("@@xx@@", time_s) return data_new def index(): with open("index.html", encoding="utf-8") as f: data = f.read() # 链接数据库,找数据 import pymysql conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="", db="day48", charset="utf-8") cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id, name, balance from user") user_list = cursor.fetchall() cursor.close() conn.close() """ <tr> <td>id</td> <td>name</td> <td>balance</td> </tr> """ ret = "" for i in user_list: ret += """ <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> </tr> """.format(i["id"], i["name"], i["balance"]) data_new = data.replace("@@xx@@", ret) return data_new def info(): return "这是个人信息页" url_func_map = [ ("/login/", login), ("/index/", index), ("/info/", info) ] while True: conn, addr = sk.accept() # 夯在此地,等待链接 data = conn.recv(8096) # 类型转换 字节类型-->字符串 data_str = str(data, encoding="utf-8") # 取到请求头 header = data_str.split("\r\n\r\n")[0] # 再取到 “GET / HTTP/1.1” tmp = header.split("\r\n")[0] # 再按照空格分割,得到列表,取索引值是1的 url = tmp.split(" ")[1] conn.send(b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n") # 响应头 # 根据请求URL的不同,我返回不同的内容 func_name = None # 定义一个存放将要执行函数的变量 # for循环url_func_map for i in url_func_map: if i[0] == url: func_name = i[1] break if func_name: response = func_name() else: response = "404" conn.send(bytes(response, encoding="utf-8")) # 响应体 conn.close()