使用 Flask 實(shí)現(xiàn) RESTful API

Flash做RESTful API的優(yōu)勢(shì)

Flask是一個(gè)使用Python開發(fā)的基于Werkzeug的Web框架焙压。
Flask非常適合于開發(fā)RESTful API蝌焚,因?yàn)樗哂幸韵绿攸c(diǎn):
1.使用Python進(jìn)行開發(fā)钻洒,Python簡(jiǎn)潔易懂
2.容易上手
3.靈活
4.可以部署到不同的環(huán)境
5.支持RESTful請(qǐng)求分發(fā)
我一般是用curl命令進(jìn)行測(cè)試遭殉,除此之外杀捻,還可以使用Chrome瀏覽器的postman擴(kuò)展刁标。


image.png

測(cè)試應(yīng)用

首先颠通,創(chuàng)建一個(gè)完整的應(yīng)用,支持響應(yīng)/, /tests以及/test/:id膀懈。

from flask import Flask, url_for
app = Flask(__name__)

@app.route('/')
def api_test():
    return 'hello word!'

@app.route('/tests')
def api_tests():
    return 'List of ' + url_for('api_tests')

@app.route('/test/<testid>')
def api_testes(testid):
    return 'You are reading ' + testid

if __name__ == '__main__':
    app.run()

url_for('function_name') 返回function的路由顿锰。
可以使用curl命令發(fā)送請(qǐng)求:

GET /
hello word!

GET /tests
List of /tests

GET /test/111
You are reading 111

路由中還可以使用類型定義:

@app.route('/test/<testid>')

上面的路由可以替換成下面的例子:

@app.route('/articles/<int:testid>')
@app.route('/articles/<float:testid>')
@app.route('/articles/<path:testid>')

請(qǐng)求REQUESTS

請(qǐng)求參數(shù)

假設(shè)需要響應(yīng)一個(gè)/hello請(qǐng)求,使用get方法启搂,并傳遞參數(shù)name

from flask import request,Flask
app = Flask(__name__)
@app.route('/hello')
def api_hello():
    if 'name' in request.args:
        return 'Hello ' + request.args['name']
    else:
        return 'Hello John Doe'

if __name__ == '__main__':
    app.run()

服務(wù)器會(huì)返回如下響應(yīng)信息:

GET /hello
Hello John Doe

GET /hello?name=xiaokai
Hello xiaokai

請(qǐng)求方法

Flask支持不同的請(qǐng)求方法:

from flask import request,Flask
app = Flask(__name__)
@app.route('/echo', methods = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'])
def api_echo():
    if request.method == 'GET':
        return "ECHO: GET\n"

    elif request.method == 'POST':
        return "ECHO: POST\n"

    elif request.method == 'PATCH':
        return "ECHO: PACTH\n"

    elif request.method == 'PUT':
        return "ECHO: PUT\n"

    elif request.method == 'DELETE':
        return "ECHO: DELETE"
if __name__ == '__main__':
    app.run()

可以使用如下命令進(jìn)行測(cè)試:

curl -X PATCH http://127.0.0.1:5000/echo

請(qǐng)求數(shù)據(jù)REQUEST和請(qǐng)求頭request.headers

通常使用POST方法和PATCH方法的時(shí)候硼控,都會(huì)發(fā)送附加的數(shù)據(jù),這些數(shù)據(jù)的格式可能如下:普通文本(plain text)胳赌, JSON牢撼,XML,二進(jìn)制文件或者用戶自定義格式疑苫。
Flask中使用request.headers類字典對(duì)象來(lái)獲取請(qǐng)求頭信息熏版,使用request.data獲取請(qǐng)求數(shù)據(jù),如果發(fā)送類型是application/json捍掺,則可以使用request.get_json()來(lái)獲取JSON數(shù)據(jù)撼短。

from flask import request,Flask
from flask import json
app = Flask(__name__)

@app.route('/messages', methods = ['POST'])
def api_message():

    if request.headers['Content-Type'] == 'text/plain':
        return "Text Message: " + request.data

    elif request.headers['Content-Type'] == 'application/json':
        return "JSON Message: " + json.dumps(request.json)

    elif request.headers['Content-Type'] == 'application/octet-stream':
        f = open('./binary', 'wb')
        f.write(request.data)
        f.close()
        return "Binary message written!"
    else:
        return "415 Unsupported Media Type ;)"
if __name__ == '__main__':
    app.run()

使用如下命令指定請(qǐng)求數(shù)據(jù)類型進(jìn)行測(cè)試:

curl -H "Content-type: application/json" \
-X POST http://127.0.0.1:5000/messages -d '{"message":"Hello Data"}'

注意Flask可以通過(guò)request.files獲取上傳的文件,curl可以使用-F選項(xiàng)模擬上傳文件的過(guò)程乡小。

響應(yīng)RESPONSES

Flask使用Response類處理響應(yīng)。

from flask import Flask

app = Flask(__name__)

from flask import Response

@app.route('/hello', methods = ['GET'])
def api_hello():
    data = {
        'hello'  : 'world',
        'number' : 3
    }
    js = json.dumps(data)

    resp = Response(js, status=200, mimetype='application/json')
    resp.headers['Link'] = 'http://luisrei.com'

    return resp
if __name__ == '__main__':
    app.run()s

使用-i選項(xiàng)可以獲取響應(yīng)信息:

curl -i http://127.0.0.1:5000/hello

相應(yīng)結(jié)果:

GET /hello
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 31
Link: http://luisrei.com
Server: Werkzeug/0.8.2 Python/2.7.1
Date: Wed, 25 Apr 2012 16:40:27 GMT
{"hello": "world", "number": 3}
   om flask import jsonify
...
# 將下面的代碼替換成
resp = Response(js, status=200, mimetype='application/json')
# 這里的代碼
resp = jsonify(data)
resp.status_code = 200

狀態(tài)碼和錯(cuò)誤處理

成功響應(yīng)的話饵史,狀態(tài)碼為200满钟。對(duì)于404錯(cuò)誤我們可以這樣處理:

@app.errorhandler(404)
def not_found(error=None):
   message = {
           'status': 404,
           'message': 'Not Found: ' + request.url,
   }
   resp = jsonify(message)
   resp.status_code = 404

   return resp

@app.route('/users/<userid>', methods = ['GET'])
def api_users(userid):
   users = {'1':'john', '2':'steve', '3':'bill'}

   if userid in users:
       return jsonify({userid:users[userid]})
   else:
       return not_found()

測(cè)試上面的兩個(gè)URL,結(jié)果如下:

GET /users/2
HTTP/1.0 200 OK
{
    "2": "steve"
}

GET /users/4
HTTP/1.0 404 NOT FOUND
{
"status": 404, 
"message": "Not Found: http://127.0.0.1:5000/users/4"
}

默認(rèn)的Flask錯(cuò)誤處理可以使用@error_handler修飾器進(jìn)行覆蓋或者使用下面的方法:

app.error_handler_spec[None][404] = not_found

即使API不需要自定義錯(cuò)誤信息胳喷,最好還是像上面這樣做湃番,因?yàn)镕lask默認(rèn)返回的錯(cuò)誤信息是HTML格式的。

認(rèn)證

使用下面的代碼可以處理 HTTP Basic Authentication

from functools import wraps

def check_auth(username, password):
    return username == 'admin' and password == 'secret'

def authenticate():
    message = {'message': "Authenticate."}
    resp = jsonify(message)

    resp.status_code = 401
    resp.headers['WWW-Authenticate'] = 'Basic realm="Example"'

    return resp

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth: 
            return authenticate()

        elif not check_auth(auth.username, auth.password):
            return authenticate()
        return f(*args, **kwargs)

    return decorated

接下來(lái)只需要給路由增加@require_auth修飾器就可以在請(qǐng)求之前進(jìn)行認(rèn)證了:

@app.route('/secrets')
@requires_auth
def api_hello():
    return "Shhh this is top secret spy stuff!"

現(xiàn)在吭露,如果沒(méi)有通過(guò)認(rèn)證的話吠撮,響應(yīng)如下所示:

GET /secrets
HTTP/1.0 401 UNAUTHORIZED
WWW-Authenticate: Basic realm="Example"
{
  "message": "Authenticate."
}

curl通過(guò)-u選項(xiàng)來(lái)指定HTTP basic authentication,使用-v選項(xiàng)打印請(qǐng)求頭:

curl -v -u "admin:secret" http://127.0.0.1:5000/secrets

響應(yīng)結(jié)果如下:

GET /secrets Authorization: Basic YWRtaW46c2VjcmV0
Shhh this is top secret spy stuff!

Flask使用MultiDict來(lái)存儲(chǔ)頭部信息讲竿,為了給客戶端展示不同的認(rèn)證機(jī)制泥兰,可以給header添加更多的WWW-Autheticate。

resp.headers['WWW-Authenticate'] = 'Basic realm="Example"'resp.headers.add('WWW-Authenticate', 'Bearer realm="Example"')

調(diào)試與日志

通過(guò)設(shè)置debug=True來(lái)開啟調(diào)試信息:

app.run(debug=True)

使用Python的logging模塊可以設(shè)置日志信息:

import logging
file_handler = logging.FileHandler('app.log')
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)

@app.route('/hello', methods = ['GET'])
def api_hello():
    app.logger.info('informing')
    app.logger.warning('warning')
    app.logger.error('screaming bloody murder!')

    return "check your logs\n"

稍后繼續(xù)
參考:http://python.jobbole.com/85390/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末题禀,一起剝皮案震驚了整個(gè)濱河市鞋诗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌迈嘹,老刑警劉巖削彬,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件全庸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡融痛,警方通過(guò)查閱死者的電腦和手機(jī)壶笼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雁刷,“玉大人覆劈,你說(shuō)我怎么就攤上這事“踩” “怎么了墩崩?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)侯勉。 經(jīng)常有香客問(wèn)我鹦筹,道長(zhǎng),這世上最難降的妖魔是什么址貌? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任铐拐,我火速辦了婚禮,結(jié)果婚禮上练对,老公的妹妹穿的比我還像新娘遍蟋。我一直安慰自己,他們只是感情好螟凭,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布虚青。 她就那樣靜靜地躺著,像睡著了一般螺男。 火紅的嫁衣襯著肌膚如雪棒厘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天下隧,我揣著相機(jī)與錄音奢人,去河邊找鬼。 笑死淆院,一個(gè)胖子當(dāng)著我的面吹牛何乎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播土辩,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼支救,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拷淘?” 一聲冷哼從身側(cè)響起搂妻,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辕棚,沒(méi)想到半個(gè)月后欲主,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邓厕,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年扁瓢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了详恼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡引几,死狀恐怖昧互,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伟桅,我是刑警寧澤敞掘,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站楣铁,受9級(jí)特大地震影響玖雁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盖腕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一赫冬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溃列,春花似錦劲厌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至雅任,卻和暖如春风范,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背椿访。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工乌企, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虑润,地道東北人成玫。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拳喻,于是被迫代替她去往敵國(guó)和親哭当。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理冗澈,服務(wù)發(fā)現(xiàn)钦勘,斷路器,智...
    卡卡羅2017閱讀 134,719評(píng)論 18 139
  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停亚亲,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,191評(píng)論 22 257
  • 本文首載于 Gevin的博客 基于一些不錯(cuò)的RESTful開發(fā)組件彻采,可以快速的開發(fā)出不錯(cuò)的RESTful API腐缤,...
    Gevin閱讀 11,923評(píng)論 6 111
  • 一萬(wàn)字了,不知不覺(jué)堅(jiān)持了一周多記日記肛响。(9天突破10k字) 萬(wàn)事開頭難岭粤,其中數(shù)次晚上沒(méi)有動(dòng)力寫下去,由于很多負(fù)面因...
  • 現(xiàn)在做裝修的競(jìng)爭(zhēng)是越來(lái)越激烈了特笋,就以成都為例子剃浇,各種大大小小的裝修公司就太多了,還不算上路邊的一些游擊隊(duì)猎物。裝修一般...
    打不死的小小強(qiáng)閱讀 1,241評(píng)論 0 1