Python 如何創(chuàng)建一個簡單的REST接口
問題
你想使用一個簡單的REST接口通過網絡遠程控制或訪問你的應用程序,但是你又不想自己去安裝一個完整的web框架。
解決方案
構建一個REST風格的接口最簡單的方法是創(chuàng)建一個基于WSGI標準(PEP 3333)的很小的庫,下面是一個例子:
# resty.py
import cgi
def notfound_404(environ, start_response):
start_response('404 Not Found', [ ('Content-type', 'text/plain') ])
return [b'Not Found']
class PathDispatcher:
def __init__(self):
self.pathmap = { }
def __call__(self, environ, start_response):
path = environ['PATH_INFO']
params = cgi.FieldStorage(environ['wsgi.input'],
environ=environ)
method = environ['REQUEST_METHOD'].lower()
environ['params'] = { key: params.getvalue(key) for key in params }
handler = self.pathmap.get((method,path), notfound_404)
return handler(environ, start_response)
def register(self, method, path, function):
self.pathmap[method.lower(), path] = function
return function
為了使用這個調度器,你只需要編寫不同的處理器,就像下面這樣:
import time
_hello_resp = '''\
<html>
<head>
<title>Hello {name}</title>
</head>
<body>
<h1>Hello {name}!</h1>
</body>
</html>'''
def hello_world(environ, start_response):
start_response('200 OK', [ ('Content-type','text/html')])
params = environ['params']
resp = _hello_resp.format(name=params.get('name'))
yield resp.encode('utf-8')
_localtime_resp = '''\
<?xml version="1.0"?>
<time>
<year>{t.tm_year}</year>
<month>{t.tm_mon}</month>
<day>{t.tm_mday}</day>
<hour>{t.tm_hour}</hour>
<minute>{t.tm_min}</minute>
<second>{t.tm_sec}</second>
</time>'''
def localtime(environ, start_response):
start_response('200 OK', [ ('Content-type', 'application/xml') ])
resp = _localtime_resp.format(t=time.localtime())
yield resp.encode('utf-8')
if __name__ == '__main__':
from resty import PathDispatcher
from wsgiref.simple_server import make_server
# Create the dispatcher and register functions
dispatcher = PathDispatcher()
dispatcher.register('GET', '/hello', hello_world)
dispatcher.register('GET', '/localtime', localtime)
# Launch a basic server
httpd = make_server('', 8080, dispatcher)
print('Serving on port 8080...')
httpd.serve_forever()
要測試下這個服務器,你可以使用一個瀏覽器或 urllib 和它交互。例如:
>>> u = urlopen('http://localhost:8080/hello?name=Guido')
>>> print(u.read().decode('utf-8'))
<html>
<head>
<title>Hello Guido</title>
</head>
<body>
<h1>Hello Guido!</h1>
</body>
</html>
>>> u = urlopen('http://localhost:8080/localtime')
>>> print(u.read().decode('utf-8'))
<?xml version="1.0"?>
<time>
<year>2012</year>
<month>11</month>
<day>24</day>
<hour>14</hour>
<minute>49</minute>
<second>17</second>
</time>
>>>
討論
在編寫REST接口時,通常都是服務于普通的HTTP請求。但是跟那些功能完整的網站相比,你通常只需要處理數據。 這些數據以各種標準格式編碼,比如XML、JSON或CSV。 盡管程序看上去很簡單,但是以這種方式提供的API對于很多應用程序來講是非常有用的。
例如,長期運行的程序可能會使用一個REST API來實現監(jiān)控或診斷。 大數據應用程序可以使用REST來構建一個數據查詢或提取系統。 REST還能用來控制硬件設備比如機器人、傳感器、工廠或燈泡。 更重要的是,REST API已經被大量客戶端編程環(huán)境所支持,比如Javascript, Android, iOS等。 因此,利用這種接口可以讓你開發(fā)出更加復雜的應用程序。
為了實現一個簡單的REST接口,你只需讓你的程序代碼滿足Python的WSGI標準即可。 WSGI被標準庫支持,同時也被絕大部分第三方web框架支持。 因此,如果你的代碼遵循這個標準,在后面的使用過程中就會更加的靈活!
在WSGI中,你可以像下面這樣約定的方式以一個可調用對象形式來實現你的程序。
import cgi def wsgi_app(environ, start_response): pass
environ 屬性是一個字典,包含了從web服務器如Apache[參考Internet RFC 3875]提供的CGI接口中獲取的值。 要將這些不同的值提取出來,你可以像這么這樣寫:
def wsgi_app(environ, start_response): method = environ['REQUEST_METHOD'] path = environ['PATH_INFO'] # Parse the query parameters params = cgi.FieldStorage(environ['wsgi.input'], environ=environ)
我們展示了一些常見的值。environ['REQUEST_METHOD'] 代表請求類型如GET、POST、HEAD等。 environ['PATH_INFO'] 表示被請求資源的路徑。 調用 cgi.FieldStorage() 可以從請求中提取查詢參數并將它們放入一個類字典對象中以便后面使用。
start_response 參數是一個為了初始化一個請求對象而必須被調用的函數。 第一個參數是返回的HTTP狀態(tài)值,第二個參數是一個(名,值)元組列表,用來構建返回的HTTP頭。例如:
def wsgi_app(environ, start_response):
pass
start_response('200 OK', [('Content-type', 'text/plain')])
為了返回數據,一個WSGI程序必須返回一個字節(jié)字符串序列??梢韵裣旅孢@樣使用一個列表來完成:
def wsgi_app(environ, start_response):
pass
start_response('200 OK', [('Content-type', 'text/plain')])
resp = []
resp.append(b'Hello World\n')
resp.append(b'Goodbye!\n')
return resp
或者,你還可以使用 yield :
def wsgi_app(environ, start_response):
pass
start_response('200 OK', [('Content-type', 'text/plain')])
yield b'Hello World\n'
yield b'Goodbye!\n'
這里要強調的一點是最后返回的必須是字節(jié)字符串。如果返回結果包含文本字符串,必須先將其編碼成字節(jié)。 當然,并沒有要求你返回的一定是文本,你可以很輕松的編寫一個生成圖片的程序。
盡管WSGI程序通常被定義成一個函數,不過你也可以使用類實例來實現,只要它實現了合適的 __call__() 方法。例如:
class WSGIApplication:
def __init__(self):
...
def __call__(self, environ, start_response)
...
我們已經在上面使用這種技術創(chuàng)建 PathDispatcher 類。 這個分發(fā)器僅僅只是管理一個字典,將(方法,路徑)對映射到處理器函數上面。 當一個請求到來時,它的方法和路徑被提取出來,然后被分發(fā)到對應的處理器上面去。 另外,任何查詢變量會被解析后放到一個字典中,以 environ['params'] 形式存儲。 后面這個步驟太常見,所以建議你在分發(fā)器里面完成,這樣可以省掉很多重復代碼。 使用分發(fā)器的時候,你只需簡單的創(chuàng)建一個實例,然后通過它注冊各種WSGI形式的函數。 編寫這些函數應該超級簡單了,只要你遵循 start_response() 函數的編寫規(guī)則,并且最后返回字節(jié)字符串即可。
當編寫這種函數的時候還需注意的一點就是對于字符串模板的使用。 沒人愿意寫那種到處混合著 print() 函數 、XML和大量格式化操作的代碼。 我們上面使用了三引號包含的預先定義好的字符串模板。 這種方式的可以讓我們很容易的在以后修改輸出格式(只需要修改模板本身,而不用動任何使用它的地方)。
最后,使用WSGI還有一個很重要的部分就是沒有什么地方是針對特定web服務器的。 因為標準對于服務器和框架是中立的,你可以將你的程序放入任何類型服務器中。 我們使用下面的代碼測試測試本節(jié)代碼:
if __name__ == '__main__':
from wsgiref.simple_server import make_server
# Create the dispatcher and register functions
dispatcher = PathDispatcher()
pass
# Launch a basic server
httpd = make_server('', 8080, dispatcher)
print('Serving on port 8080...')
httpd.serve_forever()
上面代碼創(chuàng)建了一個簡單的服務器,然后你就可以來測試下你的實現是否能正常工作。 最后,當你準備進一步擴展你的程序的時候,你可以修改這個代碼,讓它可以為特定服務器工作。
WSGI本身是一個很小的標準。因此它并沒有提供一些高級的特性比如認證、cookies、重定向等。 這些你自己實現起來也不難。不過如果你想要更多的支持,可以考慮第三方庫,比如 WebOb 或者 Paste
以上就是Python 如何創(chuàng)建一個簡單的REST接口的詳細內容,更多關于Python 創(chuàng)建REST接口的資料請關注腳本之家其它相關文章!
相關文章
python調用系統ffmpeg實現視頻截圖、http發(fā)送
這篇文章主要為大家詳細介紹了python調用系統ffmpeg實現視頻截圖、http發(fā)送,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03
使用虛擬環(huán)境打包python為exe 文件的方法
這篇文章主要介紹了關于使用虛擬環(huán)境打包python為exe 文件的方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08
Python源碼學習之PyObject和PyTypeObject
今天給大家?guī)淼氖顷P于Python源碼的相關知識學習,文章圍繞著PyObject和PyTypeObject展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下2021-06-06

