• Web 框架本质解析


    一  Web框架本质

    1. 自己开发Web框架
        - socket
        - http协议
        - HTML知识
        - 数据库(pymysql,SQLAlchemy)
     
    HTTP:
        无状态、短连接
         
    TCP:
        不断开
         
    WEB应用(网站):
        Http协议:
            发送:
                POST /index HTTP/1.1
                Host: 127.0.0.1:8080
                Connection: keep-alive
                Cache-Control: max-age=0
                Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
                User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36
                HTTPS: 1
                Accept-Encoding: gzip, deflate, sdch
                Accept-Language: zh-CN,zh;q=0.8
                Cookie: csrftoken=hNmu2JOtntGMN0hSRSPmMQk2newEb3o8zb6pXW5Cc3m54IaA5VlTkUvqWsFezpni
     
     
                p=123
             
            响应:
                200 OK
                Cache-Control:public, max-age=15
                Connection:keep-alive
                Content-Encoding:gzip
                Content-Type:text/html; charset=utf-8
                Date:Wed, 14 Jun 2017 01:21:17 GMT
                Expires:Wed, 14 Jun 2017 01:21:33 GMT
                Last-Modified:Wed, 14 Jun 2017 01:21:03 GMT
                Transfer-Encoding:chunked
                Vary:Accept-Encoding
                X-Frame-Options:SAMEORIGIN
                X-UA-Compatible:IE=10
                         
                 
                 
                用户在页面看到的内容“字符串”(看到页面效果,由于浏览器解析)
                 
     
        浏览器(socket客户端)
            2. www.cnblogs.com(42.121.252.58,80)
                sk.socket()
                sk.connect((42.121.252.58,80))
                 
                sk.send('我想要xx')
            5. 接收
            6. 连接断开
             
             
             
        博客园(socket服务端)
            1. 监听ip和端口(42.121.252.58,80while True:
                    用户 = 等待用户连接
                    3. 收到'我想要xx'
                    4. 响应:“好”
                    用户断开
         
         
     
        import socket
     
        sock = socket.socket()
        sock.bind(('127.0.0.1',8080))
        sock.listen(5)
     
        while True:
            conn,addr = sock.accept() # hang住
            # 有人来连接了
            # 获取用户发送的数据
            data = conn.recv(8096)
            conn.send(b"HTTP/1.1 200 OK
    
    ")
            conn.send(b'123123')
            conn.close()
     
         
         
        1. Http,无状态,短连接
        2.
            浏览器(socket客户端)
            网站(socket服务端)
             
        3. 自己写网站
            a. socket服务端
            b. 根据URL不同返回不同的内容
                路由系统:
                    URL -> 函数
            c. 字符串返回给用户
                模板引擎渲染:
                    HTML充当模板(特殊字符)
                    自己创造任意数据
                字符串
                 
        4. Web框架:
            框架种类:
                - a,b,c                  --> Tornado
                - [第三方a],b,c          --> wsgiref -> Django
                - [第三方a],b,[第三方c]  --> flask,
                 
            分类:
                - Django框架(Web。。。。。。)
                - 其他
    web 框架本质

    众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。

    import socket
       
    def handle_request(client):
        buf = client.recv(1024)
        client.send("HTTP/1.1 200 OK
    
    ")
        client.send("Hello, Seven")
       
    def main():
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost',8000))
        sock.listen(5)
       
        while True:
            connection, address = sock.accept()
            handle_request(connection)
            connection.close()
       
    if __name__ == '__main__':
        main()

    上述通过socket来实现了其本质,而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序应用程序

    服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。

    应用程序则负责具体的逻辑处理。

    为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。

    不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。

    这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。

    我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。

    一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

    WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。

    python标准库提供的独立WSGI服务器称为wsgiref。

    from wsgiref.simple_server import make_server
      
      
    def RunServer(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
      
      
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print("Serving HTTP on port 8000...")
        httpd.serve_forever()

    二  自定义Web框架

    框架

    通过python标准库提供的wsgiref模块开发一个自己的Web框架

    #!/usr/bin/env python
    #coding:utf-8
    from wsgiref.simple_server import make_server
      
    def index():
        return 'index'
      
    def login():
        return 'login'
      
    def routers():
          
        urlpatterns = (
            ('/index/',index),
            ('/login/',login),
        )
          
        return urlpatterns
      
    def RunServer(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        url = environ['PATH_INFO']
        urlpatterns = routers()
        func = None
        for item in urlpatterns:
            if item[0] == url:
                func = item[1]
                break
        if func:
            return func()
        else:
            return '404 not found'
          
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()

    模板引擎

    在上一步骤中,对于所有的login、index均返回给用户浏览器一个简单的字符串,在现实的Web请求中一般会返回一个复杂的符合HTML规则的字符串,所以我们一般将要返回给用户的HTML写在指定文件中,然后再返回。如:

    index.html

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>Index</h1>
     
    </body>
    </html>

    login.html

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <form>
            <input type="text" />
            <input type="text" />
            <input type="submit" />
        </form>
    </body>
    </html>
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
      
    from wsgiref.simple_server import make_server
      
      
    def index():
        # return 'index'
        f = open('index.html')
        data = f.read()
        return data
      
      
    def login():
        # return 'login'
        f = open('login.html')
        data = f.read()
        return data
      
      
    def routers():
      
        urlpatterns = (
            ('/index/', index),
            ('/login/', login),
        )
      
        return urlpatterns
      
      
    def run_server(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        url = environ['PATH_INFO']
        urlpatterns = routers()
        func = None
        for item in urlpatterns:
            if item[0] == url:
                func = item[1]
                break
        if func:
            return func()
        else:
            return '404 not found'
      
      
    if __name__ == '__main__':
        httpd = make_server('', 8000, run_server)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()

    对于上述代码,虽然可以返回给用户HTML的内容以现实复杂的页面,但是还是存在问题:如何给用户返回动态内容?

    • 1)自定义一套特殊的语法,进行替换
    • 2)使用开源工具jinja2,遵循其指定语法

    index.html

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>{{name}}</h1>
     
        <ul>
            {% for item in user_list %}
            <li>{{item}}</li>
            {% endfor %}
        </ul>
     
    </body>
    </html>
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
      
    from wsgiref.simple_server import make_server
    from jinja2 import Template
      
      
    def index():
        # return 'index'
      
        # template = Template('Hello {{ name }}!')
        # result = template.render(name='John Doe')
      
        f = open('index.html')
        result = f.read()
        template = Template(result)
        data = template.render(name='John Doe', user_list=['alex', 'eric'])
        return data.encode('utf-8')
      
      
    def login():
        # return 'login'
        f = open('login.html')
        data = f.read()
        return data
      
      
    def routers():
      
        urlpatterns = (
            ('/index/', index),
            ('/login/', login),
        )
      
        return urlpatterns
      
      
    def run_server(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        url = environ['PATH_INFO']
        urlpatterns = routers()
        func = None
        for item in urlpatterns:
            if item[0] == url:
                func = item[1]
                break
        if func:
            return func()
        else:
            return '404 not found'
      
      
    if __name__ == '__main__':
        httpd = make_server('', 8000, run_server)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()

    遵循jinja2的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容,对于模板引擎的本质

    三  自己开发web框架

    实现静态网站

    import socket
    
    def f1(request):
        """
        处理用户请求,并返回相应的内容
        :param request: 用户请求的所有信息
        :return:
        """
        f = open('index.fsw','rb')
        data = f.read()
        f.close()
        return data
    
    def f2(request):
        f = open('aricle.tpl','r',encoding='utf-8')
        data = f.read()
        f.close()
        import time
        ctime = time.time()
        data = data.replace('@@sw@@',str(ctime))
        return bytes(data,encoding='utf-8')
    
    def f3(request):
        import pymysql
    
        # 创建连接
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,username,password from userinfo")
        user_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        content_list = []
        for row in user_list:
            tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password'])
            content_list.append(tp)
        content = "".join(content_list)
    
    
        f = open('userlist.html','r',encoding='utf-8')
        template = f.read()
        f.close()
    
        # 模板渲染(模板+数据)
        data = template.replace('@@sdfsdffd@@',content)
        return bytes(data,encoding='utf-8')
    
    def f4(request):
        import pymysql
    
        # 创建连接
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,username,password from userinfo")
        user_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        f = open('hostlist.html','r',encoding='utf-8')
        data = f.read()
        f.close()
    
        # 基于第三方工具实现的模板渲染
        from jinja2 import Template
        template = Template(data)
        data = template.render(xxxxx=user_list,user='sdfsdfsdf')
        return data.encode('utf-8')
    
    
    routers = [
        ('/xxx', f1),
        ('/ooo', f2),
        ('/userlist.htm', f3),
        ('/host.html', f4),
    ]
    
    
    def run():
        sock = socket.socket()
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('127.0.0.1',8080))
        sock.listen(5)
    
        while True:
            conn,addr = sock.accept() # hang住
            # 有人来连接了
            # 获取用户发送的数据
            data = conn.recv(8096)
            data = str(data,encoding='utf-8')
            headers,bodys = data.split('
    
    ')
            temp_list = headers.split('
    ')
            method,url,protocal = temp_list[0].split(' ')
            conn.send(b"HTTP/1.1 200 OK
    
    ")
    
            func_name = None
            for item in routers:
                if item[0] == url:
                    func_name = item[1]
                    break
    
            if func_name:
                response = func_name(data)
            else:
                response = b"404"
    
            conn.send(response)
            conn.close()
    
    if __name__ == '__main__':
        run()
    s1.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>用户登录</h1>
        <form>
            <p><input type="text" placeholder="用户名" /></p>
            <p><input type="password" placeholder="密码" /></p>
        </form>
    </body>
    </html>
    
    index.fsw
    index.fsw
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>邮箱</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th>1</th>
                    <th>root</th>
                    <th>root@qq.com</th>
                </tr>
            </tbody>
        </table>
    </body>
    </html>
    
    aricle.tpl
    aricle.tpl

    实现动态网站

    import socket
    
    def f1(request):
        """
        处理用户请求,并返回相应的内容
        :param request: 用户请求的所有信息
        :return:
        """
        f = open('index.fsw','rb')
        data = f.read()
        f.close()
        return data
    
    def f2(request):
        f = open('aricle.tpl','r',encoding='utf-8')
        data = f.read()
        f.close()
        import time
        ctime = time.time()
        data = data.replace('@@sw@@',str(ctime))
        return bytes(data,encoding='utf-8')
    
    def f3(request):
        import pymysql
    
        # 创建连接
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,username,password from userinfo")
        user_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        content_list = []
        for row in user_list:
            tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password'])
            content_list.append(tp)
        content = "".join(content_list)
    
    
        f = open('userlist.html','r',encoding='utf-8')
        template = f.read()
        f.close()
    
        # 模板渲染(模板+数据)
        data = template.replace('@@sdfsdffd@@',content)
        return bytes(data,encoding='utf-8')
    
    def f4(request):
        import pymysql
    
        # 创建连接
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,username,password from userinfo")
        user_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        f = open('hostlist.html','r',encoding='utf-8')
        data = f.read()
        f.close()
    
        # 基于第三方工具实现的模板渲染
        from jinja2 import Template
        template = Template(data)
        data = template.render(xxxxx=user_list,user='sdfsdfsdf')
        return data.encode('utf-8')
    
    
    routers = [
        ('/xxx', f1),
        ('/ooo', f2),
        ('/userlist.htm', f3),
        ('/host.html', f4),
    ]
    
    
    def run():
        sock = socket.socket()
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('127.0.0.1',8080))
        sock.listen(5)
    
        while True:
            conn,addr = sock.accept() # hang住
            # 有人来连接了
            # 获取用户发送的数据
            data = conn.recv(8096)
            data = str(data,encoding='utf-8')
            headers,bodys = data.split('
    
    ')
            temp_list = headers.split('
    ')
            method,url,protocal = temp_list[0].split(' ')
            conn.send(b"HTTP/1.1 200 OK
    
    ")
    
            func_name = None
            for item in routers:
                if item[0] == url:
                    func_name = item[1]
                    break
    
            if func_name:
                response = func_name(data)
            else:
                response = b"404"
    
            conn.send(response)
            conn.close()
    
    if __name__ == '__main__':
        run()
    
    s1.py
    s1.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>用户登录</h1>
        <form>
            <p><input type="text" placeholder="用户名" /></p>
            <p><input type="password" placeholder="密码" /></p>
        </form>
    </body>
    </html>
    
    index.fsw
    index.fsw
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>邮箱</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th>1</th>
                    <th>@@sw@@</th>
                    <th>root@qq.com</th>
                </tr>
            </tbody>
        </table>
    </body>
    </html>
    
    aricle.tpl
    aricle.tpl
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>邮箱</th>
                </tr>
            </thead>
            <tbody>
                {% for row in xxxxx %}
                    <tr>
                        <td>{{row.id}}</td>
                        <td>{{row.username}}</td>
                        <td>{{row.password}}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
        {{user}}
    </body>
    </html>
    
    userlist.html
    userlist.htmlWeb
  • 相关阅读:
    矩阵问题
    链表问题
    PHP8确认支持JIT,加上Swoole后快到飞起
    五面阿里,顺利拿下PHP研发岗offer,这些技术点你未必掌握了
    php-laravel框架用户验证(Auth)模块解析(一)
    php laravel请求处理管道(装饰者模式)
    Redis常见七种使用场景(PHP实战)
    PHP fastcgi_finish_request 方法
    Linux 下编写一个 PHP 扩展
    创 PHP RSA2 签名算法
  • 原文地址:https://www.cnblogs.com/bigtreei/p/8366349.html
Copyright © 2020-2023  润新知