国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

11.5 生成一個(gè)簡(jiǎn)單的REST接口

2018-02-24 15:27 更新

問(wèn)題

你想使用一個(gè)簡(jiǎn)單的REST接口通過(guò)網(wǎng)絡(luò)遠(yuǎn)程控制或訪問(wèn)你的應(yīng)用程序,但是你又不想自己去安裝一個(gè)完整的web框架。

解決方案

構(gòu)建一個(gè)REST風(fēng)格的接口最簡(jiǎn)單的方法是創(chuàng)建一個(gè)基于WSGI標(biāo)準(zhǔn)(PEP 3333)的很小的庫(kù),下面是一個(gè)例子:

# 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

為了使用這個(gè)調(diào)度器,你只需要編寫(xiě)不同的處理器,就像下面這樣:

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()

要測(cè)試下這個(gè)服務(wù)器,你可以使用一個(gè)瀏覽器或?<span class="pre" style="box-sizing: border-box;">urllib</span>?和它交互。例如:

>>> 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>
>>>

討論

在編寫(xiě)REST接口時(shí),通常都是服務(wù)于普通的HTTP請(qǐng)求。但是跟那些功能完整的網(wǎng)站相比,你通常只需要處理數(shù)據(jù)。 這些數(shù)據(jù)以各種標(biāo)準(zhǔn)格式編碼,比如XML、JSON或CSV。 盡管程序看上去很簡(jiǎn)單,但是以這種方式提供的API對(duì)于很多應(yīng)用程序來(lái)講是非常有用的。

例如,長(zhǎng)期運(yùn)行的程序可能會(huì)使用一個(gè)REST API來(lái)實(shí)現(xiàn)監(jiān)控或診斷。 大數(shù)據(jù)應(yīng)用程序可以使用REST來(lái)構(gòu)建一個(gè)數(shù)據(jù)查詢(xún)或提取系統(tǒng)。 REST還能用來(lái)控制硬件設(shè)備比如機(jī)器人、傳感器、工廠或燈泡。 更重要的是,REST API已經(jīng)被大量客戶(hù)端編程環(huán)境所支持,比如Javascript, Android, iOS等。 因此,利用這種接口可以讓你開(kāi)發(fā)出更加復(fù)雜的應(yīng)用程序。

為了實(shí)現(xiàn)一個(gè)簡(jiǎn)單的REST接口,你只需讓你的程序代碼滿(mǎn)足Python的WSGI標(biāo)準(zhǔn)即可。 WSGI被標(biāo)準(zhǔn)庫(kù)支持,同時(shí)也被絕大部分第三方web框架支持。 因此,如果你的代碼遵循這個(gè)標(biāo)準(zhǔn),在后面的使用過(guò)程中就會(huì)更加的靈活!

在WSGI中,你可以像下面這樣約定的方式以一個(gè)可調(diào)用對(duì)象形式來(lái)實(shí)現(xiàn)你的程序。

import cgi

def wsgi_app(environ, start_response):
    pass

<span class="pre" style="box-sizing: border-box;">environ</span>?屬性是一個(gè)字典,包含了從web服務(wù)器如Apache[參考Internet RFC 3875]提供的CGI接口中獲取的值。 要將這些不同的值提取出來(lái),你可以像這么這樣寫(xiě):

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)

我們展示了一些常見(jiàn)的值。<span class="pre" style="box-sizing: border-box;">environ['REQUEST_METHOD']</span>?代表請(qǐng)求類(lèi)型如GET、POST、HEAD等。<span class="pre" style="box-sizing: border-box;">environ['PATH_INFO']</span>?表示被請(qǐng)求資源的路徑。 調(diào)用?<span class="pre" style="box-sizing: border-box;">cgi.FieldStorage()</span>?可以從請(qǐng)求中提取查詢(xún)參數(shù)并將它們放入一個(gè)類(lèi)字典對(duì)象中以便后面使用。

<span class="pre" style="box-sizing: border-box;">start_response</span>?參數(shù)是一個(gè)為了初始化一個(gè)請(qǐng)求對(duì)象而必須被調(diào)用的函數(shù)。 第一個(gè)參數(shù)是返回的HTTP狀態(tài)值,第二個(gè)參數(shù)是一個(gè)(名,值)元組列表,用來(lái)構(gòu)建返回的HTTP頭。例如:

def wsgi_app(environ, start_response):
    pass
    start_response('200 OK', [('Content-type', 'text/plain')])

為了返回?cái)?shù)據(jù),一個(gè)WSGI程序必須返回一個(gè)字節(jié)字符串序列??梢韵裣旅孢@樣使用一個(gè)列表來(lái)完成:

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

或者,你還可以使用?<span class="pre" style="box-sizing: border-box;">yield</span>?:

def wsgi_app(environ, start_response):
    pass
    start_response('200 OK', [('Content-type', 'text/plain')])
    yield b'Hello World\n'
    yield b'Goodbye!\n'

這里要強(qiáng)調(diào)的一點(diǎn)是最后返回的必須是字節(jié)字符串。如果返回結(jié)果包含文本字符串,必須先將其編碼成字節(jié)。 當(dāng)然,并沒(méi)有要求你返回的一點(diǎn)是文本,你可以很輕松的編寫(xiě)一個(gè)生成圖片的程序。

盡管WSGI程序通常被定義成一個(gè)函數(shù),不過(guò)你也可以使用類(lèi)實(shí)例來(lái)實(shí)現(xiàn),只要它實(shí)現(xiàn)了合適的<span class="pre" style="box-sizing: border-box;">__call__()</span>?方法。例如:

class WSGIApplication:
    def __init__(self):
        ...
    def __call__(self, environ, start_response)
       ...

我們已經(jīng)在上面使用這種技術(shù)創(chuàng)建?<span class="pre" style="box-sizing: border-box;">PathDispatcher</span>?類(lèi)。 這個(gè)分發(fā)器僅僅只是管理一個(gè)字典,將(方法,路徑)對(duì)映射到處理器函數(shù)上面。 當(dāng)一個(gè)請(qǐng)求到來(lái)時(shí),它的方法和路徑被提取出來(lái),然后被分發(fā)到對(duì)應(yīng)的處理器上面去。 另外,任何查詢(xún)變量會(huì)被解析后放到一個(gè)字典中,以?<span class="pre" style="box-sizing: border-box;">environ['params']</span>?形式存儲(chǔ)。 后面這個(gè)步驟太常見(jiàn),所以建議你在分發(fā)器里面完成,這樣可以省掉很多重復(fù)代碼。 使用分發(fā)器的時(shí)候,你只需簡(jiǎn)單的創(chuàng)建一個(gè)實(shí)例,然后通過(guò)它注冊(cè)各種WSGI形式的函數(shù)。 編寫(xiě)這些函數(shù)應(yīng)該超級(jí)簡(jiǎn)單了,只要你遵循?<span class="pre" style="box-sizing: border-box;">start_response()</span>?函數(shù)的編寫(xiě)規(guī)則,并且最后返回字節(jié)字符串即可。

當(dāng)編寫(xiě)這種函數(shù)的時(shí)候還需注意的一點(diǎn)就是對(duì)于字符串模板的使用。 沒(méi)人愿意寫(xiě)那種到處混合著<span class="pre" style="box-sizing: border-box;">print()</span>?函數(shù) 、XML和大量格式化操作的代碼。 我們上面使用了三引號(hào)包含的預(yù)先定義好的字符串模板。 這種方式的可以讓我們很容易的在以后修改輸出格式(只需要修改模板本身,而不用動(dòng)任何使用它的地方)。

最后,使用WSGI還有一個(gè)很重要的部分就是沒(méi)有什么地方是針對(duì)特定web服務(wù)器的。 因?yàn)闃?biāo)準(zhǔn)對(duì)于服務(wù)器和框架是中立的,你可以將你的程序放入任何類(lèi)型服務(wù)器中。 我們使用下面的代碼測(cè)試測(cè)試本節(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)建了一個(gè)簡(jiǎn)單的服務(wù)器,然后你就可以來(lái)測(cè)試下你的實(shí)現(xiàn)是否能正常工作。 最后,當(dāng)你準(zhǔn)備進(jìn)一步擴(kuò)展你的程序的時(shí)候,你可以修改這個(gè)代碼,讓它可以為特定服務(wù)器工作。

WSGI本身是一個(gè)很小的標(biāo)準(zhǔn)。因此它并沒(méi)有提供一些高級(jí)的特性比如認(rèn)證、cookies、重定向等。 這些你自己實(shí)現(xiàn)起來(lái)也不難。不過(guò)如果你想要更多的支持,可以考慮第三方庫(kù),比如?<span class="pre" style="box-sizing: border-box;">WebOb</span>?或者<span class="pre" style="box-sizing: border-box;">Paste</span>

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)