Flask中flask-restful的应用(十一)
REST又被称为表征性状态传输,是指在客户端与服务端之间传输信息的一种方式,在WEB的应用程序
中,一般都是基于HTTP的REST服务,这样的好处是可以使用应用层的协议来方便的实现客户端与服务端
之间的基本通信。REST它与语言无关,它更多的是制定了客户端与服务端之间的交互规则,即使在目前主
流的微服务架构中,也是使用了轻量级的通信方式,也就是基于HTTP的REST的交互方式,在这样的一种
交互方式中,一般的数据格式会使用JSON或者是XML的。在HTTP的协议中,客户端与服务端的之间的交互,
首先不会去太多的关心底层网络传输层的协议,更多关注的是应用层客户端与服务端之间的交互请求。见如下:

客户端向服务端发送Request请求后,客户端会响应回复给客户端,在这样的一个交互模式中,由于HTTP的协议
它是无状态的协议,所以如果服务端需要记住客户端的某些信息的话,需要应用到cookie或者是session的的技术。
但是在这里我们并不需要刻意的去关注这些技术,更多需要关心的是当客户端发送请求后,由于网络超时等异常
情况可能会导致客户端的请求堵塞,基于同步通信的模式始终存在这样的缺陷。当然在REST中,还有如下几点要
求,分别是:
1、接口是基于资源的。
2、服务端返回的数据基本都是JSON格式的数据,客户端需要关键的数据需要对JSON序列化处理后得到自己想要
的信息。
3、API提供的每个资源信息,都必须有统一的格式,比如要么都必须是JSON格式或者是XML格式,不能是这个是
JSON格式另外一个是XML的格式。
在Flask的应用程序中,首先需要安装flask-restful,安装的命令为:
pip install flask-restful
在这里首先简单的写一个 "Hello World"的GET请求,请求地址是/index/,见实现的源码:
#!/usr/bin/env python # -*-coding:utf-8 -*- from flask import Flask,jsonify from flask_restful import Api,Resource app=Flask(__name__) api=Api(app) class IndexView(Resource): def get(self): return {'Hello':'World'} api.add_resource(IndexView,'/index/',endpoint='index') if __name__ == '__main__': app.run(debug=True)
执行应用程序,然后在浏览器中访问http://localhost:5000/index/,就会输出基于JSON格式的信息,见如下:

当然作为一个接口来说,它是有请求参数的,比如一个登录的接口,它的请求参数是username和password,下面
继续实现POST的请求方法,见源码:
#!/usr/bin/env python # -*-coding:utf-8 -*- from flask import Flask,jsonify from flask_restful import Api,Resource,reqparse app=Flask(__name__) api=Api(app) class IndexView(Resource): def get(self): return {'Hello':'World'} class LoginView(Resource): def get(self): return {'status':0,'msg':'ok','data':'this is a login page'} def post(self): parser=reqparse.RequestParser() parser.add_argument('username',type=str,help='登录账户不能为空',required=True) parser.add_argument('password',type=str,help='账户密码不能为空',required=True) return jsonify(parser.parse_args()) api.add_resource(IndexView,'/index/',endpoint='index') api.add_resource(LoginView,'/login/',endpoint='login') if __name__ == '__main__': app.run(debug=True)
在postman中模拟POST的请求,见POSTMAN的请求截图:

在如上的代码中,关于add_argument()的方法的形式参数都是有很严格的讲究的,主要为:
type:规定了请求参数的类型
required:如果是True的话,就意味着参数是必须填写的
htlp:指参数验证没有通过后返回的错误信息
choices:对一个请求参数指定它的范围,如性别,只能使用男或者女,如果是其他的信息,就会不允许
这里主要看三个维度,分别是请求参数类型,请求参数是否为空,和chioces的校验,见修改后的源码:
class LoginView(Resource): def get(self): return {'status':0,'msg':'ok','data':'this is a login page'} def post(self): parser=reqparse.RequestParser() parser.add_argument('username', type=str, required=True, help='您的用户名验证错误') parser.add_argument('password',type=str,help='账户密码不能为空') parser.add_argument('age',type=int,help='年龄必须为正正数') parser.add_argument('sex',type=str,help='性别只能是男或者女',choices=['女','男']) args=parser.parse_args() return jsonify(args)
先看用户名为空返回的错误信息:

接着验证参数的数据类型,请求参数年龄为字符串,看后台的判断信息:

最后校验choices,依据代码,性别只能是男或者是女,那么这里把请求参数为false,看程序的验证:

在flask-restful中,也可以提前定义好接口返回的参数,比如接口返回username,password以及一些想要返回的信息,
这里就会使用到marshall_with,比如这里写一个简单的案例,期望返回业务状态码,msg消息,和data里面的数据,见
实现的源码:
#!/usr/bin/env python # -*-coding:utf-8 -*- from flask import Flask,jsonify,Response,make_response,abort,url_for from flask_restful import Api,Resource,reqparse,fields,marshal_with app=Flask(__name__) api=Api(app) class Login(object): def __init__(self,username,password,isLogin): self.username=username self.password=password self.isLogin=isLogin login=Login('wuya','admin',True) class LoginView(Resource): resources={ 'username':fields.String, 'password':fields.String, 'isLogin':fields.Boolean } @marshal_with(resources) def get(self): return login api.add_resource(LoginView,'/login/',endpoint='login') #请求上下文 with app.test_request_context(): print(url_for('login')) if __name__ == '__main__': app.run(debug=True)
在浏览器中再次访问 http://127.0.0.1:5000/login/,就会显示Login实例化后的对象的实例,见如下截图:

下面就结合flask-restful写一个具体的案例,主要写一个图书管理系统的API,主要如下:
显示所有的图书信息 查询某个书的具体信息 修改书的信息 删除书的信息,在一个字典里面初始化部分需要测试的数据,具体实现的代码如下:
#!/usr/bin/env python # -*-coding:utf-8 -*- from flask import * from flask_httpauth import HTTPBasicAuth from flask_restful import reqparse,Api,Resource app=Flask(__name__) api=Api(app) auth=HTTPBasicAuth() books=[ { 'id':1, 'author':'无涯', 'name':'Python自动化测试实战', "done":True }, { 'id': 2, "aurhor":"无涯", 'name': 'Python测试开发实战', "done":False } ] @app.route('/api/v1/books',methods=['GET']) def get_books(): '''返回所有的书籍''' return jsonify({'books':books}) @app.route('/api/v1/books/<int:book_id>',methods=['GET']) def get_book(book_id): '''依据ID返回具体的书的信息''' book=list(filter(lambda t: t['id'] == book_id, books)) if len(book)==0: abort(404) return jsonify({'book':book[0]}) @app.errorhandler(404) def not_found(error): '''对404情况的处理,希望错误得到的是404的错误''' return make_response(jsonify({'error':'Not Found'}),404) @app.route('/api/v1/books',methods=['POST']) def create_book(): if not request.json or not 'author' in request.json: abort(400) book={ 'id':books[-1]['id']+1, # 'author':request.json['author'], 'author':request.json.get('author'), 'name':request.json.get('name'), 'done':False } books.append(book) return jsonify({'book':book}),201 @app.route('/api/v1/books/<int:book_id>',methods=['POST']) def update_book(book_id): book=list(filter(lambda t: t['id'] == book_id, books)) if len(book)==0: abort(404) if not request.json: abort(400) if 'author' not in request.json and 'name' not in request.json: abort(400) if 'done' in request.json and type(request.json['done']) is not bool: abort(400) book[0]['author']=request.json.get('author',book[0]['author']) book[0]['name']=request.json.get('name',book[0]['name']) book[0]['done']=request.json.get('done',book[0]['done']) return jsonify({'result':book[0]}) @app.route('/api/v1/books/<int:book_id>',methods=['DELETE']) def del_book(book_id): book=list(filter(lambda t:t['id']==book_id,books))if len(book)==0: abort(404) books.remove(book[0]) return jsonify({'result':True}) if __name__ == '__main__': app.run(debug=True)
在上面的案例代码中并没有增加认证,在认证中主要可以分为基本认证,常规认证,和基本认证,这里主要使用基本认证,也
就是说,需要访问API,必须经过认证才可以访问的。见添加认证后的信息代码:
#!/usr/bin/env python # -*-coding:utf-8 -*- from flask import * from flask_httpauth import HTTPBasicAuth from flask_restful import reqparse,Api,Resource app=Flask(__name__) api=Api(app) auth=HTTPBasicAuth() books=[ { 'id':1, 'author':'无涯', 'name':'Python自动化测试实战', "done":True }, { 'id': 2, "aurhor":"无涯", 'name': 'Python测试开发实战', "done":False } ] @auth.get_password def get_password(username): if username=='wuya': return 'admin' @auth.error_handler def authoriazed(): return make_response(jsonify({'error':'请进行认证'}),403) @app.route('/api/v1/books',methods=['GET']) @auth.login_required def get_books(): '''返回所有的书籍''' return jsonify({'books':books}) @app.route('/api/v1/books/<int:book_id>',methods=['GET']) @auth.login_required def get_book(book_id): '''依据ID返回具体的书的信息''' book=list(filter(lambda t: t['id'] == book_id, books)) if len(book)==0: abort(404) return jsonify({'book':book[0]}) @app.errorhandler(404) def not_found(error): '''对404情况的处理,希望错误得到的是404的错误''' return make_response(jsonify({'error':'Not Found'}),404) @app.route('/api/v1/books',methods=['POST']) @auth.login_required def create_book(): if not request.json or not 'author' in request.json: abort(400) book={ 'id':books[-1]['id']+1, # 'author':request.json['author'], 'author':request.json.get('author'), 'name':request.json.get('name'), 'done':False } books.append(book) return jsonify({'book':book}),201 @app.route('/api/v1/books/<int:book_id>',methods=['POST']) @auth.login_required def update_book(book_id): book=list(filter(lambda t: t['id'] == book_id, books)) if len(book)==0: abort(404) if not request.json: abort(400) if 'author' not in request.json and 'name' not in request.json: abort(400) if 'done' in request.json and type(request.json['done']) is not bool: abort(400) book[0]['author']=request.json.get('author',book[0]['author']) book[0]['name']=request.json.get('name',book[0]['name']) book[0]['done']=request.json.get('done',book[0]['done']) return jsonify({'result':book[0]}) @app.route('/api/v1/books/<int:book_id>',methods=['DELETE']) @auth.login_required def del_book(book_id): book=list(filter(lambda t:t['id']==book_id,books)) if len(book)==0: abort(404) books.remove(book[0]) return jsonify({'result':True}) if __name__ == '__main__': app.run(debug=True)
下面通过类的方式来修改代码,让API增加完整,而且通过类的方式,API更加好容易管理,具体代码见如下:
#!/usr/bin/env python # -*-coding:utf-8 -*- from flask import Flask,redirect,render_template,url_for,request,jsonify,abort,make_response from flask_restful import Resource,Api from flask_httpauth import HTTPBasicAuth app=Flask(__name__) api=Api(app=app) auth=HTTPBasicAuth() @auth.get_password def get_password(name): if name=='wuya': return 'admin' @auth.error_handler def authorized(): return make_response(jsonify({'error':'请认证'}),401) books=[ { 'id':1, 'author':'无涯', 'name':'Python自动化测试实战', "done":True }, { 'id': 2, "aurhor":"无涯", 'name': 'Python测试开发实战', "done":False } ] class BooksApi(Resource): decorators=[auth.login_required] def get(self): return jsonify(books) class BookApi(Resource): def get(self,book_id): book=list(filter(lambda t:t['id']==book_id,books)) print(book) if len(book)==0: abort(400) else: return jsonify(book) def put(self,book_id): book=list(filter(lambda t:t['id']==book_id,books)) if len(book)==0: abort(404) elif not request.json: abort(400) elif 'author' not in request.json: abort(400) elif 'done' not in request.json and type(request.json['done']) is not bool: abort(400) book[0]['author']=request.json.get('author',book[0]['author']) book[0]['name'] = request.json.get('name', book[0]['name']) book[0]['done'] = request.json.get('done', book[0]['done']) return jsonify({'status':0,'msg':'ok','datas':book}) def delete(self,book_id): book = list(filter(lambda t: t['id'] == book_id, books)) if len(book)==0: abort(404) books.remove(book[0]) return jsonify({'status':1001,'msg':'删除成功'}) api.add_resource(BooksApi,'/v1/api/books',endpoint='/v1/api/books') api.add_resource(BookApi,'/v1/api/book/<int:book_id>') if __name__ == '__main__': app.run(debug=True)
通过类的方式修改代码,关于它的API调用add_resource来添加进去,鉴权部分与函数式的编程方式还是有点差异。

浙公网安备 33010602011771号