Connexion: 返回 ResponseContainer 时 validate_responses 失败

创建于 2017-03-23  ·  3评论  ·  资料来源: zalando/connexion

(如果这已经被报道,请提前抱歉。我最近在实际发现重复问题方面做得很糟糕)

描述

validate_response=True导致在返回连接ResponseContainer对象时引发异常。 就我而言,我正在将旧 API 迁移到 swagger,并且我需要能够直接返回一个 Flask Response对象,以便我可以使用它来修改 cookie,因此返回一个ResponseContainer而不是字典。

重现步骤

测试.yaml

swagger: "2.0"

info:
  title: "Test"
  version: "1.0"

basePath: /v1.0

produces:
  - application/json

consumes:
  - application/json

paths:
  /foo:
    get:
      operationId: test.foo
      responses:
        200:
          description: A dns zones
          schema:
            type: object
            required:
              - foo
            properties:
              foo:
                type: string
                example: 'foo'

测试文件

#!/usr/bin/env python3
import connexion
from connexion.decorators.decorator import ResponseContainer
from flask import jsonify

app = connexion.App(__name__, 9091)

def foo():
    resp =  jsonify({'foo': 'bar'})
    return ResponseContainer(mimetype=resp.mimetype, response=resp, status_code=200)

if __name__ == '__main__':
    app.add_api('test.yaml', validate_responses=True)
    app.run()

如果你curl http://0.0.0.0:9091/v1.0/foo ,你会得到以下堆栈跟踪:

Traceback (most recent call last):
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/connexion/decorators/decorator.py", line 118, in wrapper
    response_container = function(*args, **kwargs)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/connexion/decorators/produces.py", line 100, in wrapper
    response = function(*args, **kwargs)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/connexion/decorators/response.py", line 92, in wrapper
    self.validate_response(response.get_data(), response.status_code, response.headers)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/connexion/decorators/response.py", line 47, in validate_response
    data = json.dumps(data)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/flask/json.py", line 123, in dumps
    rv = _json.dumps(obj, **kwargs)
  File "/usr/lib64/python3.6/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/lib64/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib64/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/connexion/decorators/produces.py", line 33, in default
    return json.JSONEncoder.default(self, o)
  File "/home/lgbland/code/connexion/venv/lib/python3.6/site-packages/flask/json.py", line 80, in default
    return _json.JSONEncoder.default(self, o)
  File "/usr/lib64/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'bytes' is not JSON serializable
127.0.0.1 - - [23/Mar/2017 14:17:30] "GET /v1.0/foo HTTP/1.1" 500 -

如果我删除validate_responses=True ,那么这将按预期返回 JSON。

附加信息:

  • 蟒蛇版本 3.6
  • 连接版本 1.1.5

最有用的评论

在 1.1.6 中修复

所有3条评论

看起来ResponseContainer存储的数据是:

b'{\n  "foo": "bar"\n}\n'

由于response.get_data()在decorator.py(第153行)中返回已经编码的数据。

它到达了 response.py 中的这个代码块(第 45 到 48 行)并阻塞,因为数据已经被转储了:

                # For cases of custom encoders, we need to encode and decode to
                # transform to the actual types that are going to be returned.
                data = json.dumps(data)
                data = json.loads(data)

如果我按如下方式更新decorator.py,那么我就开始越来越近了。 它不再引发异常,但它现在返回字符串 'foo' 而不是 json {"foo": "bar"}(我想如果有人使用不同的格式而不是 JSON,这是否会导致问题,例如作为 XML,但根据上述代码块中的注释,我认为目前无论如何都不会支持它)。

from flask import json
...
            self.data = json.loads(self._response.get_data())

看起来最后一个错误(返回'foo'而不是JSON)与在decorator.py中重建flask响应对象有关,并且self.data现在是一个dict而不是一个bytes对象。

    def flask_response_object(self):
        """
        Builds an Flask response using the contained data,
        status_code, and headers.

        :rtype: flask.Response
        """
        self._response = flask.current_app.response_class(
            self.data, mimetype=self.mimetype, content_type=self.headers.get('content-type'),
            headers=self.headers)  # type: flask.Response
        self._response.status_code = self.status_code

        return self._response

修复它的一种方法是在此处重新转储它:

 self._response = flask.current_app.response_class(
            json.dumps(self.data), mimetype=self.mimetype, content_type=self.headers.get('content-type'),
            headers=self.headers)  # type: flask.Response

另一个解决方案可能是仅返回原始响应(如果给出)

    def flask_response_object(self):
        """
        Builds an Flask response using the contained data,
        status_code, and headers.

        :rtype: flask.Response
        """
        if not self._response:
            self._response = flask.current_app.response_class(
                self.data, mimetype=self.mimetype, content_type=self.headers.get('content-type'),
                headers=self.headers)  # type: flask.Response
            self._response.status_code = self.status_code

        return self._response

如果这些解决方案将错误或边缘情况引入系统,我不会感到惊讶,但我不能肯定地说。 如果它们背后的总体思路看起来没问题,我很乐意巩固它们,进行一些测试并提交拉取请求。 如果有更好的方法来解决这个问题,那就无视我吧:)

干杯。

在 1.1.6 中修复

此页面是否有帮助?
0 / 5 - 0 等级