API与用户的通信协议总是使用https协议
应尽量将API部署在专用域名下
https://www.xialigang.com
如果确定API很简单,不会有进一步扩展,可以放在主域名下
https://www.xialigang.com/api/
应该将版本放入URL
https://www.xialigang.com/api/v1/
可以将版本放入http头信息中
说明
注意
示例
错误示例
https://127.0.0.1/api/v1/getStudents/
正确示例
https://127.0.0.1/api/v1/students/
方法 | 行为 | 例子 |
---|---|---|
GET | 获取所有资源 | http://127.0.0.1:5000/api/source |
GET | 获取指定资源 | http://127.0.0.1:5000/api/source/250 |
POST | 创建新的资源 | http://127.0.0.1:5000/api/source |
PUT | 更新指定资源 | http://127.0.0.1:5000/api/source/250 |
DELETE | 删除指定资源 | http://127.0.0.1:5000/api/source/250 |
DELETE | 删除所有资源 | http://127.0.0.1:5000/api/source/ |
状态码 | 请求方式 | 说明 |
---|---|---|
200 | get | OK 服务器成功返回资源 |
201 | post、put、patch | Created 用户新建或者修改资源成功 |
202 | * | Accepted 表示请求已经进入后台排队 |
204 | delete | No Content 用户删除资源成功 |
400 | post、put、patch | Bad Request 用户发出的请求有错误 |
401 | * | Unauthorized 用户没有权限(令牌、用户名、密码错误) |
403 | * | Forbidden 表示用户得到授权(与401相对),但是访问是被禁止的 |
404 | * | Not Found 请求针对的是不存在的资源 |
405 | * | Method Not Allowed 用户请求的方式不被允许 |
406 | get | Not Acceptable用户请求的格式不可得(比如用户请求json格式,但是只有xml格式) |
410 | get | Gone 用户请求的资源被永久删除,且不可在得到 |
422 | post、put、patch | Unprocessable Entity 创建对象时发生了验证错误 |
500 | * | Internal Server Error 服务器发生错误 |
如果状态码是4xx,就应该向用户返回错误信息,一般返回内容中以error作为键,错误信息作为值返回
{
<span class="hljs-string">"error"</span>: <span class="hljs-string">"参数有误"</span>
}
说明
返回的结果中提供了链接,链向其他API方法啊,需要让用户不查看文档(项目文档)就知道下一步该干什么
实例
地址
GET /students/<id>/
{
<span class="hljs-string">"name"</span>: <span class="hljs-string">"lucky"</span>,
<span class="hljs-string">"age"</span>: <span class="hljs-number">50</span>,
<span class="hljs-string">"link"</span>: <span class="hljs-string">"https://127.0.0.1/api/v1/test/"</span>
}
restful风格
{
<span class="hljs-string">"name"</span>: <span class="hljs-string">"lucky"</span>,
<span class="hljs-string">"age"</span>: <span class="hljs-number">50</span>,
<span class="hljs-string">"link"</span>: {
<span class="hljs-string">"rel"</span>: <span class="hljs-string">"collection 127.0.0.1:5000"</span>
<span class="hljs-string">"href"</span>: <span class="hljs-string">"127.0.0.1/api/v1/test/"</span>
<span class="hljs-string">"title"</span>: <span class="hljs-string">"测试界面"</span>
<span class="hljs-string">"type"</span>: <span class="hljs-string">"application/json"</span>
}
}
键
<span class="hljs-comment"># 测试数据</span>
posts = [
{
<span class="hljs-string">'id'</span>: <span class="hljs-number">1</span>,
<span class="hljs-string">'title'</span>: <span class="hljs-string">'Python语法'</span>,
<span class="hljs-string">'content'</span>: <span class="hljs-string">'别人都说python语法很简单,但是每次问题都出在语法上'</span>
},
{
<span class="hljs-string">'id'</span>: <span class="hljs-number">2</span>,
<span class="hljs-string">'title'</span>: <span class="hljs-string">'HTML'</span>,
<span class="hljs-string">'content'</span>: <span class="hljs-string">'不就是几个标签的问题嘛,但是最好细心点'</span>
}
]
获取所有资源
<span class="hljs-comment"># 获取资源列表</span>
<span class="hljs-meta">@app.route('/posts')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_posts_list</span><span class="hljs-params">()</span>:</span>
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'posts'</span>: posts})
获取指定资源
<span class="hljs-comment"># 获取指定资源</span>
<span class="hljs-meta">@app.route('/posts/<int:pid>')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_posts</span><span class="hljs-params">(pid)</span>:</span>
p = list(filter(<span class="hljs-keyword">lambda</span> p: p[<span class="hljs-string">'id'</span>] == pid, posts))
<span class="hljs-keyword">if</span> len(p) == <span class="hljs-number">0</span>:
abort(<span class="hljs-number">404</span>)
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'posts'</span>: p[<span class="hljs-number">0</span>]})
<span class="hljs-comment"># 添加新的资源</span>
<span class="hljs-meta">@app.route('/posts', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_posts</span><span class="hljs-params">()</span>:</span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> request.json <span class="hljs-keyword">or</span> <span class="hljs-string">'title'</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> request.json <span class="hljs-keyword">or</span> <span class="hljs-string">'content'</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> request.json:
abort(<span class="hljs-number">400</span>)
<span class="hljs-comment"># 创建新资源</span>
p = {
<span class="hljs-string">'id'</span>: posts[<span class="hljs-number">-1</span>][<span class="hljs-string">'id'</span>] + <span class="hljs-number">1</span>,
<span class="hljs-string">'title'</span>: request.json[<span class="hljs-string">'title'</span>],
<span class="hljs-string">'content'</span>: request.json[<span class="hljs-string">'content'</span>]
}
<span class="hljs-comment"># 保存资源</span>
posts.append(p)
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'posts'</span>: p}), <span class="hljs-number">201</span>
<span class="hljs-comment"># 修改指定资源</span>
<span class="hljs-meta">@app.route('/posts/<int:pid>', methods=['PUT'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_posts</span><span class="hljs-params">(pid)</span>:</span>
p = list(filter(<span class="hljs-keyword">lambda</span> p: p[<span class="hljs-string">'id'</span>] == pid, posts))
<span class="hljs-keyword">if</span> len(p) == <span class="hljs-number">0</span>:
abort(<span class="hljs-number">404</span>)
<span class="hljs-keyword">if</span> <span class="hljs-string">'title'</span> <span class="hljs-keyword">in</span> request.json:
p[<span class="hljs-number">0</span>][<span class="hljs-string">'title'</span>] = request.json[<span class="hljs-string">'title'</span>]
<span class="hljs-keyword">if</span> <span class="hljs-string">'content'</span> <span class="hljs-keyword">in</span> request.json:
p[<span class="hljs-number">0</span>][<span class="hljs-string">'content'</span>] = request.json[<span class="hljs-string">'content'</span>]
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'posts'</span>: p[<span class="hljs-number">0</span>]}), <span class="hljs-number">201</span>
<span class="hljs-comment"># 删除指定资源</span>
<span class="hljs-meta">@app.route('/posts/<int:pid>', methods=['DELETE'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_posts</span><span class="hljs-params">(pid)</span>:</span>
p = list(filter(<span class="hljs-keyword">lambda</span> p: p[<span class="hljs-string">'id'</span>] == pid, posts))
<span class="hljs-keyword">if</span> len(p) == <span class="hljs-number">0</span>:
abort(<span class="hljs-number">404</span>)
posts.remove(p[<span class="hljs-number">0</span>])
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'result'</span>: <span class="hljs-string">'数据已删除'</span>}), <span class="hljs-number">204</span>
<span class="hljs-comment"># 删除所有资源</span>
<span class="hljs-meta">@app.route('/posts', methods=['DELETE'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_posts</span><span class="hljs-params">(pid)</span>:</span>
posts.clear()
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'result'</span>: <span class="hljs-string">'数据已删除'</span>}), <span class="hljs-number">204</span>
<span class="hljs-meta">@app.errorhandler(404)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">page_not_found</span><span class="hljs-params">(e)</span>:</span>
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'page not found'</span>}), <span class="hljs-number">404</span>
<span class="hljs-meta">@app.errorhandler(400)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bad_request</span><span class="hljs-params">(e)</span>:</span>
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'bad request'</span>}), <span class="hljs-number">400</span>
pip install flask-restful
<span class="hljs-keyword">from</span> flask_restful <span class="hljs-keyword">import</span> Api
api = Api()
<span class="hljs-keyword">from</span> .ext_api <span class="hljs-keyword">import</span> api
<span class="hljs-keyword">from</span> exts <span class="hljs-keyword">import</span> api
api.init_app(app)
<span class="hljs-keyword">from</span> flask_restful <span class="hljs-keyword">import</span> Resource
<span class="hljs-keyword">from</span> myApp.models <span class="hljs-keyword">import</span> User
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> request
<span class="hljs-comment"># 创建用户处理类</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserApi</span><span class="hljs-params">(Resource)</span>:</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span><span class="hljs-params">(self, uid)</span>:</span>
u = User.query.get(uid)
<span class="hljs-keyword">if</span> u:
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>:<span class="hljs-number">0</span>, <span class="hljs-string">'error'</span>:<span class="hljs-string">''</span>,<span class="hljs-string">'data'</span>:{<span class="hljs-string">'id'</span>:uid, <span class="hljs-string">'uusername'</span>:u.uusername, <span class="hljs-string">'uage'</span>: u.uage, <span class="hljs-string">'usex'</span>: u.usex, <span class="hljs-string">'uinfo'</span>: u.uinfo}}
<span class="hljs-keyword">else</span>:
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>:<span class="hljs-number">1</span>, <span class="hljs-string">'error'</span>:<span class="hljs-string">'获取失败'</span>,<span class="hljs-string">'data'</span>:{}}, <span class="hljs-number">404</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">put</span><span class="hljs-params">(self, uid)</span>:</span>
u = User.query.get(uid)
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> u:
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>:<span class="hljs-number">1</span>, <span class="hljs-string">'error'</span>:<span class="hljs-string">'获取失败'</span>,<span class="hljs-string">'data'</span>:{}}, <span class="hljs-number">404</span>
json = request.json
u.uusername = json.get(<span class="hljs-string">'uusername'</span>)
u.uage = json.get(<span class="hljs-string">'uage'</span>)
u.save()
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>: <span class="hljs-number">0</span>, <span class="hljs-string">'error'</span>: <span class="hljs-string">''</span>,
<span class="hljs-string">'data'</span>: {<span class="hljs-string">'id'</span>: uid, <span class="hljs-string">'uusername'</span>: u.uusername, <span class="hljs-string">'uage'</span>: u.uage, <span class="hljs-string">'usex'</span>: u.usex, <span class="hljs-string">'uinfo'</span>: u.uinfo}}, <span class="hljs-number">201</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete</span><span class="hljs-params">(self, uid)</span>:</span>
u = User.query.get(uid)
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> u:
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>: <span class="hljs-number">1</span>, <span class="hljs-string">'error'</span>: <span class="hljs-string">'获取失败'</span>, <span class="hljs-string">'data'</span>: {}}, <span class="hljs-number">404</span>
u.delete()
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>: <span class="hljs-number">0</span>, <span class="hljs-string">'error'</span>: <span class="hljs-string">''</span>, <span class="hljs-string">'data'</span>: {}}, <span class="hljs-number">204</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserApiTwo</span><span class="hljs-params">(Resource)</span>:</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span><span class="hljs-params">(self)</span>:</span>
u = User.query.all()
userList = []
<span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> u:
userList.append({<span class="hljs-string">'id'</span>:user.id, <span class="hljs-string">'uusername'</span>:user.uusername, <span class="hljs-string">'uage'</span>: user.uage, <span class="hljs-string">'usex'</span>: user.usex, <span class="hljs-string">'uinfo'</span>: user.uinfo})
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>: <span class="hljs-number">0</span>, <span class="hljs-string">'error'</span>: <span class="hljs-string">''</span>, <span class="hljs-string">'data'</span>: userList}
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete</span><span class="hljs-params">(self)</span>:</span>
uList = User.query.all()
<span class="hljs-keyword">for</span> u <span class="hljs-keyword">in</span> uList:
u.delete()
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>:<span class="hljs-number">0</span>, <span class="hljs-string">'error'</span>: <span class="hljs-string">''</span>, <span class="hljs-string">'data'</span>:<span class="hljs-string">''</span>}, <span class="hljs-number">204</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">post</span><span class="hljs-params">(self)</span>:</span>
json = request.json
u = User()
u.uusername = json.get(<span class="hljs-string">'uusername'</span>)
u.uage = json.get(<span class="hljs-string">'uage'</span>)
u.usex = bool(json.get(<span class="hljs-string">'usex'</span>))
u.uinfo = json.get(<span class="hljs-string">'uinfo'</span>)
print(json)
<span class="hljs-keyword">if</span> u.save():
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>: <span class="hljs-number">0</span>, <span class="hljs-string">'error'</span>: <span class="hljs-string">''</span>,
<span class="hljs-string">'data'</span>: {<span class="hljs-string">'id'</span>: u.id, <span class="hljs-string">'uusername'</span>: u.uusername, <span class="hljs-string">'uage'</span>: u.uage, <span class="hljs-string">'usex'</span>: u.usex,<span class="hljs-string">'uinfo'</span>: u.uinfo}}, <span class="hljs-number">201</span>
<span class="hljs-keyword">return</span> {<span class="hljs-string">'code'</span>:<span class="hljs-number">1</span> ,<span class="hljs-string">'error'</span>:<span class="hljs-string">'创建失败'</span>, <span class="hljs-string">'data'</span>:<span class="hljs-string">''</span>}, <span class="hljs-number">400</span>
<span class="hljs-comment"># 添加资源,可以一个资源指定多个路由地址</span>
api.add_resource(UserAPI, <span class="hljs-string">'/users/<int:uid>'</span>, <span class="hljs-string">'/u/<int:uid>'</span>)
api.add_resource(UserListAPI, <span class="hljs-string">'/users/'</span>)
<span class="hljs-comment"># 若创建Api对象时没有指定app,那么指定app的位置应放在添加资源之后</span>
api目录下一个文件就是一个模型相关的视图类,urls.py仅做路由的匹配
<span class="hljs-keyword">from</span> myApp.api.api_user <span class="hljs-keyword">import</span> UserApi, UserListAPI
<span class="hljs-comment"># 配置路由</span>
api.add_resource(UserAPI, <span class="hljs-string">'/users/<int:uid>'</span>, <span class="hljs-string">'/u/<int:uid>'</span>)
api.add_resource(UserListAPI, <span class="hljs-string">'/users/'</span>)
说明
Restful API不保存状态,无法依赖Cookie及Session来保存用户信息,自然也无法使用Flask-Login扩展来实现用户认证。所以这里,我们就要介绍另一个扩展,Flask-HTTPAuth
安装
pip install flask-httpauth
示例
<span class="hljs-keyword">from</span> itsdangerous <span class="hljs-keyword">import</span> TimedJSONWebSignatureSerializer <span class="hljs-keyword">as</span> Serializer
<span class="hljs-keyword">from</span> flask_httpauth <span class="hljs-keyword">import</span> HTTPBasicAuth
<span class="hljs-comment"># 创建对象</span>
auth = HTTPBasicAuth()
<span class="hljs-comment"># 认证的回调函数</span>
<span class="hljs-meta">@auth.verify_password</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">verify_password</span><span class="hljs-params">(username_or_token, password)</span>:</span>
<span class="hljs-comment"># 去数据库有中进行查找</span>
u = User.query.filter(User.uusername == username_or_token, User.upassword == password).first()
print(username_or_token, <span class="hljs-string">'=====>'</span>,password)
<span class="hljs-keyword">if</span> u:
<span class="hljs-comment"># if username_or_token == 'lucky' and password == '123456':</span>
g.username = username_or_token
<span class="hljs-keyword">return</span> <span class="hljs-keyword">True</span>
<span class="hljs-comment"># 验证token</span>
s = Serializer(current_app.config[<span class="hljs-string">'SECRET_KEY'</span>])
<span class="hljs-keyword">try</span>:
data = s.loads(username_or_token)
g.username = data[<span class="hljs-string">'username'</span>]
<span class="hljs-keyword">return</span> <span class="hljs-keyword">True</span>
<span class="hljs-keyword">except</span>:
<span class="hljs-keyword">return</span> <span class="hljs-keyword">False</span>
<span class="hljs-comment"># 认证错误定制</span>
<span class="hljs-meta">@auth.error_handler</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">unauthorized</span><span class="hljs-params">()</span>:</span>
<span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Unauthorized Access'</span>}), <span class="hljs-number">403</span>
获取token
<span class="hljs-comment"># 获取token</span>
<span class="hljs-meta">@app.route('/get_token')</span>
<span class="hljs-meta">@auth.login_required</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_token</span><span class="hljs-params">()</span>:</span>
s = Serializer(app.config[<span class="hljs-string">'SECRET_KEY'</span>], expires_in=<span class="hljs-number">3600</span>)
<span class="hljs-keyword">return</span> s.dumps({<span class="hljs-string">'username'</span>: g.username})
保护指定的视图类
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserAPI</span><span class="hljs-params">(Resource)</span>:</span>
<span class="hljs-comment"># 添加认证</span>
decorators = [auth.login_required]