天天看點

Python的Flask架構開發RESTful API

web架構選擇

  • Django,流行但是笨重,還麻煩,人生苦短,肯定不選
  • web.py,輕量,但據說作者仙逝無人維護,好吧,先pass
  • tornado,據說倡導自己造輪子,雖然是facebook開源的吧,但聽到這個,就算了吧
  • flask,輕量,流行,可以自己定義

安裝flask

pip install flask
      

flask前端模闆引擎預設是jinja2,是以我們還需要安裝jinja2

pip install jinja2
      

hello world

from flask import Flask
from flask import request

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def home():
return '<h1>hello world</h1>'



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

運作python app.py,Flask自帶的Server在端口5000上監聽:

打開浏覽器,輸入首頁位址http://localhost:5000/:

會出現hello world

簡單的RESTful實作

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# by vellhe 2017/7/9
from flask import Flask, abort, request, jsonify

app = Flask(__name__)

# 測試資料暫時存放
tasks = []

@app.route('/add_task/', methods=['POST'])
def add_task():
if not request.json or 'id' not in request.json or 'info' not in request.json:
abort(400)
task = {
'id': request.json['id'],
'info': request.json['info']
    }
tasks.append(task)
return jsonify({'result': 'success'})


@app.route('/get_task/', methods=['GET'])
def get_task():
if not request.args or 'id' not in request.args:
        # 沒有指定id則傳回全部
return jsonify(tasks)
else:
task_id = request.args['id']
task = filter(lambda t: t['id'] == int(task_id), tasks)
return jsonify(task) if task else jsonify({'result': 'not found'})


if __name__ == "__main__":
    # 将host設定為0.0.0.0,則外網使用者也可以通路到這個服務
app.run(host="0.0.0.0", port=8383, debug=True)

      

驗證結果

image.png

以上是通過最原始的方式實作,沒有使用flask的RESTful擴充庫

使用flask的RESTful擴充庫 flask-restful

安裝Flask-RESTful庫:

pip install flask-restful
      

demo

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# by vellhe 2017/7/9
from flask import Flask
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)

TODOS = {
'todo1': {'task': 'build an API'},
'todo2': {'task': '哈哈哈'},
'todo3': {'task': 'profit!'},
}


def abort_if_todo_doesnt_exist(todo_id):
if todo_id not in TODOS:
abort(404, message="Todo {} doesn't exist".format(todo_id))


parser = reqparse.RequestParser()
parser.add_argument('task')


# # 操作(put / get / delete)單一資源Todo
# shows a single todo item and lets you delete a todo item
class Todo(Resource):
def get(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
return TODOS[todo_id]

def delete(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return '', 204

def put(self, todo_id):
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task, 201


# # 操作(post / get)資源清單TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
def get(self):
return TODOS

def post(self):
args = parser.parse_args()
todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
todo_id = 'todo%i' % todo_id
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201



# 設定路由
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')

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

(1)引入需要的庫名、函數、變量等,并做簡單的Application初始化:

from flask import Flask
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)
      

(2)定義我們需要操作的資源類型(都是json格式的):

TODOS = {
'todo1': {'task': 'build an API'},
'todo2': {'task': '哈哈哈'},
'todo3': {'task': 'profit!'},
}
      

(3)Flask-RESTful提供了一個用于參數解析的RequestParser類,類似于Python中自帶的argparse類,可以很友善的解析請求中的-d參數,并進行類型轉換。

parser = reqparse.RequestParser()
parser.add_argument('task')
      

(4)我們觀察标準的API接口,這裡的接口可以分為兩類:帶有item_id的,和不帶有item_id的。前者是操作單一資源,後者是操作資源清單或建立一個資源。

從操作單一資源開始,繼承Resource類,并添加put / get / delete方法:

class Todo(Resource):
def get(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
return TODOS[todo_id]

def delete(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return '', 204

def put(self, todo_id):
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task, 201
      

(5)繼續操作資源清單,繼承Resource類,并添加get / post方法:

class TodoList(Resource):
def get(self):
return TODOS

def post(self):
args = parser.parse_args()
todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
todo_id = 'todo%i' % todo_id
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201
      

(6)資源操作類定義完畢之後,需要設定路由,即告訴Python程式URL的對應關系。

api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')