• Web框架的引入


    为什么会有web框架

    有了上一篇内容,静态、动态web服务器的实现,已经掌握了客户端请求到服务器处理的机制。在动态资源处理中,根据请求 .py 导入模块应用,然后调用应用入口程序实现动态处理。但是在真实的项目中,肯定有很多应用(.py文件),按照这种处理机制(每个应用中都要写一个入口,设置状态码、headers)不符合架构设计原则,这一部分肯定是可以复用的,所用应用应该公用一个入口,这就引入一个概念web框架。

    设计思路

    • 把每个应用中入口 application(env,start_response) 提取到web服务器中,然后在入口中通过路由机制分发到各应用中处理。

    代码如下:

    - WebFramework.py

    `
    # coding:utf-8
    
    import time
    from MyWebServer import WebServer
    
    HTML_ROOT_DIR = "./static"
    
    class Application(object):
        '''自定义通用的web框架'''
    
        # 初始化路由信息
        def __init__(self,urls):
            self.urls = urls
    
        # 匹配路由
        def __call__(self, env, start_response):
            path = env.get("PATH_INFO", "/")
            # /static/index.html
            if path.startswith("/static"):
                file_name = path[7:]
                # 打开文件,读取内容
                try:
                    file = open(HTML_ROOT_DIR + file_name, "rb")
                except IOError:
                    # 代表未找到路由信息,404错误
                    status = "404 Not Found"
                    headers = []
                    start_response(status, headers)
                    return "not found"
                else:
                    file_data = file.read()
                    file.close()
    
                    status = "200 OK"
                    headers = []
                    start_response(status, headers)
                    return file_data.decode("utf-8")
    
    
            for url,handler in self.urls:
                if path == url:
                    return handler(env,start_response)
            # 未匹配到
            status = '404 Not Found'
            headers = []
            start_response(status,headers)
            return 'not found'
    
    def showtime(env,start_response):
        status = '200 OK'
        headers = [
            ('Content-Type', 'text/plain')
        ]
        start_response(status, headers)
        return str(time.time())
    
    def sayhello(env,start_response):
        status = '200 OK'
        headers = [
            ('Content-Type','text/plain')
        ]
        start_response(status,headers)
        return 'say hello'
    
    def helloworld(env,start_response):
        status = '200 OK'
        headers =[
            ('Content-Type','text/plain')
        ]
        start_response(status,headers)
        return 'hello world'
    
    
    if __name__ == '__main__':
        urls = [
            ('/', showtime),
            ('/sayhello',sayhello),
            ('/helloworld',helloworld)
        ]
        app = Application(urls)
    
        webServer = WebServer(app)
        webServer.bind(8000)
        webServer.start()
    
    `
    

    - MyWebServer.py

    `
    # coding:utf-8
    
    import socket
    import re
    import sys
    
    from multiprocessing import Process
    
    HTML_ROOT_DIR = './static'
    WSGI_PY = './wsgipy'
    
    class WebServer(object):
        '''
        简单的webserver
        '''
    
        def __init__(self,application):
            '''application:框架'''
            self.sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.app = application
    
        def start(self):
            self.sock_server.listen(128)
            while True:
                sock_client, addr = self.sock_server.accept()
                print('[%s,%s]用户已连接......' % addr)
                handle_client_process = Process(target=self.handle_client, args=(sock_client,))
                handle_client_process.start()
                sock_client.close()
    
        def start_response(self, status, headers):
            """
                     status = "200 OK"
                headers = [
                    ("Content-Type", "text/plain")
                ]
                star
                    """
            resp_headers = 'HTTP/1.1 ' + status + '
    '
            for header in headers:
                resp_headers += '%s: %s
    ' % header
    
            self.resp_headers = resp_headers
    
        def handle_client(self, sock_client):
            '''处理客户端请求'''
            recv_data = sock_client.recv(1024)
            #print('请求数据:', recv_data)
            req_lines = recv_data.splitlines()
            #for line in req_lines:
            #    print(line)
    
            req_start_line = req_lines[0]
            #print(req_start_line.decode('utf-8'))
            file_name = re.match(r"w+ +(/[^ ]*) ", req_start_line.decode("utf-8")).group(1)
            method = re.match(r"(w+) +/[^ ]* ", req_start_line.decode("utf-8")).group(1)
    
            env = {
                "PATH_INFO": file_name,
                "METHOD": method
            }
            response_body = self.app(env, self.start_response)
    
            response = self.resp_headers + "
    " + response_body
    
            # 向客户端返回响应数据
            sock_client.send(bytes(response, "utf-8"))
    
            # 关闭客户端连接
            sock_client.close()
    
        def bind(self, port):
            self.sock_server.bind(('', port))
    
    
    def main():
        sys.path.insert(1,WSGI_PY)    
        webServer = WebServer()
        webServer.bind(8000)
        webServer.start()
    
    
    if __name__ == '__main__':
        main()
    
    `
    

    启动

    入口实在WebFramework.py 中,直接启动该文件,客户端请求指定路径即可看到效果。但是对于大多框架都是通过命令行启动,我们不可能在程序中这样启动,而且对于webserver来说始终是不变得,变的是web框架,所以改造成命令行启动,代码如下:

    - WebFramework.py

    `
    # coding:utf-8
    
    import time
    
    HTML_ROOT_DIR = "./static"
    
    class Application(object):
        '''自定义通用的web框架'''
    
        # 初始化路由信息
        def __init__(self,urls):
            self.urls = urls
    
        # 匹配路由
        def __call__(self, env, start_response):
            path = env.get("PATH_INFO", "/")
            # /static/index.html
            if path.startswith("/static"):
                file_name = path[7:]
                # 打开文件,读取内容
                try:
                    file = open(HTML_ROOT_DIR + file_name, "rb")
                except IOError:
                    # 代表未找到路由信息,404错误
                    status = "404 Not Found"
                    headers = []
                    start_response(status, headers)
                    return "not found"
                else:
                    file_data = file.read()
                    file.close()
    
                    status = "200 OK"
                    headers = []
                    start_response(status, headers)
                    return file_data.decode("utf-8")
    
    
            for url,handler in self.urls:
                if path == url:
                    return handler(env,start_response)
            # 未匹配到
            status = '404 Not Found'
            headers = []
            start_response(status,headers)
            return 'not found'
    
    def showtime(env,start_response):
        status = '200 OK'
        headers = [
            ('Content-Type', 'text/plain')
        ]
        start_response(status, headers)
        return str(time.time())
    
    def sayhello(env,start_response):
        status = '200 OK'
        headers = [
            ('Content-Type','text/plain')
        ]
        start_response(status,headers)
        return 'say hello'
    
    def helloworld(env,start_response):
        status = '200 OK'
        headers =[
            ('Content-Type','text/plain')
        ]
        start_response(status,headers)
        return 'hello world'
    
    
    urls = [
        ('/', showtime),
        ('/sayhello',sayhello),
        ('/helloworld',helloworld)
    ]
    app = Application(urls)
    # if __name__ == '__main__':
    #     urls = [
    #         ('/', showtime),
    #         ('/sayhello',sayhello),
    #         ('/helloworld',helloworld)
    #     ]
    #     app = Application(urls)
    #
    #     webServer = WebServer(app)
    #     webServer.bind(8000)
    #     webServer.start() 
    `
    

    - MyWebServer.py

    `
    # coding:utf-8
    
    import socket
    import re
    import sys
    
    from multiprocessing import Process
    
    HTML_ROOT_DIR = './static'
    WSGI_PY = './wsgipy'
    
    class WebServer(object):
        '''
        简单的webserver
        '''
    
        def __init__(self,application):
            '''application:框架'''
            self.sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.app = application
    
        def start(self):
            self.sock_server.listen(128)
            while True:
                sock_client, addr = self.sock_server.accept()
                print('[%s,%s]用户已连接......' % addr)
                handle_client_process = Process(target=self.handle_client, args=(sock_client,))
                handle_client_process.start()
                sock_client.close()
    
        def start_response(self, status, headers):
            """
                     status = "200 OK"
                headers = [
                    ("Content-Type", "text/plain")
                ]
                star
                    """
            resp_headers = 'HTTP/1.1 ' + status + '
    '
            for header in headers:
                resp_headers += '%s: %s
    ' % header
    
            self.resp_headers = resp_headers
    
        def handle_client(self, sock_client):
            '''处理客户端请求'''
            recv_data = sock_client.recv(1024)
            #print('请求数据:', recv_data)
            req_lines = recv_data.splitlines()
            #for line in req_lines:
            #    print(line)
    
            req_start_line = req_lines[0]
            #print(req_start_line.decode('utf-8'))
            file_name = re.match(r"w+ +(/[^ ]*) ", req_start_line.decode("utf-8")).group(1)
            method = re.match(r"(w+) +/[^ ]* ", req_start_line.decode("utf-8")).group(1)
    
            env = {
                "PATH_INFO": file_name,
                "METHOD": method
            }
            response_body = self.app(env, self.start_response)
    
            response = self.resp_headers + "
    " + response_body
    
            # 向客户端返回响应数据
            sock_client.send(bytes(response, "utf-8"))
    
            # 关闭客户端连接
            sock_client.close()
    
        def bind(self, port):
            self.sock_server.bind(('', port))
    
    
    def main():
        sys.path.insert(1,WSGI_PY)
        if len(sys.argv) < 2:
            sys.exit("python MyWebServer.py Module:app")
        module_name, app_name = sys.argv[1].split(':')
        m = __import__(module_name)
        app = getattr(m,app_name)
        webServer = WebServer(app)
        webServer.bind(8000)
        webServer.start()
    
    
    if __name__ == '__main__':
        main()
    `
    

    调用方式

    在命令行中,执行如下命令即可启动webserver服务,模块名称:应用框架名, 其中这里的app指的是WebFramework.py中Application类的实例,其实也就是应用框架的实例。后续自定义框架的时候,直接调用即可。

    `
    python MyWebServer.py WebFramework:app  
    `
    

    代码已上传到github:python 小程序

  • 相关阅读:
    python -- 初始函数 函数的定义,函数的返回值以及函数的参数
    python 文件操作: 文件操作的函数, 模式及常用操作.
    第三节 深入JavaScript
    第二节 JavaScript基础
    第一节 JavaScript概述
    面试大纲
    flask
    面试准备
    数据结构与算法 学习
    Linux学习
  • 原文地址:https://www.cnblogs.com/tianboblog/p/7296045.html
Copyright © 2020-2023  润新知