博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用 Flask 来写个轻博客 (35) — 使用 Flask-RESTful 来构建 RESTful API 之四
阅读量:6627 次
发布时间:2019-06-25

本文共 7810 字,大约阅读时间需要 26 分钟。

目录

前文列表

POST 请求

前三篇博文介绍了如果实现 GET 请求, 接下来继续实现 POST 创建数据请求.

  • 执行 Create 操作, 就肯定少不了要向服务端传入数据. 所以第一步当然就是定义解析器了.
    vim jmilkfansblog/controllers/flask_restful/parsers.py
from flask.ext.restful import reqparsepost_post_parser = reqparse.RequestParser()post_post_parser.add_argument(    'title',    type=str,    required=True,    help='Title is required!')post_post_parser.add_argument(    'text',    type=str,    required=True,    help='Text is required!')post_post_parser.add_argument(    'tags',    type=str,    action='append')post_post_parser.add_argument(    'token',    type=str,    required=True,    help='Auth Token is required to create posts.')

NOTE 1: add_argument 的关键字参数 action='append' 指定了传入的参数会转换为以字典为元素的列表数据类型. 这是为了便于创建 post.tags 对象.

NOTE 2: 定义 token 参数是为了后期的身份认证做准备

  • 在资源来 PostApi 中实现 post()方法
    vim jmilkfansblog/controllers/flask_restful/posts.py
class PostApi(Resource):    """Restful API of posts resource."""    ...        def post(self, post_id=None):        """Can be execute when receive HTTP Method `POST`.        """        if post_id:            abort(400)        else:            args = parsers.post_post_parser.parse_args(strict=True)            new_post = Post()            new_post.title = args['title']            new_post.date = datetime.datetime.now()            new_post.text = args['text']            new_post.user = user            if args['tags']:                for item in args['tags']:                    tag = Tag.query.filter_by(name=item).first()                    # If the tag already exist, append.                    if tag:                        new_post.tags.append(tag)                    # If the tag not exist, create the new one.                    # Will be write into DB with session do.                    else:                        new_tag = Tag()                        new_tag.name = item                        new_post.tags.append(new_tag)        db.session.add(new_post)        db.session.commit()        return (new_post.id, 201)

NOTE 1: post() 返回了一个 Tuple 类型对象, 第二个元素会作为 Response Hander 中的 HTTP status_int 状态码.

身份认证

需要注意的是, 对外开发的 RESTful API 一定要非常注重安全, 所有从外部对数据库的写入操作请求都必须进行身份认证.

身份认证的功能我们仍然可以由 Flask-Login 来支持, 但很明显的, 这并不符合 REST 的无状态约束. 所以我们在这里引入 Token 的概念, 外部请求如果希望通过 RESTful API 执行写数据库操作时, 必须携带用户登录信息, 通过身份认证之后, 再由服务端发放一段时间内有效的 Token. 身份认证在整个项目中也应当作为一种资源来定义.

  • 首先还是定义 auth 解析器来接受用户信息
    vim jmilkfansblog/controllers/flask_restful/parsers.py
######################################################### User's HTTP Request Parser########################################################user_post_parser = reqparse.RequestParser()user_post_parser.add_argument(    'username',    type=str,    required=True,    help='Username is required!')user_post_parser.add_argument(    'password',    type=str,    required=True,    help='Password is required!')

NOTE : auth 的解析器只需要用户名和密码两个参数.

  • 创建认证资源 auth 的资源类
    vim jmilkfansblog/controllers/flask_restful/auth.py
from itsdangerous import TimedJSONWebSignatureSerializer as Serializerfrom flask import abort, current_appfrom flask.ext.restful import Resourcefrom jmilkfansblog.controllers.flask_restful import parsersfrom jmilkfansblog.db.sqlalchemy.models import Userclass AuthApi(Resource):    """Restful api of Auth."""    def post(self):        """Can be execute when receive HTTP Method `POST`."""        args = parsers.user_post_parser.parse_args()        user = User.query.filter_by(username=args['username']).first()        # Check the args['password'] whether as same as user.password.        if user.check_password(args['password']):            # serializer object will be saved the token period of time.            serializer = Serializer(                current_app.config['SECRET_KEY'],                expires_in=600)            return {
'token': serializer.dumps({
'id': user.id})} else: abort(401)

NOTE 1: Token 使用 Python 内建的 itsdangerous 库来实现, itsdangerous.TimedJSONWebSignatureSerializer() 的第一个参数需要传入 app 对象的私钥, 该私钥在之前的 Flask-WTForm 已经定义在 config.py 中了. 第二个参数定义了 Token 的有效时间.

NOTE 2: 我们使用 post() 方法来实现用户身份验证和发放 Token

  • 定义资源 auth 的路由
    vim jmilkfansblog/__init__.py
def create_app(object_name):    ...    restful_api.add_resource(        AuthApi,        '/api/auth',        endpoint='restful_api_auth')    restful_api.init_app(app)
  • 为 User 对象实现 Token 验证方法
    需要注意的是: 这个 Token 应该是有时限的, 如果永久生效则会非常危险. 所以我们还需要对有时限的 Token 进行校验, 确定其没有失效.
    vim jmilkfansblog/models.py
...class User(db.Model):    """Represents Proected users."""    ...    @staticmethod    @cache.memoize(60)    def verify_auth_token(token):        """Validate the token whether is night."""        serializer = Serializer(            current_app.config['SECRET_KEY'])        try:            # serializer object already has tokens in itself and wait for             # compare with token from HTTP Request /api/posts Method `POST`.            data = serializer.loads(token)        except SignatureExpired:            return None        except BadSignature:            return None        user = User.query.filter_by(id=data['id']).first()        return user

NOTE: 使用 itsdangerous.TimedJSONWebSignatureSerializer.loads() 来进行 Token 验证, 如果验证失败的话, 我们直接返回 None

  • 现在我们拥有了生成 Token 和验证 Token 的支撑, 最后我们在 PostApi.post() 中加入 Token 验证机制.

    vim jmilkfansblog/controllers/flask_restful/posts.py

def post(self, post_id=None):        """Can be execute when receive HTTP Method `POST`.        """        if post_id:            abort(400)        else:            args = parsers.post_post_parser.parse_args(strict=True)            # Validate the user identity via token(/api/auth POST).            # Will be create the post(/api/posts POST), if pass with validate token.            user = User.verify_auth_token(args['token'])            if not user:                abort(401)        ...

测试

  • 不加 Token 的 POST 请求
(env) jmilkfan@JmilkFan-Devstack:/opt/JmilkFan-s-Blog$ curl -d "title=Just Test POST" -d "text=Hello world" -d "tags=Python" http://localhost:8089/api/posts{    "message": {        "token": "Auth Token is required to create posts."    }}
  • 创建 Token
(env) jmilkfan@JmilkFan-Devstack:/opt/JmilkFan-s-Blog$ curl -d "username=
" -d "password=
" http://localhost:8089/api/auth{
"token": "eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ4MzM1MjkwNSwiaWF0IjoxNDgzMzUyMzA1fQ.eyJpZCI6IjY1Y2I5NzkyLWI4NzYtNDllNy1iMmM1LTQ2NDY4NjI0MTk5ZSJ9.hYpczUEZUalgzutyyIViheBd_jnnCmegvp4sazHIEoA"}
  • 加 Token 的 POST 请求
(env) jmilkfan@JmilkFan-Devstack:/opt/JmilkFan-s-Blog$ curl -d "title=Just Test" -d "text=Hello" -d "tags=Python" -d "token=eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ4MzM1MjkwNSwiaWF0IjoxNDgzMzUyMzA1fQ.eyJpZCI6IjY1Y2I5NzkyLWI4NzYtNDllNy1iMmM1LTQ2NDY4NjI0MTk5ZSJ9.hYpczUEZUalgzutyyIViheBd_jnnCmegvp4sazHIEoA" http://localhost:8089/api/posts"1746b650-bcab-436b-82ab-7411e252b576"

数据库记录 :

mysql> select * from posts;+--------------------------------------+-----------+-----------------+---------------------+--------------------------------------+| id                                   | title     | text            | publish_date        | user_id                              |+--------------------------------------+-----------+-----------------+---------------------+--------------------------------------+| 1746b650-bcab-436b-82ab-7411e252b576 | Just Test | Hello           | NULL                | 65cb9792-b876-49e7-b2c5-46468624199e || 1af8f334-c9ac-4eba-bdca-4dda597aba70 | 333333333 | 

22222

| 2016-12-17 22:39:16 | 65cb9792-b876-49e7-b2c5-46468624199e || 29bab6a0-6a0f-48f1-a088-6c271cebe906 | 222222 |

222222

| 2016-12-27 22:35:00 | 65cb9792-b876-49e7-b2c5-46468624199e || 9c25d00e-49a7-4369-ac83-c0aca046ba73 | Just Test | Hello | NULL | 65cb9792-b876-49e7-b2c5-46468624199e |+--------------------------------------+-----------+-----------------+---------------------+--------------------------------------+

转载地址:http://hfhpo.baihongyu.com/

你可能感兴趣的文章
基于jCOM搭建Java-微软信息桥梁(上)
查看>>
js 函数定义三种方式
查看>>
mysql 得到重复的记录
查看>>
亦步亦趋在CentOS 6.4下安装Oracle 11gR2(x64)
查看>>
ACM-SG函数之S-Nim——hdu1536 hdu1944 poj2960
查看>>
JavaScript大杂烩5 - JavaScript对象的若干问题
查看>>
SQLServer User and Login Tips
查看>>
IOS开发之——使用Segue在StoryBoard之间切换
查看>>
深入分析 Java I/O 的工作机制
查看>>
TortoiseSVN使用简介
查看>>
*Hdu 1026-Ignatius and the Princess I
查看>>
Liferay 6.1开发学习
查看>>
教会你如何编写makefile文件
查看>>
OA系统权限管理设计(转载)
查看>>
Xamarin.Android活动的生命周期
查看>>
完美C++(第5版)(双色)
查看>>
android 从assets和res中读取文件
查看>>
atittit.表单验证的实现方式以及原理本质以及选型以及自定义兼容easyui dsl规则的表单验证...
查看>>
myEclipse svn 插件安装
查看>>
JSP动作--JSP有三种凝视方式
查看>>