快速入门 基于Flask实现Restful风格API

使用POSTMAN测试

早在一个笔记应用引出的全栈工程师的能力锻炼这篇文章中我就提到过应该尽量去编写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约定的状态信息即可!

在下面代码中,对函数的返回进行了简单的封装

下面代码中的 fullResponsestatusResponse 来源于自定义的模块 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插件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,333评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,812评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,016评论 0 246
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,293评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,650评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,788评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,003评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,741评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,462评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,681评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,168评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,528评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,169评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,119评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,902评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,846评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,719评论 2 274

推荐阅读更多精彩内容

  • 一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式。”但是在要求详细讲述它所提出的各个约束,以及如...
    时待吾阅读 3,339评论 0 19
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,103评论 18 139
  • 本文首载于 Gevin的博客 基于一些不错的RESTful开发组件,可以快速的开发出不错的RESTful API,...
    Gevin阅读 11,860评论 6 111
  • 22年12月更新:个人网站关停,如果仍旧对旧教程有兴趣参考 Github 的markdown内容[https://...
    tangyefei阅读 35,132评论 22 257
  • 蝶爸蝶妈们: 您们好。 时光在雨季里穿行,来不及日日再见。我们真的在不知不觉里,走了一周。 ...
    小蝴蝶杨宁阅读 300评论 0 1