-
-
Notifications
You must be signed in to change notification settings - Fork 780
Description
Description
In an API written in OAS v3 (which supports content negotiation), when an operation has two different response mimetypes and one of them is not json-compatible, the validation assumes application/json. This entails some errors on the validation.
Expected behaviour
The validation should honor the API specification and use the schema of the associated content type. One should be able to have an operation that can respond application/json and application/octet-stream according to the Accept header.
Actual behaviour
When the operation initializes its mimetype and finds that the operation can produce more than one type, it assumes that it is application/json. However, if the implementation's response is not json or does, the validation will fail.
Steps to reproduce
- Create an API with an operation that can respond two different content types where one of the types is not json-compatible, like
application/jsonandapplication/content-type. For example:
---
openapi: 3.0.2
info:
title: example
description: example
version: 0.0.1
paths:
/api/files/{id}:
parameters:
- name: id
in: path
description: File identifier
required: true
schema:
type: string
get:
summary: Fetch file metadata or contents
description: ...
operationId: app.api.files.get
responses:
'200':
description: File contents or metadata
content:
application/json:
schema:
type: object
application/octet-stream:
schema:
type: string
format: binary
- Implement the operation while honoring the Accept header. For example:
import os
from flask import request, send_file
def get(*, id):
# Content negotiation
best = request.accept_mimetypes.best_match(['application/json',
'application/octet-stream'],
default=None)
if best == 'application/json':
return {'id': id, 'metadata': {'key': 'value'}}, 200
elif best == 'application/octet-stream':
tmp_file = tempfile.NamedTemporaryFile(mode='w+b')
tmp_file.write(os.urandom(32)) # 32 random bytes
tmp_file.flush()
tmp_file.seek(0)
response = send_file(tmp_file, mimetype='application/octet-stream')
response.direct_passthrough = False
return response, 200
-
Run connexion with response validation enabled.
-
Request the operation from a client using the Accept header to request
application/octet-stream. In the example above, it will fail with a
UnicodeDecodeError: 'utf-8' codec can't decode bytewhile trying to
interpret the binary contents as json.
Additional info:
After some analysis, I have identified some potential sources that produce this behavior.
First, it seems that
connexion.operations.abstract.AbstrsactOperation.get_mimetype incorrectly
assumes that the mimetype is application/json when the operation does not
produce json for all its responses (according to connexion.utils.all_json.
This makes the ResponseValidator have an incorrect mimetype field. Then,
ResponseValidator.is_json_Schema_compatible is confused by this and eventually
the ResponseValidator.validate_response calls json.loads, which fails and it
is interpreted as a validation error.
Output of the commands:
-
python --version
Python 3.7.1 -
pip show connexion | grep "^Version\:"
Version: 2.2.0