aiohttp-apispec icon indicating copy to clipboard operation
aiohttp-apispec copied to clipboard

Two errors in Schema

Open elsimqoz opened this issue 4 years ago • 3 comments

Ubuntu 18.04
Python 3.8.0
aiohttp==3.6.2
aiohttp-apispec==2.2.1

https://github.com/maximdanilchenko/aiohttp-apispec/blob/master/docs/usage.rst

class ResponseSchema(Schema):
    msg = fields.Str()
    data = fields.Dict()


@docs(tags=['mytag'],
      summary='Test method summary',
      description='Test method description')
@request_schema(RequestSchema(strict=True))
@response_schema(ResponseSchema(), 200)
async def index(request):
    return web.json_response({'msg': 'done',
                              'data': {}})
  1. Run it, get error
Traceback (most recent call last):
  ...
    @request_schema(RequestSchema(strict=True))
TypeError: __init__() got an unexpected keyword argument 'strict'
  1. Let's add not-in-schema field
@response_schema(ResponseSchema(), 200)
return web.json_response({'msg': 'done',
                          'data': {}, 'foo': 'bar'})

It seems that ResponseSchema didn't validate response

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Python/3.8 aiohttp/3.6.2

{
    "data": {},
    "foo": "bar",
    "msg": "done"
}

But directly it will

>>> ResponseSchema().validate({'msg': 'done', 'data': {}, 'foo': 'bar'})
{'foo': ['Unknown field.']}

elsimqoz avatar May 11 '20 19:05 elsimqoz

@elsimqoz did you add validation middlware to your app?

maximdanilchenko avatar Jun 05 '20 13:06 maximdanilchenko

@maximdanilchenko yes

from aiohttp_apispec import setup_aiohttp_apispec, validation_middleware

app = Application(
    ...
    middlewares=[error_middleware, validation_middleware],
)

setup_aiohttp_apispec(
    ...
    error_callback=handle_validation_error,
)

=======

def format_http_error(http_error_cls, message: Optional[str] = None,
                      fields: Optional[Mapping] = None) -> HTTPException:
    status = HTTPStatus(http_error_cls.status_code)
    error = {
        'code': status.name.lower(),
        'message': message or status.description
    }

    if fields:
        error['fields'] = fields

    return http_error_cls(body={'error': error})


def handle_validation_error(error: ValidationError, *_):
    raise format_http_error(HTTPBadRequest, 'Request validation has failed', error.messages)


@middleware
async def error_middleware(request: Request, handler):
    try:
        return await handler(request)
    except HTTPException as err:
        if not isinstance(err.body, JsonPayload):
            err = format_http_error(err.__class__, err.text)
        raise err
    except ValidationError as err:
        raise handle_validation_error(err)
    except Exception:
        log.exception('Unhandled exception')
        raise format_http_error(HTTPInternalServerError)

elsimqoz avatar Jul 02 '20 08:07 elsimqoz

Oh, @elsimqoz, sorry, I missed that you wrote about response validation. Yes, aiohttp-apispec cannot validate responses. And I don’t think it is necessary. If you don’t think so you are welcome for PR’s:)

maximdanilchenko avatar Jul 05 '20 14:07 maximdanilchenko