上次我们写出的服务器只能接收GET请求,这次我们完成POST请求。
首先我们把上次的代码分解一下。
首先我们先新建一个templates文件夹,把所有的需要返回的模版放入其中,毕竟代码和HTML混在一起很不好。
顺便我们把routes_dict和index、error函数拆分成一个routes.py的文件,因为我们的响应函数会越来越多,而且url也会越来越多,放在一起代码会变得很不清晰。
def template(file_name):
"""
模版函数,返回HTML字符串
"""
with open('templates/' + file_name, encoding='utf-8') as f:
content = f.read()
return content
def index(request):
header = 'HTTP/1.1 200 ok\r\nContent-Type: text/html\r\n'
body = template('index.html')
return header + '\r\n' + body
def error(request):
header = 'HTTP/1.1 404 NOT Found\r\nContent-Type: text/html\r\n'
body = template('404.html')
return header + '\r\n' + body
router_dict = {
'/': index,
}
细心的同学发现了我们的函数里多了一个request参数,因为request是个多个属性的集合,把它们拆开传进去显得很不协调而且代码会变多,所以我们建立一个Request类。
class Request:
def __init__(self, request_str):
self._parse(request_str)
def _parse(self, request_str):
"""
根据请求字符串解析需要的信息
"""
h, body = request_str.split('\r\n\r\n', 1)
self.body = body
s, headers = h.split('\r\n', 1)
s = s.split(maxsplit=2)
self.method = s[0]
path_and_qs = s[1]
# 如果?在http报文的第一行出现说明请求的path后面跟有参数
if '?' in path_and_qs:
self.path, self.qs = path_and_qs.split('?', 1)
else:
self.path = path_and_qs
self.qs = ''
# 请求头部字典
headers_dict = {}
for i in headers.split('\r\n'):
headers_dict[i.split(': ', 1)[0]] = i.split(': ', 1)[1]
self.headers = headers_dict
下面我们开始完成POST请求。
首先在routes.py中增加一个login函数。
def login(request):
if request.method == 'GET':
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
body = template('index.html')
return header + '\r\n' + body
elif request.method == 'POST':
pass
else:
return error(request)
接下来我们先看看POST的报文长什么样。
POST /login HTTP/1.1
Host: 192.168.1.102:2000
User-Agent: Mozilla/5.0 (X11; Linux armv7l; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.1.102:2000/login
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
username=200OK&password=123
跟GET报文相比较我们发现POST报文有body了,而且body是我们在前台html的form里填入的数据。
所以我们需要解析这个内容了,我们在Request类里加入一个form方法用来获得前台传入的数据。
def form(self):
"""
解析请求的数据
"""
data = {}
if self.method == 'POST':
# 如果是POST请求则数据在body里
body_list = self.body.split('&')
for item in body_list:
k, v = item.split('=')
data[k] = v
if self.method == 'GET':
# 如果是GET请求数据在url上
data_list = self.qs.split('&')
for item in data_list:
k, v = item.split('=')
data[k] = v
return data
下面我们完善login函数
def login(request):
if request.method == 'GET':
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
body = template('login.html')
return header + '\r\n' + body
elif request.method == 'POST':
data = request.form()
if data['username'] == '200OK' and data['password'] == '123':
body = template('login.html')
body = body.replace('{{ message }}', '登陆成功')
else:
body = template('login.html')
body = body.replace('{{ message }}', '登录失败')
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
return header + '\r\n' + body
else:
return error(request)
我们完成了POST请求,虽然只是很简单的功能,但是我们基本掌握了请求、处理、返回这个基本的逻辑。
项目地址:https://github.com/KEYYYYY/simple-server
网友评论