Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ResponseValidator: how to handle mime-type with character encoding details? #378

Closed
erikbos opened this issue Oct 31, 2021 · 3 comments · Fixed by #678
Closed

ResponseValidator: how to handle mime-type with character encoding details? #378

erikbos opened this issue Oct 31, 2021 · 3 comments · Fixed by #678

Comments

@erikbos
Copy link

erikbos commented Oct 31, 2021

While using ResponseValidator.validate I noticed validation is sensitive to the content-type header including character encoding details.

In case an HTTP response includes content-type: application/json; charset=utf-8 validation fails: MediaTypeNotFound(mimetype='application/json; charset=utf-8', availableMimetypes=['application/json'])

(As quick work I tried adding application/json; charset=utf-8 to the openapi spec as additional mime-type but that results in vague errors such as raised in #128)

I can manually overwrite mimetype of the response before validating but that feels a bit wrong .. one could reason that application/json; charset=utf-8 should be accepted and treated equal as application/json. Any comment?

See below for code that reproduces this situation.

import json
from openapi_core import create_spec
from openapi_core.validation.request.validators import RequestValidator
from openapi_core.validation.response.validators import ResponseValidator
from openapi_core.testing import MockRequest
from openapi_core.testing import MockResponse

def validate(spec_dict, mime_type):
    spec = create_spec(spec_dict)

    openapi_request = MockRequest('localhost', 'get', '/v1/cars')
    validator = RequestValidator(spec)
    result = validator.validate(openapi_request)
    request_errors = result.errors

    data = json.dumps({
        'cars' :[
            {
                'name': 'car1',
            },
            {
                'name': 'car2',
            }
        ]
    })
    openapi_response = MockResponse(data, mimetype=mime_type)

    # Workaround
    #if openapi_response.mimetype == 'application/json; charset=utf-8':
    #     openapi_response.mimetype = 'application/json'

    validator = ResponseValidator(spec)
    result = validator.validate(openapi_request, openapi_response)
    response_errors = result.errors

    print('Request errors: {} Response errors: {}'.format(request_errors, response_errors))


spec = {
    'openapi': '3.0.0',
    'info': {
        'version': '0.1',
        'title': 'List of objects',
        'description': 'Test for list of objects'
    },
    'paths': {
        '/v1/cars': {
            'get': {
                'description': 'Retrieve all cars',
                'responses': {
                    '200': {
                        'description': 'Successfully retrieved all cars',
                        'content': {
                            'application/json': {
                                'schema': {
                                    'type': 'object',
                                    'properties': {
                                        'cars': {
                                            'type': 'array',
                                            'items': {
                                                '$ref': '#/components/schemas/Car'
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    'components': {
        'schemas': {
            'Car': {
                'type': 'object',
                'properties': {
                    'name': {
                        'type': 'string'
                    }
                }
            }
        }
    }
}
validate(spec, 'application/json')
validate(spec, 'application/json; charset=utf-8')
@NasaGeek
Copy link

NasaGeek commented Nov 4, 2021

Personally, I worked around this by specifying the response mimetype in the spec as "application/json; UTF-8", e.g.

--- snip ---
      responses:
        '200':
          description: OK
          content:
            application/json; charset=UTF-8:
              schema:
                properties:
--- snip ---

I see that you mentioned trying that and encountering errors. Perhaps they're being caused by something else?

@erikbos
Copy link
Author

erikbos commented Nov 14, 2021

Not sure if such work around actually works, hence I provided a test:

Run "as is":

  • the first validate() call works, no errors, as expected.
  • the second validate() fails as there is no matching content-type, as expected.

after the updating the content-type in test spec from application/json to application/json; charset=utf-8:

  • the first validate() fails as there is no matching content-type, as expected.
  • second one now fails with a schema validation error: schema_errors=(<ValidationError: '\'{"cars": [{"name": "car1"}, {"name": "car2"}]}\' is not of type object'>

By updating the spec I would expect the validation for the second validate() call to work, but that does not appear to be the case. Am I overlooking something?

@NasaGeek
Copy link

Looking back, I see there was one more element I needed for my workaround:

validator = ResponseValidator(
        spec,
        custom_media_type_deserializers={"application/json; charset=UTF-8": json.loads},
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants