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的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容。

参考资料:



原文地址:https://www.cnblogs.com/phennry/p/5844053.html