自定义web框架
web框架的根本是socket的代码,那么我们可以基于socket服务端的十几行代码写一个我们自己的web框架。
我们先不处理浏览器发送的请求,先让浏览器能显示我们web框架返回的信息,那我们就要按照HTTP协议的格式来发送响应。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import socket 2 3 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 sock.bind(('127.0.0.1', 8000)) 5 sock.listen() 6 7 while True: 8 conn, addr = sock.accept() 9 data = conn.recv(8096) 10 # 给回复的消息加上响应状态行 11 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") 12 conn.send(b"OK") 13 conn.close()
下面就是继续完善自定义的web框架。
1. 根据不同的路径返回不同的内容
现在的一个问题就是如何让我们的web服务根据用户请求的URL不同,然后返回不同的内容?
这个问题,我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import socket 2 3 sk = socket.socket() 4 sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口 5 sk.listen() # 监听 6 7 while True: 8 # 等待连接 9 conn, add = sk.accept() 10 data = conn.recv(8096) # 接收客户端发来的消息 11 # 从data中取到路径 12 data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串 13 # 按\r\n分割 14 data1 = data.split("\r\n")[0] 15 url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 16 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行 17 # 根据不同的路径返回不同内容 18 if url == "/index/": 19 response = b"index" 20 elif url == "/home/": 21 response = b"home" 22 else: 23 response = b"404 not found!" 24 25 conn.send(response) 26 conn.close()
2. 根据不同的路径返回不同的内容——函数版
上面的代码解决了不同的URL路径,返回不同内容的需求。
返回的内容只是简单的几个字符,那么可以将返回的内容封装成一个函数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1
2 import socket
3
4 sk = socket.socket()
5 sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
6 sk.listen() # 监听
7
8
9 # 将返回不同的内容部分封装成函数
10 def func(url):
11 s = "这是{}页面!".format(url)
12 return bytes(s, encoding="utf8")
13
14
15 while True:
16 # 等待连接
17 conn, add = sk.accept()
18 data = conn.recv(8096) # 接收客户端发来的消息
19 # 从data中取到路径
20 data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
21 # 按\r\n分割
22 data1 = data.split("\r\n")[0]
23 url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
24 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
25 # 根据不同的路径返回不同内容,response是具体的响应体
26 if url == "/index/":
27 response = func(url)
28 elif url == "/home/":
29 response = func(url)
30 else:
31 response = b"404 not found!"
32
33 conn.send(response)
34 conn.close()
3. 根据不同的路径返回不同内容——函数进阶版
上面的代码只是简单的写了一个函数,那么继续延伸下去,就可以写多个函数,不同的路径对应执行不同的函数拿到结果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import socket 2 3 sk = socket.socket() 4 sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口 5 sk.listen() # 监听 6 7 8 # 将返回不同的内容部分封装成不同的函数 9 def index(url): 10 s = "这是{}页面XX!".format(url) 11 return bytes(s, encoding="utf8") 12 13 14 def home(url): 15 s = "这是{}页面。。!".format(url) 16 return bytes(s, encoding="utf8") 17 18 19 # 定义一个url和实际要执行的函数的对应关系 20 list1 = [ 21 ("/index/", index), 22 ("/home/", home), 23 ] 24 25 while True: 26 # 等待连接 27 conn, add = sk.accept() 28 data = conn.recv(8096) # 接收客户端发来的消息 29 # 从data中取到路径 30 data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串 31 # 按\r\n分割 32 data1 = data.split("\r\n")[0] 33 url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 34 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行 35 # 根据不同的路径返回不同内容 36 func = None # 定义一个保存将要执行的函数名的变量 37 for item in list1: 38 if item[0] == url: 39 func = item[1] 40 break 41 if func: 42 response = func(url) 43 else: 44 response = b"404 not found!" 45 46 # 返回具体的响应消息 47 conn.send(response) 48 conn.close()
4. 根据不同的路径返回不同的内容——函数进阶版(返回具体的HTML文件)
如果想给浏览器返回完整的HTML内容,那么又应该怎么做呢?
不管是什么内容,最后都是转换成字节数据发送出去。我们可以打开HTML文件,读取出它内部的二进制文件,然后再发送给浏览器。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import socket 2 3 sk = socket.socket() 4 sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口 5 sk.listen() # 监听 6 7 8 # 将返回不同的内容部分封装成不同的函数 9 def index(url): 10 # 读取index.html页面的内容 11 with open("index.html", "r", encoding="utf8") as f: 12 s = f.read() 13 # 返回字节数据 14 return bytes(s, encoding="utf8") 15 16 17 def home(url): 18 with open("home.html", "r", encoding="utf8") as f: 19 s = f.read() 20 return bytes(s, encoding="utf8") 21 22 23 # 定义一个url和实际要执行的函数的对应关系 24 list1 = [ 25 ("/index/", index), 26 ("/home/", home), 27 ] 28 29 while True: 30 # 等待连接 31 conn, add = sk.accept() 32 data = conn.recv(8096) # 接收客户端发来的消息 33 # 从data中取到路径 34 data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串 35 # 按\r\n分割 36 data1 = data.split("\r\n")[0] 37 url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 38 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行 39 # 根据不同的路径返回不同内容 40 func = None # 定义一个保存将要执行的函数名的变量 41 for item in list1: 42 if item[0] == url: 43 func = item[1] 44 break 45 if func: 46 response = func(url) 47 else: 48 response = b"404 not found!" 49 50 # 返回具体的响应消息 51 conn.send(response) 52 conn.close()
index.html中的代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>index</title> 8 </head> 9 <body> 10 <div>这是index页面</div> 11 </body> 12 </html>
home.html中的代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>index</title> 8 </head> 9 <body> 10 <div>这是home页面</div> 11 </body> 12 </html>
5. 根据URL中不同的路径返回不同的内容——函数进阶版(返回独立的HTML页面)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import time 2 import socket 3 4 sk = socket.socket() 5 sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口 6 sk.listen() # 监听 7 8 9 # 将返回不同的内容部分封装成不同的函数 10 def index(url): 11 # 读取index.html页面的内容 12 with open("index.html", "r", encoding="utf8") as f: 13 s = f.read() 14 # 返回字节数据 15 return bytes(s, encoding="utf8") 16 17 18 def home(url): 19 with open("home.html", "r", encoding="utf8") as f: 20 s = f.read() 21 return bytes(s, encoding="utf8") 22 23 24 def timer(url): 25 with open("time.html", "r", encoding="utf8") as f: 26 s = f.read() 27 s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S")) 28 return bytes(s, encoding="utf8") 29 30 31 # 定义一个url和实际要执行的函数的对应关系 32 list1 = [ 33 ("/index/", index), 34 ("/home/", home), 35 ("/time/", timer), 36 ] 37 38 while True: 39 # 等待连接 40 conn, add = sk.accept() 41 data = conn.recv(8096) # 接收客户端发来的消息 42 # 从data中取到路径 43 data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串 44 # 按\r\n分割 45 data1 = data.split("\r\n")[0] 46 url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 47 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行 48 # 根据不同的路径返回不同内容 49 func = None # 定义一个保存将要执行的函数名的变量 50 for item in list1: 51 if item[0] == url: 52 func = item[1] 53 break 54 if func: 55 response = func(url) 56 else: 57 response = b"404 not found!" 58 59 # 返回具体的响应消息 60 conn.send(response) 61 conn.close()
index.html代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>index</title> 8 </head> 9 <body> 10 <div>这是index页面</div> 11 </body> 12 </html>
home.html代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>index</title> 8 </head> 9 <body> 10 <div>这是home页面</div> 11 </body> 12 </html>
time.html代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" 6 content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 7 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 8 <title>Document</title> 9 </head> 10 <body> 11 <h1>当前时间是:@@time@@</h1> 12 </body> 13 </html>