有關(guān)RESTful API的介紹隔崎,請(qǐng)參考這篇文章Flask REST
REST的六個(gè)特性:
- Client-Server:服務(wù)器端與客戶端分離卓嫂。
- Stateless(無(wú)狀態(tài)):每次客戶端請(qǐng)求必需包含完整的信息护锤,換句話說(shuō)钱贯,每一次請(qǐng)求都是獨(dú)立的吹截。
- Cacheable(可緩存):服務(wù)器端必需指定哪些請(qǐng)求是可以緩存的。
- Layered System(分層結(jié)構(gòu)):服務(wù)器端與客戶端通訊必需標(biāo)準(zhǔn)化鳖谈,服務(wù)器的變更并不會(huì)影響客戶端岁疼。
- Uniform Interface(統(tǒng)一接口):客戶端與服務(wù)器端的通訊方法必需是統(tǒng)一的。
- Code on demand(按需執(zhí)行代碼缆娃?):服務(wù)器端可以在上下文中執(zhí)行代碼或者腳本五续?
GET:檢索人物
POST:添加任務(wù)
PUT:更新存在人物
DELETE:刪除一個(gè)任務(wù)
** 不用擴(kuò)展模塊實(shí)現(xiàn)REST **
在views.py文件中添加兩個(gè)task列表,列表內(nèi)容放在內(nèi)存中龄恋,json數(shù)據(jù)格式
GET檢索任務(wù)
from flask import Flask, jsonify
tasks = [
{
'id': 1,
'title': u'Buy groceries',
'description': u'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': u'Learn Python',
'description': u'Need to find a good Python tutorial on the web',
'done': False
}
]
@flask_app.route('/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
這就調(diào)用了一個(gè)RESTful service方法疙驾。
如果覺(jué)得網(wǎng)頁(yè)測(cè)試太麻煩,可以使用curl命令郭毕,也可以使用firefox插件RESTeasy進(jìn)行測(cè)試
$ curl http://192.168.1.109:316/api/v1.0/tasks
{
"tasks": [
{
"description": "Milk, Cheese, Pizza, Fruit, Tylenol",
"done": false,
"id": 1,
"title": "Buy groceries"
},
{
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
}
]
}
帶有參數(shù)的REST方法
from flask import abort
@flask_app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
task = filter(lambda t: t['id'] == task_id, tasks)
if len(task) == 0:
abort(404)
return jsonify({'task': task[0]})
運(yùn)行結(jié)果
$ curl http://192.168.1.109:316/api/v1.0/tasks/2
{
"task": {
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
}
}
對(duì)404錯(cuò)誤進(jìn)行處理
from flask import make_response
@flask_app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 404)
POST:添加任務(wù)
### POST ###
@flask_app.route('/api/v1.0/tasks', methods=['POST'])
def create_task():
if not request.json or not 'title' in request.json:
abort(400)
task = {
'id': tasks[-1]['id'] + 1,
'title': request.json['title'],
'description': request.json.get('description', ""),
'done': False
}
tasks.append(task)
return jsonify({'task': task}), 201
@flask_app.errorhandler(400)
def bad_request(error):
return make_response(jsonify({'error': 'User defined Bad request'}), 400)
curl -i -H "Content-Type: application/json" -X POST \
-d '{"title":"Read a book"}' http://192.168.1.109:316/api/v1.0/tasks
如果使用原生版本的curl命令行提示符它碎,上面的命令會(huì)正確執(zhí)行。如果是在Windows下使用Cygwin bash版本的curl显押,需要將body部份添加雙引號(hào):
curl -i -H "Content-Type: application/json" -X POST -d "{"""title""":"""Read a book"""}" http://192.168.1.109:316/api/v1.0/tasks
PUT:更新任務(wù)
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['PUT'])
def update_task(task_id):
task = filter(lambda t: t['id'] == task_id, tasks)
if len(task) == 0:
abort(404)
if not request.json:
abort(400)
if 'title' in request.json and type(request.json['title']) != unicode:
abort(400)
if 'description' in request.json and type(request.json['description']) is not unicode:
abort(400)
if 'done' in request.json and type(request.json['done']) is not bool:
abort(400)
task[0]['title'] = request.json.get('title', task[0]['title'])
task[0]['description'] = request.json.get('description', task[0]['description'])
task[0]['done'] = request.json.get('done', task[0]['done'])
return jsonify({'task': task[0]})
$ curl -i -H "Content-Type: application/json" -X PUT -d '{"done":true}' http://192.168.1.109:316/api/v1.0/tasks/2
HTTP/1.1 200 OK
Server: nginx/1.4.6 (Ubuntu)
Date: Wed, 15 Jun 2016 13:01:52 GMT
Content-Type: application/json
Content-Length: 151
Connection: keep-alive
{
"task": {
"description": "Need to find a good Python tutorial on the web",
"done": true,
"id": 2,
"title": "Learn Python"
}
}
DELETE:刪除任務(wù)
@flask_app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
task = filter(lambda t: t['id'] == task_id, tasks)
if len(task) == 0:
abort(404)
tasks.remove(task[0])
return jsonify({'result': True})
返回URI
通過(guò)Flask的url_for模塊扳肛,獲取任務(wù)時(shí),返回uri值乘碑,避免了與其它功能的兼容挖息,拿到的是完整uri。
from flask import url_for
def make_public_task(task):
new_task = {}
for field in task:
if field == 'id':
new_task['uri'] = url_for('get_task', task_id=task['id'], _external=True)
else:
new_task[field] = task[field]
return new_task
RESTful web service的安全認(rèn)證
最簡(jiǎn)單的辦法是在web service中兽肤,只允許用戶名和密碼驗(yàn)證通過(guò)的客戶端連接套腹。在一個(gè)常規(guī)的web應(yīng)用中绪抛,應(yīng)該有登錄表單提交去認(rèn)證,同時(shí)服務(wù)器會(huì)創(chuàng)建一個(gè)會(huì)話過(guò)程去進(jìn)行通訊电禀。這個(gè)會(huì)話過(guò)程id會(huì)被存儲(chǔ)在客戶端的cookie里面幢码。不過(guò)這樣就違返了我們REST中無(wú)狀態(tài)的規(guī)則,因此尖飞,我們需求客戶端每次都將他們的認(rèn)證信息發(fā)送到服務(wù)器症副。
有兩種方法表單認(rèn)證方法去做,分別是 Basic 和 Digest政基。
pip install flask-httpauth
from flask.ext.httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
@auth.get_password
def get_password(username):
if username == 'ok':
return 'python'
return None
@auth.error_handler
def unauthorized():
return make_response(jsonify({'error': 'Unauthorized access'}), 401)
$ curl http://192.168.1.109:316/api/v1.0/tasks
{
"error": "Unauthorized access"
}
$ curl -u ok:python -i http://192.168.1.109:316/api/v1.0/tasks
HTTP/1.1 200 OK
修改返回的錯(cuò)誤代碼401贞铣。例如修改成403(”Forbidden“),網(wǎng)頁(yè)登錄就不會(huì)彈出驗(yàn)證對(duì)話框了沮明。
** 使用擴(kuò)展模塊flask.ext.restful實(shí)現(xiàn)REST **
為什么使用擴(kuò)展模塊實(shí)現(xiàn)REST
Flask-RESTful 是一個(gè)可以簡(jiǎn)化 APIs 的構(gòu)建的 Flask 擴(kuò)展辕坝,添加了快速構(gòu)建 REST APIs 的支持
from flask.ext import restful
api = restful.Api(flask_app)
class Task(restful.Resource):
def get(self):
return {'Wendy':'Simon'}
api.add_resource(Task, '/api/v1.0/ext/tasks', endpoint = 'user')