使用flask-restful批处理API请求

时间:2014-10-16 21:40:51

标签: rest flask flask-restful

我正在构建一个REST-restful的REST API,我要启用的一件事就是能够批量请求资源,类似于Facebook Graph API的工作方式:

curl \
    -F 'access_token=…' \
    -F 'batch=[{"method":"GET", "relative_url":"me"},{"method":"GET", "relative_url":"me/friends?limit=50"}]' \
    https://graph.facebook.com

然后返回一个数组,每个请求都以其状态代码和结果解析:

[
    { "code": 200, 
      "headers":[
          { "name": "Content-Type", 
            "value": "text/javascript; charset=UTF-8" }
      ],
      "body": "{\"id\":\"…\"}"},
    { "code": 200,
      "headers":[
          { "name":"Content-Type", 
            "value":"text/javascript; charset=UTF-8"}
      ],
      "body":"{\"data\": [{…}]}}
]

我已经能够通过简单地循环遍历请求并对我自己的应用程序调用urlopen来复制它。这似乎效率很低,我不得不认为有更好的方法。是否有更简单和/或更好的方法从请求处理程序中对我自己的应用程序发出请求?

2 个答案:

答案 0 :(得分:2)

由于您需要返回标题,我建议您将这些批量请求发送给您自己(即将请求发送到localhost),这样响应将与您在进行单个调用时获得的响应一致

考虑到当您的API收到批处理请求时,您将需要至少一个空闲工作程序在第一个工作程序阻塞并等待时接受这些间接请求。所以你需要至少有两名工人。即使这样,如果两个批次请求同时到达并带走您的两名工人,您也会面临僵局。因此,实际上,您需要拥有与期望同时接收的批处理请求一样多的工作人员以及至少一个以处理间接请求的工作。

从另一方面来看,您需要并行运行这些间接请求中的多个,因为如果它们最终一个接一个地运行,则使用批处理请求的好处就会丢失。因此,您还需要有足够数量的工作人员来实现并行性。

老实说,我不认为这是一个很棒的功能。在大多数客户端语言中,并行执行多个请求相当容易,因此您不需要提供这是服务器端功能。如果您使用的是Javascript,则非常容易,但在Python,Ruby等中也很容易。

答案 1 :(得分:1)

您可以使用Flask执行批处理中提交的各个请求,如下所示。

批量请求

[
    {
        "method" : <string:method>,
        "path"   : <string:path>,
        "body"   : <string:body>
    },
    {
        "method" : <string:method>,
        "path"   : <string:path>,
        "body"   : <string:body>
    }
]

批量回复

[
    {
        "status"   : <int:status_code>,
        "response" : <string:response>
    },
    {
        "status"   : <int:status_code>,
        "response" : <string:response>
    }
]

示例代码

def _read_response(response):
    output = StringIO.StringIO()
    try:
        for line in response.response:
            output.write(line)

        return output.getvalue()

    finally:
        output.close()

@app.route('/batch', methods=['POST'])
def batch(username):
    """
    Execute multiple requests, submitted as a batch.

    :statuscode 207: Multi status
    """
    try:
        requests = json.loads(request.data)
    except ValueError as e:
        abort(400)

    responses = []

    for index, req in enumerate(requests):
        method = req['method']
        path = req['path']
        body = req.get('body', None)

        with app.app_context():
            with app.test_request_context(path, method=method, data=body):
                try:
                    # Can modify flask.g here without affecting flask.g of the root request for the batch

                    # Pre process Request
                    rv = app.preprocess_request()

                    if rv is None:
                        # Main Dispatch
                        rv = app.dispatch_request()

                except Exception as e:
                    rv = app.handle_user_exception(e)

                response = app.make_response(rv)

                # Post process Request
                response = app.process_response(response)

        # Response is a Flask response object.
        # _read_response(response) reads response.response and returns a string. If your endpoints return JSON object,
        # this string would be the response as a JSON string.
        responses.append({
            "status": response.status_code,
            "response": _read_response(response)
        })

    return make_response(json.dumps(responses), 207, HEADERS)