(如果这已经被报道,请提前抱歉。我最近在实际发现重复问题方面做得很糟糕)
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。
看起来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 中修复
最有用的评论
在 1.1.6 中修复