天天看點

深入解析Python中的WSGI接口

概述

WSGI接口包含兩方面:server/gateway 及 application/framework。

server調用由application提供的可調用對象。

另外在server和application之間還可能有一種稱作middleware的中間件。

可調用對象是指:函數、方法、類或者帶有callable方法的執行個體。

關于application

函數、方法、類及帶有callable方法的執行個體等可調用對象都可以作為the application object。

WSGI協定要求:

the application object接受兩個參數且可以被多次調用

這兩個參數分别為:

1.CGI式的字典;

2.回調函數:application用來向server傳遞http狀态碼/消息/http頭

另外協定要求可調用對象必須将響應體封裝成一個可疊代的strings傳回。

?

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

# the application object. 可以使用其他名字,

# 但是在使用mod_wsgi 時必須為 "application"

def

application( environ, start_response):

# 函數接受兩個參數:

# environ :包含有CGI 式環境變量的字典,由server負責提供内容

# start_response:由server提供的回調函數,其作用是将狀态碼和響應頭傳回給server

# 構造響應體,以可疊代字元串形式封裝

response_body

=

'The request method was %s'

%

environ[

'REQUEST_METHOD'

]

# HTTP 響應碼及消息

status

=

'200 OK'

# 提供給用戶端的響應頭.

# 封裝成list of tuple pairs 的形式:

# 格式要求:[(Header name, Header value)].

response_headers

=

[(

'Content-Type'

,

'text/plain'

),

(

'Content-Length'

,

str

(

len

(response_body)))]

# 将響應碼/消息及響應頭通過傳入的start_reponse回調函數傳回給server

start_response(status, response_headers)

# 響應體作為傳回值傳回

# 注意這裡被封裝到了list中.

return

[response_body]

關于server

從概述中可以知道,WSGI server必須要調用application,同時,從application的協定要求可知:

1. WSGI server必須向application提供環境參數,是以,自身也必須能夠擷取環境參數。

2. WSGI server接收application的傳回值作為響應體。

最簡單的WSGI server為Python自帶的wsgiref.simple_server

示例如下:

?

1 2 3

from

wsgiref.simple_server

import

make_server

srv

=

make_server(

'localhost'

,

8080

, hello_world)

srv.serve_forever()

關于middleware

middleware的概念沒有appllication和server那麼容易了解。

假設一個符合application标準的可調用對象,它接受可調用對象作為參數,傳回一個可調用對象的對象。

那麼對于server來說,它是一個符合标準的可調用對象,是以是application。

而對于application來說,它可以調用application,是以是server。

這樣的可調用對象稱為middleware。

middleware的概念非常接近decorator。

以一個路由的例子示例:

?

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

import

re

# 這是一個标準的application object

def

index(environ, start_response):

start_response(

'200 OK'

, [(

'Content-Type'

,

'text/html'

)])

return

[

'index page'

]

# 這是一個标準的application object

def

hello(environ, start_response):

start_response(

'200 OK'

, [(

'Content-Type'

,

'text/html'

)])

return

[

'hello page'

]

# 這是一個标準的application object

def

not_found(environ, start_response):

start_response(

'404 NOT FOUND'

, [(

'Content-Type'

,

'text/plain'

)])

return

[

'Not Found Page'

]

# map urls to functions

urls

=

[

(r

'^$'

, index),

(r

'hello/?$'

, hello)

]

# 這是一個middleware

# 根據不同的route傳回不同的application object

def

application(environ, start_response):

path

=

environ.get(

'PATH_INFO'

, '

').lstrip('

/

')

for

regex, callback

in

urls:

match

=

re.search(regex, path)

if

match

is

not

None

: