早在一個筆記應用引出的全棧工程師的能力鍛煉這篇文章中我就提到過應該盡量去編寫API來實現一個web應用,在此之前也使用PHP實現了一個小的demo(具體參見用php實現一個app的API)。事實證明,越來越多的設備進入互聯網(物聯網時代的到來),我們需要更加優秀和耐用的API。于是,時隔半年,這一次我來展現給大家如何使用python的微框架flask,快速的實現一個Restful風格的API。
為什么是Restful?
必須有一種統一的機制,方便不同的前端設備與后端進行通信。這導致API構架的流行,RESTful API是目前比較成熟的一套互聯網應用程序的API設計理論。
工作原理
額,工作原理就是,客戶端發送request,服務端response一個json對象。
Restful Url的要求和在flask中的實現
在RESTful架構中,每個網址代表一種資源(resource),所以網址中不能有動詞,只能有名詞,而且所用的名詞往往與數據庫的表格名對應。一般來說,數據庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應該使用復數。
舉個例子:比如我們有一個數據資源,是一種對編程語言的描述,大概如下:
datas = [{'name': 'javascript', 'useto': 'web development'},
{'name': 'python', 'useto': 'do anything'},
{'name': 'php', 'useto': 'web development'},
{'name': 'c++', 'useto': 'web server'}]
上面定義了一個數組,數組內包含了一些字典,每一個數組元素其實就是對某一種編程語言的簡單描述。
當我們要獲取數據集合的時候就可以這么請求:
https://myservice.com/api/v1/languages
當我們要獲取一條數據的詳細信息的時候,可以這么請求:
https://myservice.com/api/v1/languages/python
請求的方法 (解釋了為什么RestfulAPI的url里面沒有動詞)
我覺得你應該受夠了類似于下列的請求url了:
http://test.com/getstudents.do
http://test.com/getinfobyid.action
http://test.com/updateinfobyid.action
說實話,這樣的url我也很討厭。所以我用REST呀!
RestFul對于資源的具體操作類型,由HTTP動詞表示。
常用的HTTP動詞有下面五個。
GET(SELECT):從服務器取出資源(一項或多項)。
POST(CREATE):在服務器新建一個資源。
PUT(UPDATE):在服務器更新資源(客戶端提供改變后的完整資源)。
PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性)。
DELETE(DELETE):從服務器刪除資源。
恩,相同的url,執行不同的動作,得到的結果是不同的,習慣了之后,你就會愛上這種設計。
如何實現 直接貼上代碼
首先,關于flask的知識,pocoo的文檔真心不錯,用上半個小時過一遍快速如么就基本能看懂下面的代碼了。
其次,下面的代碼對數據源(一個數組)進行了CRUD的操作,返回json對象。
json對象被封裝成兩種情況:
第一種帶數據和狀態信息的響應:
例如:
{
"data": [
{
"name": "javascript",
"useto": "web development"
},
{
"name": "python",
"useto": "do anything"
},
{
"name": "php",
"useto": "web development"
},
{
"name": "c++",
"useto": "web server"
}
],
"status": {
"code": 200,
"message": "OK all right."
}
}
當需要獲取資源的時候,我們返回一個data對應一個數組,同時返回狀態信息
第二種僅僅包含狀態信息的響應:
{
"status": {
"code": 404,
"message": "No result matched."
}
}
當僅僅執行增加、刪除和修改操作的時候,用戶關心的只是操作時否成功,僅需要返回對應的Restful約定的狀態信息即可!
在下面代碼中,對函數的返回進行了簡單的封裝
下面代碼中的
fullResponse
和statusResponse
來源于自定義的模塊restfultools
fullResponse
接受兩個參數,一個是自己定義的狀態字典,一個是數據集和(數組)
statusResponse
僅僅接收一個自定義的狀態字典
Flask主程序, rest.py
from flask import Flask, request, jsonify
from restfultools import * #這個是我自己寫的一個簡單的數據封裝工具(裝B的描述)
app = Flask(__name__)
#這是數據源
datas = [{'name': 'javascript', 'useto': 'web development'},
{'name': 'python', 'useto': 'do anything'},
{'name': 'php', 'useto': 'web development'},
{'name': 'c++', 'useto': 'web server'}]
#獲取所有的資源 注意我用了復數形式的url
@app.route('/languages')
def getAll():
return fullResponse(R200_OK, datas)
#根據name獲取資源中的某一個
@app.route('/language/<string:name>')
def getOne(name):
result = [data for data in datas if data['name'] == name]
if len(result) == 0:
return statusResponse(R404_NOTFOUND)
return fullResponse(R200_OK, result[0])
#POST請求,增加一項
@app.route('/language', methods=['POST'])
def addOne():
request_data = request.get_json()
if not 'name' in request_data or not 'useto' in request_data:
return statusResponse(R400_BADREQUEST)
name = request_data['name']
useto = request_data['useto']
datas.append({'name': name, 'useto': useto})
return statusResponse(R201_CREATED)
#PUT,PATCH 更新資源
#按照RestFul設計:
#PUT動作要求客戶端提供改變后的完整資源
#PATCH動作要求客戶端可以只提供需要被改變的屬性
#在這里統一使用PATCH的方法
@app.route('/language/<string:name>', methods=['PUT', 'PATCH'])
def editOne(name):
result = [data for data in datas if data['name'] == name]
if len(result) == 0:
return statusResponse(R404_NOTFOUND)
request_data = request.get_json()
if 'name' in request_data:
result[0]['name'] = request_data['name']
if 'useto' in request_data:
result[0]['useto'] = request_data['useto']
return statusResponse(R201_CREATED)
#DELETE刪除,沒什么好說的
@app.route('/language/<string:name>', methods=['DELETE'])
def delOne(name):
result = [data for data in datas if data['name'] == name]
if len(result) == 0:
return statusResponse(R404_NOTFOUND)
datas.remove(result[0])
return statusResponse(R204_NOCONTENT)
自定義的工具模塊 restfultools.py
#-*- coding: UTF-8 -*-
from flask import jsonify
# define statu_dics here
R200_OK = {'code': 200, 'message': 'OK all right.'}
R201_CREATED = {'code': 201, 'message': 'All created.'}
R204_NOCONTENT = {'code': 204, 'message': 'All deleted.'}
R400_BADREQUEST = {'code': 400, 'message': 'Bad request.'}
R403_FORBIDDEN = {'code': 403, 'message': 'You can not do this.'}
R404_NOTFOUND = {'code': 404, 'message': 'No result matched.'}
def fullResponse(statu_dic, data):
return jsonify({'status': statu_dic, 'data': data})
def statusResponse(statu_dic):
return jsonify({'status': statu_dic})
關于flask的版本: 0.11
運行的方法和之前有一點點不同:
export FLASK_APP = rest.py
flask run
悄悄的告訴你 以前的方法也支持,不過不推薦!
最后:API開發工具推薦:
PostMan : 一個google-chrome的插件
Json Formatter: 一個Json格式化工具 也是Google-Chrome插件