[Python之路] 实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)

摘要:
本文描述了如何简单地实现自定义Web服务器和自定义简单框架,并继续迭代版本,以便清楚地显示服务器和Web框架如何协同工作。本文中有很多代码,但每次迭代都很少有变化。注意代码的黄色背景部分,即修改部分。

本文描述如果简单实现自定义Web服务器与自定义简易框架,并且不断进行版本迭代,从而清晰的展现服务器与Web框架之间是如何结合、如何配合工作的。以及WSGI是什么。

本文帖的代码有点多,但基本每次迭代修改的地方很少(为了每一节相对完整,所以重复代码比较多),注意看代码中黄色背景的部分,即是修改的部分。

一、选取一个自定义的服务器版本

参照 https://www.cnblogs.com/leokale-zz/p/11957768.html 中的各种服务器实现版本,我们选择比较简单的多进程版本作为演示版本

代码如下:

import socket
import re
import multiprocessing


def handle_request(new_socket):
    # 接收请求
    recv_msg = ""
    recv_msg = new_socket.recv(1024).decode("utf-8")
    if recv_msg == "":
        print("recv null")
        new_socket.close()
        return

    # 从请求中解析出URI
    recv_lines = recv_msg.splitlines()
    print(recv_lines.__len__())
    # 使用正则表达式提取出URI
    ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
    if ret:
        # 获取URI字符串
        file_name = ret.group(1)
        # 如果URI是/,则默认返回index.html的内容
        if file_name == "/":
            file_name = "/index.html"

    try:
        # 根据请求的URI,读取相应的文件
        fp = open("." + file_name, "rb")
    except:
        # 找不到文件,响应404
        response_msg = "HTTP/1.1 404 NOT FOUND
"
        response_msg += "
"
        response_msg += "<h1>----file not found----</h1>"
        new_socket.send(response_msg.encode("utf-8"))
    else:
        html_content = fp.read()
        fp.close()
        # 响应正确 200 OK
        response_msg = "HTTP/1.1 200 OK
"
        response_msg += "
"

        # 返回响应头
        new_socket.send(response_msg.encode("utf-8"))
        # 返回响应体
        new_socket.send(html_content)

    # 关闭该次socket连接
    new_socket.close()


def main():
    # 创建TCP SOCKET实例
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # # 设置重用地址
    # tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 绑定地址(默认本机IP)和端口
    tcp_server_socket.bind(("", 7890))
    # 监听
    tcp_server_socket.listen(128)
    # 循环接收客户端连接
    while True:
        new_socket, client_addr = tcp_server_socket.accept()
        # 启动一个子进程来处理客户端的请求
        sub_p = multiprocessing.Process(target=handle_request, args=(new_socket,))
        sub_p.start()
        # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
        new_socket.close()

    # 关闭整个SOCKET
    tcp_server_socket.close()


if __name__ == "__main__":
    main()

二、将代码用面向对象思想改写

import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 创建socket实例
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置资源重用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定IP端口
        self.tcp_server_socket.bind(("", 7890))
        # 开始监听
        self.tcp_server_socket.listen(128)

    # 请求处理函数
    def handle_request(self, new_socket):
        # 接收请求
        recv_msg = ""
        recv_msg = new_socket.recv(1024).decode("utf-8")
        if recv_msg == "":
            print("recv null")
            new_socket.close()
            return

        # 从请求中解析出URI
        recv_lines = recv_msg.splitlines()
        # 使用正则表达式提取出URI
        ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
        if ret:
            # 获取URI字符串
            file_name = ret.group(1)
            # 如果URI是/,则默认返回index.html的内容
            if file_name == "/":
                file_name = "/index.html"

        try:
            # 根据请求的URI,读取相应的文件
            fp = open("." + file_name, "rb")
        except:
            # 找不到文件,响应404
            response_msg = "HTTP/1.1 404 NOT FOUND
"
            response_msg += "
"
            response_msg += "<h1>----file not found----</h1>"
            new_socket.send(response_msg.encode("utf-8"))
        else:
            html_content = fp.read()
            fp.close()
            # 响应正确 200 OK
            response_msg = "HTTP/1.1 200 OK
"
            response_msg += "
"

            # 返回响应头
            new_socket.send(response_msg.encode("utf-8"))
            # 返回响应体
            new_socket.send(html_content)

        # 关闭该次socket连接
        new_socket.close()

    # 开始无限循环,接受请求
    def run_forever(self):
        while True:
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 启动一个子进程来处理客户端的请求
            sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
            sub_p.start()
            # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
            new_socket.close()

        # 关闭整个SOCKET
        tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

在构造函数内创建监听socket实例,然后使用run_forever开始运行。

三、静态资源和动态资源

静态资源:例如html页面、css文件、js文件、图片等都属于静态资源。

动态资源:每次请求返回的数据都不一样,例如从数据库中获取的数据,或者变化的时间等,都叫动态资源。


如下图所示:

[Python之路] 实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)第1张

解释:

1.当服务器收到请求,判断请求内容为xxx.html、xxxx.css、xxxx.js、xxxx.png等,则直接从磁盘获取静态文件,读取并返回。

2.当服务器收到请求,判断请求内容为xxx.py(或其他自定义的特殊形式),则会调用web框架中的函数来获取数据。

3.HTTP服务器除了返回静态数据,以及从web框架获取reponse header和reponse body,将两者组装起来返回给客户端,他不做其他事情。

四、给HTTP服务器加上处理动态请求的逻辑

import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 创建socket实例
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置资源重用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定IP端口
        self.tcp_server_socket.bind(("", 7890))
        # 开始监听
        self.tcp_server_socket.listen(128)

    # 请求处理函数
    def handle_request(self, new_socket):
        # 接收请求
        recv_msg = ""
        recv_msg = new_socket.recv(1024).decode("utf-8")
        if recv_msg == "":
            print("recv null")
            new_socket.close()
            return

        # 从请求中解析出URI
        recv_lines = recv_msg.splitlines()
        # 使用正则表达式提取出URI
        ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
        if ret:
            # 获取URI字符串
            file_name = ret.group(1)
            # 如果URI是/,则默认返回index.html的内容
            if file_name == "/":
                file_name = "/index.html"

        if not file_name.endswith(".py"):
            try:
                # 根据请求的URI,读取相应的文件
                fp = open("." + file_name, "rb")
            except:
                # 找不到文件,响应404
                response_msg = "HTTP/1.1 404 NOT FOUND
"
                response_msg += "
"
                response_msg += "<h1>----file not found----</h1>"
                new_socket.send(response_msg.encode("utf-8"))
            else:
                html_content = fp.read()
                fp.close()
                # 响应正确 200 OK
                response_msg = "HTTP/1.1 200 OK
"
                response_msg += "
"

                # 返回响应头
                new_socket.send(response_msg.encode("utf-8"))
                # 返回响应体
                new_socket.send(html_content)
        else:
            header = "HTTP/1.1 200 OK
"
            header += "
"
            body = "dynamic request" + "  %s" % time.ctime()
            response = header + body
            new_socket.send(response.encode("utf-8"))

        # 关闭该次socket连接
        new_socket.close()

    # 开始无限循环,接受请求
    def run_forever(self):
        while True:
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 启动一个子进程来处理客户端的请求
            sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
            sub_p.start()
            # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
            new_socket.close()

        # 关闭整个SOCKET
        tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

添加判断分支,如果请求内容以.py结尾,则判断为动态数据,返回dynamic request + 时间。如下图:

[Python之路] 实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)第2张

五、服务器与动态请求解耦

另外实现一个py模块,叫mini_frame.py

import time


def login():
    return "----login page----
 %s" % time.ctime()


def register():
    return "----register page----
 %s" % time.ctime()


def application(file_name):
    if file_name == "/login.py":
        return login()
    elif file_name == "register.py":
        return register()
    else:
        return "Not Found You Page..."

服务器一旦判断收到的请求为动态数据请求,则调用application(file_name),并将请求交给mini_frame来处理。

服务器端代码:

import socket
import re
import multiprocessing
import time

import mini_frame


class WSGIServer(object):
    def __init__(self):
        # 创建socket实例
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置资源重用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定IP端口
        self.tcp_server_socket.bind(("", 7890))
        # 开始监听
        self.tcp_server_socket.listen(128)

    # 请求处理函数
    def handle_request(self, new_socket):
        # 接收请求
        recv_msg = ""
        recv_msg = new_socket.recv(1024).decode("utf-8")
        if recv_msg == "":
            print("recv null")
            new_socket.close()
            return

        # 从请求中解析出URI
        recv_lines = recv_msg.splitlines()
        # 使用正则表达式提取出URI
        ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
        if ret:
            # 获取URI字符串
            file_name = ret.group(1)
            # 如果URI是/,则默认返回index.html的内容
            if file_name == "/":
                file_name = "/index.html"

        if not file_name.endswith(".py"):
            try:
                # 根据请求的URI,读取相应的文件
                fp = open("." + file_name, "rb")
            except:
                # 找不到文件,响应404
                response_msg = "HTTP/1.1 404 NOT FOUND
"
                response_msg += "
"
                response_msg += "<h1>----file not found----</h1>"
                new_socket.send(response_msg.encode("utf-8"))
            else:
                html_content = fp.read()
                fp.close()
                # 响应正确 200 OK
                response_msg = "HTTP/1.1 200 OK
"
                response_msg += "
"

                # 返回响应头
                new_socket.send(response_msg.encode("utf-8"))
                # 返回响应体
                new_socket.send(html_content)
        else:
            header = "HTTP/1.1 200 OK
"
            header += "
"
            body = mini_frame.application(file_name)
            response = header + body
            new_socket.send(response.encode("utf-8"))

        # 关闭该次socket连接
        new_socket.close()

    # 开始无限循环,接受请求
    def run_forever(self):
        while True:
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 启动一个子进程来处理客户端的请求
            sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
            sub_p.start()
            # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
            new_socket.close()

        # 关闭整个SOCKET
        tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

服务器只需调用mini_frame.application(file_name),获取body数据即可。

六、WSGI介绍

[Python之路] 实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)第3张

如图,WSGI主要有以下流程:

1.服务器默认调用WEB 框架提供的application函数,参数为env和func_name,env是一个字典,用于存放请求中的一些数据,例如"/login.py"等,func_name是函数引用,这个函数的服务器WSGIServer类的一个成员函数,用于提供给application调用,从而将response header先返回给服务器,并保存在self.header中。

2.Web框架再调用func_name指向的函数之后,再准备response body数据,并通过return返回给服务器。

3.服务器将self.header和body数据组合起来,返回给客户端,完成一次完整的请求流程。

七、WSGI标准接口

def application(environ, start_response):
    start_response('200 OK',[('Content-Type','text/html')])
    return 'Hello World...'

上述代码是一个最简单符合WSGI标准的HTTP处理函数,接收两个参数,environ是包含所有HTTP请求信息的dict对象,start_response提供给web框架调用的函数。

将我们的nimi_frame框架代码修改一下:

def application(env, start_response):
    start_response('200 OK', [('Content-Tpye', 'text/html')])
    return "Hello World.."

将服务器代码修改一下:

import socket
import re
import multiprocessing
import time

import mini_frame


class WSGIServer(object):
    def __init__(self):
        self.headers = list()
        self.status = ""
        # 创建socket实例
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置资源重用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定IP端口
        self.tcp_server_socket.bind(("", 7890))
        # 开始监听
        self.tcp_server_socket.listen(128)

    # 请求处理函数
    def handle_request(self, new_socket):
        # 接收请求
        recv_msg = ""
        recv_msg = new_socket.recv(1024).decode("utf-8")
        if recv_msg == "":
            print("recv null")
            new_socket.close()
            return

        # 从请求中解析出URI
        recv_lines = recv_msg.splitlines()
        # 使用正则表达式提取出URI
        ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
        if ret:
            # 获取URI字符串
            file_name = ret.group(1)
            # 如果URI是/,则默认返回index.html的内容
            if file_name == "/":
                file_name = "/index.html"

        if not file_name.endswith(".py"):
            try:
                # 根据请求的URI,读取相应的文件
                fp = open("." + file_name, "rb")
            except:
                # 找不到文件,响应404
                response_msg = "HTTP/1.1 404 NOT FOUND
"
                response_msg += "
"
                response_msg += "<h1>----file not found----</h1>"
                new_socket.send(response_msg.encode("utf-8"))
            else:
                html_content = fp.read()
                fp.close()
                # 响应正确 200 OK
                response_msg = "HTTP/1.1 200 OK
"
                response_msg += "
"

                # 返回响应头
                new_socket.send(response_msg.encode("utf-8"))
                # 返回响应体
                new_socket.send(html_content)
        else:
            env = dict()
            body = mini_frame.application(env, self.set_response_header)

            # 将框架返回的status组合进response header中
            header = "HTTP/1.1 %s
" % self.status
            # 将框架返回的响应头键值对加入到header中
            for temp in self.headers:
                header += "%s:%s
" % (temp[0], temp[1])
            # 最后加上一个分割行
            header += "
"

            # 将响应体数据加在header后面
            response = header + body
            # 返回给客户端
            new_socket.send(response.encode("utf-8"))

        # 关闭该次socket连接
        new_socket.close()

    # 该成员函数提供给web框架的applicaiton函数调用,并将status,headers设置到服务器中
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    # 开始无限循环,接受请求
    def run_forever(self):
        while True:
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 启动一个子进程来处理客户端的请求
            sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
            sub_p.start()
            # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
            new_socket.close()

        # 关闭整个SOCKET
        tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

解释:

1.代码的开头导入了mini_frame模块,这样导入显然是不太合理的,因为我们使用开源的服务器,不可能直接导入我们的框架代码。我们在后续版本中会解决这个问题。

2.动态请求的逻辑代码中,服务器调用mini_frame.application,这里传递了一个空的env字典(后续版本会将请求信息通过env传递给mini_frame)。

3.服务器定义了一个成员函数,set_response_header(),并将其引用传递给了application函

4.在mini_frame.application中,框架通过set_response_header()函数向服务器返回了response header(分为status和headers列表)。

5.服务器通过拼接self.status和self.headers,得到真正的response header。

6.application返回了一个固定的字符串(Hello World..这个字符串模拟response body),并将其串接在response header后,形成最终的响应数据。

八、通过environ字典传递请求头信息给web框架

mini_frame代码修改为:

import time


def login():
    return "----login page----
 %s" % time.ctime()


def register():
    return "----register page----
 %s" % time.ctime()


def application(env, start_response):
    file_name = env['PATH_INFO']
    if file_name == '/login.py':
        start_response('200 OK', [('Content-Tpye', 'text/html')])
        return login()
    elif file_name == '/register.py':
        start_response('200 OK', [('Content-Tpye', 'text/html')])
        return register()
    else:
        start_response('404 NOT FOUND', [])
        return "Not found page..."

通过env来获取服务器传递过来的请求头信息,然后返回响应的动态内容。如果没有对应数据,则返回404。

服务器代码修改为:

import socket
import re
import multiprocessing
import time

import mini_frame


class WSGIServer(object):
    def __init__(self):
        self.headers = list()
        self.status = ""
        # 创建socket实例
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置资源重用
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定IP端口
        self.tcp_server_socket.bind(("", 7890))
        # 开始监听
        self.tcp_server_socket.listen(128)

    # 请求处理函数
    def handle_request(self, new_socket):
        # 接收请求
        recv_msg = ""
        recv_msg = new_socket.recv(1024).decode("utf-8")
        if recv_msg == "":
            print("recv null")
            new_socket.close()
            return

        # 从请求中解析出URI
        recv_lines = recv_msg.splitlines()
        # 使用正则表达式提取出URI
        ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])
        if ret:
            # 获取URI字符串
            file_name = ret.group(1)
            # 如果URI是/,则默认返回index.html的内容
            if file_name == "/":
                file_name = "/index.html"

        if not file_name.endswith(".py"):
            try:
                # 根据请求的URI,读取相应的文件
                fp = open("." + file_name, "rb")
            except:
                # 找不到文件,响应404
                response_msg = "HTTP/1.1 404 NOT FOUND
"
                response_msg += "
"
                response_msg += "<h1>----file not found----</h1>"
                new_socket.send(response_msg.encode("utf-8"))
            else:
                html_content = fp.read()
                fp.close()
                # 响应正确 200 OK
                response_msg = "HTTP/1.1 200 OK
"
                response_msg += "
"

                # 返回响应头
                new_socket.send(response_msg.encode("utf-8"))
                # 返回响应体
                new_socket.send(html_content)
        else:
            env = dict()
            # 将请求的内容添加到字典中,并传递给application
            env['PATH_INFO'] = file_name
            body = mini_frame.application(env, self.set_response_header)

            # 将框架返回的status组合进response header中
            header = "HTTP/1.1 %s
" % self.status
            # 将框架返回的响应头键值对加入到header中
            for temp in self.headers:
                header += "%s:%s
" % (temp[0], temp[1])
            # 最后加上一个分割行
            header += "
"

            # 将响应体数据加在header后面
            response = header + body
            # 返回给客户端
            new_socket.send(response.encode("utf-8"))

        # 关闭该次socket连接
        new_socket.close()

    # 该成员函数提供给web框架的applicaiton函数调用,并将status,headers设置到服务器中
    def set_response_header(self, status, headers):
        self.status = status
        # 如果要在响应头中添加服务器信息,一定是在服务器代码中加,而不是web框架代码中加
        self.headers = [("server", "mini_server v1.0")]
        self.headers += headers

    # 开始无限循环,接受请求
    def run_forever(self):
        while True:
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 启动一个子进程来处理客户端的请求
            sub_p = multiprocessing.Process(target=self.handle_request, args=(new_socket,))
            sub_p.start()
            # 这里要关闭父进程中的new_socket,因为创建子进程会复制一份new_socket给子进程
            new_socket.close()

        # 关闭整个SOCKET
        tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()

解释:

1.将请求内容,例如/login.py、/register.py放到env字典中,传递给application。

2.接收到来自Web框架的response header后,如果想要加入服务器信息,则服务器代码中自己添加,这样才符合解耦的思想。

九、如何安排项目文件

目前我们的代码有以下几部分:

1.mini_web_server部分

2.mini_frame部分

3.静态资源部分,例如css、js、图片等

4.html页面,称之为模板(template)

我们将这些文件重新分类放置:

[Python之路] 实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)第4张

解释:

1.将mini_frame放到dynamic中,因为web框架用于处理动态数据请求。

2.将css、js、图片等静态资源分类放到static中。

3.将html页面放到templates文件夹中。

4.web服务器程序在根目录下。

注意:文件放置的目录变化了,代码中响应的代码也需要改变,例如读取index.html,则需要修改为templates/index.html。

十、让web框架返回页面数据

当我们请求http://192.168.1.8:7890/index.py时,注意这里不是index.html。

修改mini_frame代码:

import time


def index():
    with open("templates/index.html", 'rb') as f:
        content = f.read()
    return content.decode("utf-8")


def login():
    return "----login page----
 %s" % time.ctime()


def register():
    return "----register page----
 %s" % time.ctime()


def application(env, start_response):
    file_name = env['PATH_INFO']
    if file_name == '/login.py':
        start_response('200 OK', [('Content-Tpye', 'text/html')])
        return login()
    elif file_name == '/register.py':
        start_response('200 OK', [('Content-Tpye', 'text/html')])
        return register()
    elif file_name == '/index.py':
        start_response('200 OK', [('Content-Tpye', 'text/html')])
        return index()
    else:
        start_response('404 NOT FOUND', [])
        return "Not found page..."

服务器代码无需进行修改。这里相当于使用动态请求的方式index.py,来请求了index.html。

十一、总结

不管我们使用index.py的方式还是index.html的方式访问服务器,服务器都能够正确的返回index.html的内容给客户端。

当index.html中需求请求其他资源时(例如图片,css样式等),则浏览器会自动发起新的请求,此时服务器同样按照既定的方式才处理就可以了。

十二、最后完善一些东西

1.将一些信息使用配置文件来提供给服务器

例如静态资源路径、动态资源路径、绑定端口、web框架路径等:

创建一个配置文件web_server.conf:

{
    "static_path":"./static"
    "dynamic_path":"./dynamic"
    "bind_port":7890
    "app_path":"mini_frame:application"
}

在服务器代码中,可以通过添加以下代码来读取配置文件信息(在WSGIServer的__init__中添加即可):

with open("./web_server.conf") as f:
    self.conf_info = eval(f.read())

self.static_path = self.conf_info['static_path']
self.dynamic_path = self.conf_info['dynamic_path']
self.bind_port = self.conf_info['bind_port']
self.app_module = self.conf_info['app_module']
self.app_name = self.conf_info["app_name"]

# 导入mini_frame.application
import sys
sys.path.append(self.dynamic_path)
frame = __import__(self.app_module)
self.app = getattr(frame, self.app_name)

解释:

1.读取配置文件,并将其转换为字典。

2.获取字典中的每个配置项。

3.将dynamic_path加入python环境变量,然后使用__import__导入mini_frame。

4.使用getattr获取mini_frame模块中的application函数的引用。

5.其他的配置项,诸如static_path、bind_port,则将其替换到响应的位置即可。

2.创建一个快速启动的shell脚本

创建一个run.sh

python3 web_server.py

修改运行权限:

chmod +x run.sh

免责声明:文章转载自《[Python之路] 实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用LogParser分析日志【转】如何修改 video 样式下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

python元类深入解析

元类 什么是元类 元类是类的类,是类的模板(就如对象的模板是类一样) 元类的实例为类,类的实例为对象 元类是用来产生类的 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,是运行时动态创建的 __new__() 我们之前说类实例化第一个调用的是__init__,但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Pe...

《图解 HTTP》 摘要一

学习过程对书本的内容的摘要以及总结,逐步完善,带有个人理解成分。 Web 及网络基础 使用 HTTP 协议访问 Web 客户端:通过获取请求获取服务资源的 Web 浏览器等 HTTP 全称:HtyperText Transfer Protocol WWW 全称:Wrold Wide Web SGML 标准通用标记语言 全称:Standard Gener...

网络协议和管理

网络的特征 速度 成本 安全性 可用性 可扩展性 可靠性 拓扑 物理拓扑分类 OSI七层模型(OSI System Interconnection) 物理层:二进制传输 数据链路层:访问介质 网络层:数据传输 传输层:端到端连接 会话层:主机间通信 表示层:数据表示 应用层:网络进程访问应用层...

Vscode + Python + Django开发环境常见问题

Vscode 越来越好用了,轻量还跨平台。 1, 主程序安装。 2. 汉化:左侧工具条的扩展菜单,搜索 chinese 第一个就是汉化包 ,安装后重启。 3. 安装Python扩展:还是扩展菜单,搜索python 一般第一个就是, 注释为:Linting,Debugging (multi-threaded, ..... 4. 我用过Kite 自动补全,还不...

康少带你python项目从部署到上线云服务器

首先,服务器要安装nginx和mysql,网站文件建议放在/usr/local/www,环境python3.6+mysql5.7,阿里云的服务器可以用公共镜像有一个配置好的,不然就自己装一下环境吧。 本项目名称:loginOrRegister 对于小白来说首先我们需要干的一件事情就是安置与配置nginx下面就是nginx的配置 1.nginx配置 安装ng...

Python——继承

Python的继承是多继承机制,一个子类可以同时有多个直接父类;继承可以得到父类定义的方法,子类就可以复用父类的方法。 一、继承的语法 子类:实现继承的类。 父类(基类、超类):被继承的类。 子类继承父类是在定义子类时,将多个父类放在子类之后的圆括号内,如果定义类时,未指定这个类的直接父类,则默认继承object类,所以object类是所有类的父类(直接父...