美文网首页
用Socket搭建一个简易网站(二)

用Socket搭建一个简易网站(二)

作者: 我有一只碗 | 来源:发表于2018-02-14 16:27 被阅读0次

上次我们写出的服务器只能接收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

相关文章

网友评论

      本文标题:用Socket搭建一个简易网站(二)

      本文链接:https://www.haomeiwen.com/subject/qtuetftx.html