搭建一個(gè)簡(jiǎn)單的分布式系統(tǒng)(3)

有關(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政基。

安裝 Flask-HTTPAuth

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')
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市珊擂,隨后出現(xiàn)的幾起案子圣勒,更是在濱河造成了極大的恐慌费变,老刑警劉巖摧扇,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異挚歧,居然都是意外死亡扛稽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)滑负,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)在张,“玉大人,你說(shuō)我怎么就攤上這事矮慕“镓遥” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵痴鳄,是天一觀的道長(zhǎng)瘟斜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)痪寻,這世上最難降的妖魔是什么螺句? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮橡类,結(jié)果婚禮上蛇尚,老公的妹妹穿的比我還像新娘。我一直安慰自己顾画,他們只是感情好取劫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布匆笤。 她就那樣靜靜地躺著,像睡著了一般勇凭。 火紅的嫁衣襯著肌膚如雪疚膊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天虾标,我揣著相機(jī)與錄音寓盗,去河邊找鬼。 笑死璧函,一個(gè)胖子當(dāng)著我的面吹牛傀蚌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蘸吓,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼善炫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了库继?” 一聲冷哼從身側(cè)響起箩艺,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宪萄,沒(méi)想到半個(gè)月后艺谆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拜英,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年静汤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片居凶。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虫给,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出侠碧,到底是詐尸還是另有隱情抹估,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布弄兜,位于F島的核電站药蜻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏挨队。R本人自食惡果不足惜谷暮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盛垦。 院中可真熱鬧湿弦,春花似錦、人聲如沸腾夯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至班利,卻和暖如春饥漫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背罗标。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工庸队, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闯割。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓彻消,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親宙拉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宾尚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)谢澈,斷路器煌贴,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 一說(shuō)到REST,我想大家的第一反應(yīng)就是“啊锥忿,就是那種前后臺(tái)通信方式牛郑。”但是在要求詳細(xì)講述它所提出的各個(gè)約束缎谷,以及如...
    時(shí)待吾閱讀 3,430評(píng)論 0 19
  • 憶起往惜 甬時(shí)晴處留下幾滴雨 打傘不及濕身衣 白巖山上 群山隱云霧不散 二七風(fēng)車(chē)悠悠轉(zhuǎn) 幾片油菜花田 路揚(yáng)多殘?jiān)?...
    山水似獸閱讀 156評(píng)論 0 2
  • 聽(tīng)說(shuō)現(xiàn)在的小籠包很火鲫懒,滿文軍很想看一看嫩实,但身在監(jiān)獄不方便,老友尹相杰聞?dòng)嵖遥藥卓诩紫祝聊季?.....于是第二天...
    棉花兔閱讀 535評(píng)論 0 2
  • 一 天空是藍(lán)色的、眼睛是黑色的颂翼,但在小秋的眼睛里晃洒,它們?nèi)呛谏目穑拖褚沟耐队啊?小秋卻固執(zhí)的認(rèn)為,天空是藍(lán)色的球及,...
    粉蝕太平閱讀 246評(píng)論 0 0