RESTful-API接口编程思路和注意事项:flask为例

前言:

API的设计主要包含:

API路由设计

API权限设计

API接口统计和计费设计

API计费报表设计

API请求和响应的数据格式设计和响应码设计

 

一 开发阶段

1 设计API路由URL规范。

  要使用restful  API设计API接口,绑定GET,POST,PATCH,DELETE作为查,增,改,删的方法,

  比如数据集的增/查改删,假设用post创建一个数据集叫做dataset001

POST   http://api.helloworld.com/datasets  

GET    http://api.helloworld.com/datasets/dataset001
PATCH  http://api.helloworld.com/datasets/dataset001
DELETE http://api.helloworld.com/datasets/dataset001
 1 app.add_url_rule('/datasets', view_func=list_datasets, methods=['GET'])
 2 app.add_url_rule('/datasets', view_func=create_dataset, methods=['POST'])
 3 app.add_url_rule('/datasets/<dataset_name>', view_func=get_dataset, methods=['GET'])
 4 app.add_url_rule('/datasets/<dataset_name>', view_func=update_dataset, methods=['PATCH'])
 5 app.add_url_rule('/datasets/<dataset_name>', view_func=delete_dataset, methods=['DELETE'])
 6 app.add_url_rule('/datasets/<dataset_name>/items', view_func=list_dataset_items, methods=['GET'])
 7 app.add_url_rule('/datasets/<dataset_name>/items', view_func=create_dataset_item, methods=['POST'])
 8 app.add_url_rule('/datasets/<dataset_name>/items', view_func=update_dataset_items, methods=['PATCH'])
 9 app.add_url_rule('/datasets/<dataset_name>/items', view_func=delete_dataset_items, methods=['DELETE'])
10 app.add_url_rule('/datasets/<dataset_name>/items/<item_id>', view_func=get_dataset_item, methods=['GET'])
11 app.add_url_rule('/datasets/<dataset_name>/items/<item_id>', view_func=update_dataset_item, methods=['PATCH'])
12 app.add_url_rule('/datasets/<dataset_name>/items/<item_id>', view_func=delete_dataset_item, methods=['DELETE'])
flask实际路由

2 前端请求规范和对前端跨域请求的设置 

  headers:携带必要的token和ID

  json: 数据必须返回后端指定数据格式,需要和前端约定,或者直接设计好

1 from flask import Flask
2 from flask import Flask, jsonify, request, abort
3 from flask_cors import CORS
4 
5 app = Flask(__name__)
6 
7 CORS(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True)
为前端设置跨域请求

3 后端接口返回响应规范

  (0) 敏感数据必须做脱敏处理,敏感信息不能显示在接口返回的数据中。

  (1) 返回的http状态码,要符合http标准

    201 创建成功,对应POST

    200 查询或者修改成功,对应GET,PATCH    

    204 只返回http码,没有内容no content, 适合删除,对应DELETE

  (2) 自定义状态码code和自定义消息message,放置于一级字段code中

    40001 重复创建,message:product_id already exists

    40002 字段缺乏;字段无效,比如传过来的product_id没有值, message:product_id is required

    40003 类型不在指定范围内,比如type不在['product', 'image'],status不在['on','off','all']中, message:shelf must be on or off

    40004 遇到未知错误产生异常,比如except Exception as e,  message:unexpected error    

 1 @auth_required
 2 def create_dataset_item(x_consumer_custom_id, dataset_name):
 3     result = json_template.copy()
 4     item = item_template.copy()
 5 
 6     print(request.get_json())
 7     datasets = Dataset.objects(name=dataset_name, x_consumer_custom_id=x_consumer_custom_id, deleted_at=None)
 8 
 9     if datasets:
10         collection_name = '_'.join([str(x_consumer_custom_id), datasets[0].name, 'items'])
11 
12         if datasets[0].type == 'product':
13             with switch_collection(ProductItem, collection_name) as _ProductItem:
14 
15                 if not request.get_json():
16                     result['code'] = 40002
17                     result['message'] = 'invalid fields'
18                 elif not request.get_json().get('item').get('product_id'):
19                     result['code'] = 40002
20                     result['message'] = 'product_id is required'
21                 elif not request.get_json().get('item').get('shelf'):
22                     result['code'] = 40002
23                     result['message'] = 'shelf is required'
24                 elif request.get_json().get('item').get('shelf') not in ['on', 'off']:
25                     result['code'] = 40003
26                     result['message'] = 'shelf must be on or off'
27                 else:
28                     try:
29                         item['item'] = _ProductItem(**request.get_json().get('item')).save().to_dict()
30                         result['data'] = item
31                         return jsonify(result), 201
32                     except mongoengine.NotUniqueError as e:
33                         result['code'] = 40001
34                         result['message'] = 'product already exists'
35                         return jsonify(result), 404
36                     except Exception as e:
37                         print(e)
38                         result['code'] = 40004
39                         result['message'] = 'unexpected error'
40                         return jsonify(result), 404
41 
42                 return jsonify(result), 200
43 
44         else:
45             pass
46 
47     else:
48         result['code'] = 404
49         result['message'] = 'dataset not found'
50         return jsonify(result), 404
一个简单直观显示

  (3) 返回一级字段至少带有code,data,message三个一级字段,如果是查询多个,需要返回page,per_page,total等一级字段。

{
  "code": 100, 
  "data": {
    "dataset": {
      "created_at": "2018-09-18T11:38:09Z", 
      "description": "description001", 
      "id": "5ba0e3a1bbb4960010f5a62b", 
      "name": "dataset020", 
      "status": "created", 
      "type": "product", 
      "updated_at": "2018-09-18T11:38:09Z"
    }
  }, 
  "message": "success"
}
返回单个
{
  "code": 100, 
  "data": {
    "datasets": [
      {
        "created_at": "2018-09-18T11:38:09Z", 
        "description": "description00000001", 
        "id": "5ba0e3a1bbb4960010f5a62b", 
        "name": "dataset020", 
        "status": "created", 
        "type": "product", 
        "updated_at": "2018-09-18T11:38:09Z"
      }
    ]
  }, 
  "message": "success", 
  "page": 1, 
  "per_page": 20, 
  "total": 1
}
返回多个

  以上返回格式,data用dataset和datasets关键字返回给前端,以便于前端取而展现

  (4) 返回二级字段data里,根据是单个返回item作为key,value是一个具体数据字典,根据多个返回items作为key,value是多个数据字典组成的列表

4 API权限设计

  使用强大插件kong API做权限控制,jwt令牌模式。

5 API接口统计和计费(阶梯计费)

  使用logstash,统计,接入阶梯计费策略

6 API计费报表

二 开发自测阶段

  使用多种方法测API接口,校正输入格式和输出格式。

  1 自己写流程化脚本,看请求和结果返回的日志是否正常,requests发送请求,unittest管理用例,HTMLTestRunner生成测试报告

  2 使用postman建组,测试增查改删,保存在组中,通过workspace共享给前端使用,同时通过脚本和postman双重测试对接口比较可靠

  以上两种工具都需要对数据返回做基本比对。

    (1)敏感数据是否脱敏?

    (2)返回数据格式一级字段是否符合?

    (3)返回数据格式二级字段甚至三级字段是否符合?

    (4)字段键的英文字母是否完全一致?

    (5)字段值的结果是否异常?

    (6)汉字编码问题!!!!!UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)

三 和前端对接阶段的后端工作

  1 为前端设置跨域访问请求,如上。

  2 程序代码中,每个接口的开始都要print或者log前端传输过来的, Header,  json数据, 以便于前端对接接口出现问题及时回应给前端。  

  3 后端应主动指导前端,当前端遇到传输数据格式问题的时候,这是后端应该做的将标准请求格式使前端明晰。

四 部署生产环境测试阶段

 

posted @ 2018-09-29 16:56  Adamanter  阅读(530)  评论(0编辑  收藏  举报