zoukankan      html  css  js  c++  java
  • doc

    一、Web框架本质

    Python的Web框架分为两类:

    • 通过socket自己写程序,自己处理请求;

    • 基于Wsgi(Web Server Gateway Interface:Web服务网关接口),处理请求。

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

    1、Socket实现Web框架的本质

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #!/usr/bin/env python
    #coding:utf-8
      
    importsocket
      
    def handle_request(client):
        #接收请求
        buf =client.recv(1024)
        #返回信息
        client.send("HTTP/1.1 200 OK ")
        client.send("Hello, Tim")
      
    def main():
        #创建sock对象
        sock =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #监听80端口
        sock.bind(('localhost',8000))
        #最大允许排队的客户端
        sock.listen(5)
      
        #循环
        whileTrue:
            #等待用户的连接,默认accept阻塞当有请求的时候往下执行
            connection, address =sock.accept()
            #把连接交给handle_request函数
            handle_request(connection)
            #关闭连接
            connection.close()
      
    if__name__ =='__main__':
        main()

        上述通过socket来实现了其本质,而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。

        不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

    2、WSGI

        WSGI是一种规范,它定义了使用Python编写web app与web server之间接口格式,实现web app与web server 间的解耦。

    Python标准库提供的独立WSGI服务器为wsgiref,下来例子为Python3版本。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
     
    from wsgiref.util import setup_testing_defaults
    from wsgiref.simple_server import make_server
     
     
    def simple_app(environ, start_response):
        setup_testing_defaults(environ)
     
        status = '200 OK'
        headers = [('Content-type', 'text/plain')]
     
        start_response(status, headers)
     
        ret = ["%s: %s " % (key, value)
               for key, value in environ.iteritems()]
        return ret
     
     
    if __name__ == '__main__':
        httpd = make_server('', 8000, simple_app)
        print "Serving on port 8000..."
        httpd.serve_forever()

    下面看WEB框架图:

    基于Python的Web框架有以下常用几种:

    • Tornado:使用自己的异步非阻塞的WSGI;

    • Flask:只提供了最精简和基本的框架,轻量级的;

    • Django:直接使用了WSGI,并实现了大部分功能。

    二、自定义Web框架​

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #!/usr/bin/env python    
    #coding:utf-8
    from wsgiref.simple_server import make_server
     
     
    def RunServer(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        return '<h1>Hello, World!</h1>'
     
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()

        上面的代码,当我们访问时,访问任何的URL都会显示"Hello,World",这显然不是我们先要的结果。下面我们可以根据用户输入的URL不同,返回不同的字符串。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #!/usr/bin/env python
    # coding:utf-8
    from wsgiref.simple_server import make_server
     
     
    def RunServer(environ, start_response):
        """
        根据url的不同,返回不同的字符串
        :param environ: 当请求过来之后执行RunServer,wsgi给咱们封装了这些请求,这些请求都封装到了environ和start_response里
        :param start_response:
        :return:
        """
        start_response('200 OK', [('Content-Type', 'text/html')])
         
        request_url = environ['PATH_INFO']
        print request_url
         
        #根据URL做不同的响应
        # print environ 这里可以通过Pycharm断点来查看environ封装的数据
         
        if request_url == '/index':
            return "Hello Home"
        elif request_url == '/admin':
            return "Hello admin"
        else:
            return '<h1>404!</h1>'
     
     
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()

        看上面的操作,如果别人使用上面定义的框架,当用户来访问的时候,如果URL少还行,如果很多的话,就不是很完美了,为了解决这个问题,我们将上面代码做相对应修改:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
     
    from wsgiref.simple_server import make_server
     
    '''可以进行拆分---这一部分可以给框架使用者,按照定义的格式来操作'''
     
    def index():
        return 'index'
     
    def login():
        return 'login'
     
     
    #1 定义一个列表,上面定义函数
    url_list = [
        #这里吧URL和函数做一个对应
        ('/index/',index),
        ('/login/',login),
    ]
     
    ########################################################################
     
    '''这一部分可以单独拿出来,作为框架开发者,框架使用'''
    def RunServer(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        #根据url的不同,返回不同的字符串
        #1 获取URL[URL从哪里获取?当请求过来之后执行RunServer,wsgi给咱们封装了这些请求,这些请求都封装到了,environ & start_response]
        request_url = environ['PATH_INFO']
        #2 根据URL做不同的相应
        #print environ #这里可以通过断点来查看它都封装了什么数据
     
        #循环这个列表
        for url in url_list:
            #如果用户请求的url和咱们定义的rul匹配
            if request_url == url[0]:
                print url
                return url[1]()
                #执行里面的方法
        else:
            #url_list列表里都没有返回404
            return '404'
     
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()

    2、使用模板引擎

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

     1 <!DOCTYPE html>
     2 <html>
     3 <head lang="en">
     4     <meta charset="UTF-8">
     5     <title></title>
     6 </head>
     7 <body>
     8     <h1>Index</h1>
     9  
    10 </body>
    11 </html>
    index.html
     1 <!DOCTYPE html>
     2 <html>
     3 <head lang="en">
     4     <meta charset="UTF-8">
     5     <title></title>
     6 </head>
     7 <body>
     8     <form>
     9         <input type="text" />
    10         <input type="text" />
    11         <input type="submit" />
    12     </form>
    13 </body>
    14 </html>
    15  
    login.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
     
    from wsgiref.simple_server import make_server
     
     
    def index():
        #读取html并返回
        data = open('html/index.html').read()
        return data
     
    def login():
        #读取html并返回
        data = open('html/login.html').read()
        return data
     
     
    #1 定义一个列表,上面定义函数
    url_list = [
        #这里吧URL和函数做一个对应
        ('/index/',index),
        ('/login/',login),
    ]
     
     
    def RunServer(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        #根据url的不同,返回不同的字符串
        #1 获取URL[URL从哪里获取?当请求过来之后执行RunServer,wsgi给咱们封装了这些请求,这些请求都封装到了,environ & start_response]
        request_url = environ['PATH_INFO']
        #2 根据URL做不同的相应
        #print environ #这里可以通过断点来查看它都封装了什么数据
     
        #循环这个列表
        for url in url_list:
            #如果用户请求的url和咱们定义的rul匹配
            if request_url == url[0]:
                print url
                return url[1]()
                #执行里面的方法
        else:
            #url_list列表里都没有返回404
            return '404'
     
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()

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

    • 自定义一套特殊的语法,进行转换;

    • 使用开源工具jinja2,遵循其指定语法。

     1 <!DOCTYPE html>
     2 <html>
     3 <head lang="en">
     4     <meta charset="UTF-8">
     5     <title></title>
     6 </head>
     7 <body>
     8     <h1>{{name}}</h1>
     9  
    10     <ul>
    11         {% for item in user_list %}
    12         <li>{{item}}</li>
    13         {% endfor %}
    14     </ul>
    15  
    16 </body>
    17 </html>
    index.html
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
     
    import time
    from wsgiref.simple_server import make_server
    from jinja2 import Template
     
     
    def index():
        data = open('html/index.html').read()
     
        template = Template(data)
        result = template.render(
            name = 'luotianshuai',
            age = '18',
            time = str(time.time()),
            user_list = ['tianshuai','tim','shuaige'],
            num = 1
        )
     
        #同样是替换为什么用jinja,因为他不仅仅是文本的他还支持if判断 & for循环 操作
     
        #这里需要注意因为默认是的unicode的编码所以设置为utf-8
        return result.encode('utf-8')
     
    def login():
        #读取html并返回
        data = open('html/login.html').read()
        return data
     
     
    #1 定义一个列表,上面定义函数
    url_list = [
        #这里吧URL和函数做一个对应
        ('/index/',index),
        ('/login/',login),
    ]
     
     
    def RunServer(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        #根据url的不同,返回不同的字符串
        #1 获取URL[URL从哪里获取?当请求过来之后执行RunServer,wsgi给咱们封装了这些请求,这些请求都封装到了,environ & start_response]
        request_url = environ['PATH_INFO']
        #2 根据URL做不同的相应
        #print environ #这里可以通过断点来查看它都封装了什么数据
     
        #循环这个列表
        for url in url_list:
            #如果用户请求的url和咱们定义的rul匹配
            if request_url == url[0]:
                print url
                return url[1]()
                #执行里面的方法
        else:
            #url_list列表里都没有返回404
            return '404'
     
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()

        遵循jinja2的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容。

    参考资料:



  • 相关阅读:
    BZOJ 4358 坑 莫队+线段树 死T
    BZOJ 4321 DP
    两倍问题
    通宵教室
    [编程题]字符串模式匹配
    [编程题]表达式求值
    [编程题]美团骑手包裹区间分组
    1153 Decode Registration Card of PAT
    1154 Vertex Coloring
    1155 Heap Paths
  • 原文地址:https://www.cnblogs.com/phennry/p/5844053.html
Copyright © 2011-2022 走看看