当前位置:   article > 正文

【Python】Web开发基础_python 做web 开发使用的是 cgi 技术原理吗

python 做web 开发使用的是 cgi 技术原理吗

一、web应用服务

Web应用服务架构
Web运作体系

Web服务的本质:
1. 浏览器发送一个HTTP请求
2. 服务器收到请求,生成一个HTML文档
3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器
4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示
  • 1
  • 2
  • 3
  • 4
  • 5
  • 最简单的Web应用服务就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
  • 如果要动态生成HTML,就需要把上述步骤(如接受HTTP请求、解析HTTP请求、发送HTTP响应等)自己来实现。这需要你花大量的时间去读HTTP规范。

正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。

二、python中的WSGI

WSGI是基于现存的CGI标准而设计的,是Python的CGI包装。WSGI没有官方的实现, 因为WSGI更像一个协议。

  • Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。
  • WSGI(有时发音作’wiz-gee’)是作为Web服务器与Web应用程序或应用框架之间的一种低级别的接口,以提升可移植Web应用开发的共同点。
    Web开发基础

1、测试简单的WSGI服务器

  • 编写hello.py作为一个Web应用程序
'''
application():符合WSGI标准的一个HTTP处理函数,必须由WSGI服务器来调用
environ:一个包含所有HTTP请求信息的dict对象
start_response:一个发送HTTP响应的函数,该函数发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数
'''
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'<h1>Hello, World!</h1>']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
'
运行
  • 编写server.py作为一个WSGI服务器
from wsgiref.simple_server import make_server
# 导入编写的application函数
from hello import application
# 创建一个服务器,IP地址为空,端口是8000,传入函数application
httpd = make_server('', 8000, application)
print('Serving HTTP on port 8000...')
# 开始监听HTTP请求:
httpd.serve_forever()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 启动WSGI服务器

     python server.py
    
    • 1
  • 使用客户端访问
    打开浏览器,输入http://localhost:8000/ ,在浏览器正常显示“Hello, World!”
    1
    2

2、升级版

这次的服务器功能更为全面,同时也将使用flask框架

  • 在WSGIServer.py文件下
# python3环境,python2与python3的StringIO模块所在位置不同,同时部分解码也有所差异
import socket
from io import StringIO
import sys
'''
WSGI服务器类
'''
class WSGIServer(object):

    address_family = socket.AF_INET #地址家族:IPv4
    socket_type = socket.SOCK_STREAM #传输模式:可靠的稳定的(TCP)
    request_queue_size = 1 #请求队列大小

    def __init__(self, server_address):
        # 创建socket,利用socket获取客户端的请求
        self.listen_socket = listen_socket = socket.socket(self.address_family, self.socket_type)
        # 设置socket的工作模式,SOL_SOCKET设置级别,SO_REUSEADDR大意是允许服务器bind一个地址,即使这个地址当前已经存在已建立的连接
        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定socket地址
        listen_socket.bind(server_address)
        # socket active, 监听文件描述符
        listen_socket.listen(self.request_queue_size)
        # 获得serve的host name和port
        host, port = self.listen_socket.getsockname()[:2]
        self.server_name = socket.getfqdn(host)
        self.server_port = port
		
        self.headers_set = []

    def set_app(self, application):
        self.application = application 

    #启动WSGI server服务,不停的监听并获取socket数据。
    def serve_forever(self):
        listen_socket = self.listen_socket
        while True:
        	#接受客户端请求
            self.client_connection, client_address = listen_socket.accept()
            #处理客户端请求
            self.handle_one_request()

    def handle_one_request(self): #处理请求
        #1.获取请求字节数据
        self.request_data = request_data = self.client_connection.recv(1024).decode()
        #2.按照协议解析请求字节数据
        self.parse_request(request_data)
        #3.将请求数据构造成字典并作相应的设置
        env = self.get_environ()
        #4.给(Web应用程序)flask\tornado传递两个参数,environ,start_response,并获取application返回给WSGI的数据
        result = self.application(env, self.start_response)
        #5.把数据result返回给客户端
        self.finish_response(result)

    def parse_request(self, data):	#处理socket的http协议
    	#splitlines()按照行('\r', '\r\n', \n')分隔,返回一个包含各行作为元素的列表,
        format_data = data.splitlines()
        if len(format_data):
            request_line = data.splitlines()[0]
            # 去除多余字符
            request_line = request_line.rstrip('\r\n')
            # ['GET', '/', 'HTTP/1.1']
            (self.request_method, self.path, self.request_version) = request_line.split() 

    def get_environ(self): # 获取environ数据并设置当前server的工作模式
        env = {}
        env['wsgi.version']      = (1, 0)
        env['wsgi.url_scheme']   = 'http'
        env['wsgi.input']        = StringIO(self.request_data).getvalue()
        env['wsgi.errors']       = sys.stderr
        env['wsgi.multithread']  = False
        env['wsgi.multiprocess'] = False
        env['wsgi.run_once']     = False
        # 必需的CGI变量
        env['REQUEST_METHOD']    = self.request_method    # GET
        env['PATH_INFO']         = self.path              # /hello
        env['SERVER_NAME']       = self.server_name       # localhost
        env['SERVER_PORT']       = str(self.server_port)  # 8888
        return env

    def start_response(self, status, response_headers, exc_info=None): #设置HTTP响应的Header
        server_headers = [('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'), ('Server', 'WSGIServer 0.2')]
        self.headers_set = [status, response_headers + server_headers]

    def finish_response(self, result): #向客户端发送响应
        try:
        	#格式化处理
            status, response_headers = self.headers_set
            response = 'HTTP/1.1 {status}\r\n'.format(status=status)
            for header in response_headers:
                response += '{0}: {1}\r\n'.format(*header)
            response += '\r\n'
            response = str.encode(response)
            for data in result:
                response += data
            #发送响应
            self.client_connection.sendall(response)
            print(''.join(['> {line}\n'.format(line=line) for line in response.splitlines()]))
        finally:
            self.client_connection.close()
'''———————————————————————————————————————————————————————————————————————————————————————————'''
#服务器地址和端口
SERVER_ADDRESS = (HOST, PORT) = '', 8888

def make_server(server_address, application):
    server = WSGIServer(server_address)
    server.set_app(application)
    return server

if __name__ == '__main__':
	#sys.argv 是获取运行python文件的时候命令行参数,且以list形式存储参数
	#sys.argv[0]表示代码本身文件路径
    if len(sys.argv) < 2:
        sys.exit('Provide a WSGI application object as module:callable')
    app_path = sys.argv[1]
    #split(':')指定分隔符为:并对字符串进行切片
    module, application = app_path.split(':') # 第一个参数是文件名,第二个参数是文件内app的名字
    module = __import__(module) #__import__()函数用于动态加载类和函数
    application = getattr(module, application) # getattr() 函数用于返回一个对象属性值或获取对象方法的运行结果,getattr(object, name[, default]) -> value
    #创建服务器
    httpd = make_server(SERVER_ADDRESS, application)
    print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))
    #启动服务器
    httpd.serve_forever()
  • 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
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 在flaskapp.py文件下
from flask import Flask
from flask import Response
flask_app = Flask ( 'flaskapp' )
 
 
@ flask_app . route ( '/hello' )
def hello_world ( ) :
    return Response (
        'Hello world from Flask!\r\n' ,
        mimetype = 'text/plain'
    )

app = flask_app . wsgi_app
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 在终端中输入python.exe .\WSGIServer.py flaskapp:app
  • 在浏览器窗口搜索栏中输入http://localhost:8888/hello
    c1
    c2

三、生产环境下的Web

1、Web服务器

每个web框架都不是专注于实现服务器方面的,因此,在生产环境部署的时候,使用的服务器也不会简单的使用web框架自带的服务器

  • Nginx是一个异步框架的 Web服务器,也可以用作反向代理,负载平衡器 和 HTTP缓存。

正向代理是指浏览器主动请求代理服务器,代理服务器转发请求到对应的目标服务器。
反向代理则部署在Web服务器上,代理所有外部网络对内部网络的访问。浏览器访问服务器,必须经过这个代理,是被动的。
正向代理的主动方是客户端,反向代理的主动方是Web服务器

  • Gunicorn服务器:依赖Nginx的代理行为,同Nginx进行功能上的分离。由于不需要直接处理用户来的请求(都被Nginx先处理),Gunicorn不需要完成相关的功能,其内部逻辑非常简单:接受从Nginx来的动态请求,处理完之后返回给Nginx,由后者返回给用户。

  • uWSGI服务器: uWSGI既不使用wsgi协议也不用FastCGI协议,而是自创了一个uwsgi的协议,uwsgi协议是一个uWSGI服务器自有的协议,它用于定义传输信息的类型,每一个uwsgi packet前4byte为传输信息类型描述,它与WSGI相比是两样东西。

     uWSGI 服务器自己实现了基于uwsgi协议的server部分,因此我们只需要在uwsgi的配置文件中指定application的地址,uWSGI 就能直接和应用框架中的WSGI application通信。
    
    • 1
  • bjoern服务器:是一个用C语言编写的,快速超轻量级的 Python WSGI服务器。它是最快速的,最小的并且是最轻量级的WSGI服务器。

如果单纯追求性能,那uWSGI会更好一点,而Gunicorn则会更易安装和结合gevent。

在Python的Web开发中,较为成熟稳定的服务器架构一般是Nginx + uWSGI + Django。
而实际上Nginx服务器并不是必须的,直接使用uWSGI + Djang完全是可以的,
但这样一来,直接将uWSGI服务器暴露给了浏览器客户端,由此会导致诸多隐患。
  • 1
  • 2
  • 3

2、Web应用程序

  • 常见的Python Web应用框架

     Django:全能型Web框架
     Flask:一个使用Python编写的轻量级Web框架
     web.py:一个小巧的Web框架
     Bottle:和Flask类似的Web框架
     Tornado:Facebook的开源异步Web框架
    
    • 1
    • 2
    • 3
    • 4
    • 5

四、Web中的HTTP状态码

  • 2xx:成功

     200 正常,请求已完成。
     201 正常,紧接POST命令。
     202 正常,已接受用于处理,但处理尚未完成。
     203 正常,部分信息—返回的信息只是一部分。
     204 正常,无响应—已接收请求,但不存在要回送的信息。
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 3xx:重定向

     301 已移动,请求的数据具有新的位置且更改是永久的。
     302 已找到,请求的数据临时具有不同 URI。
     303 请参阅其它,可在另一 URI 下找到对请求的响应,且应使用 GET 方法检索此响应。
     304 未修改,未按预期修改文档。
     305 使用代理,必须通过位置字段中提供的代理来访问请求的资源。
     306 未使用,不再使用,保留此代码以便将来使用。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 4xx:客户机中出现的错误

     400 错误请求,请求中有语法问题,或不能满足请求。
     401 未授权,未授权客户机访问数据。
     402 需要付款,表示计费系统已有效。
     403 禁止,即使有授权也不需要访问。
     404 找不到,服务器找不到给定的资源;文档不存在。
     407 代理认证请求,客户机首先必须使用代理认证自身。
     415 介质类型不受支持,服务器拒绝服务请求,因为不支持请求实体的格式。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 5xx:服务器中出现的错误

     500 内部错误,因为意外情况,服务器不能完成请求。
     501 未执行,服务器不支持请求的工具。
     502 错误网关,服务器接收到来自上游服务器的无效响应。
     503 无法获得服务,由于临时过载或维护,服务器无法处理请求。
    
    • 1
    • 2
    • 3
    • 4
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号