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

Flask BadRequest error leads to 500 #1807

Closed
LewisCowlesMotive opened this issue Nov 16, 2023 · 9 comments
Closed

Flask BadRequest error leads to 500 #1807

LewisCowlesMotive opened this issue Nov 16, 2023 · 9 comments

Comments

@LewisCowlesMotive
Copy link

Description

Just updating to 3.0.1 (latest release) I noticed that OpenAPI schema failure on maximum for a float / number value leads to a 500

Expected behaviour

HTTP 400 error describing the problem

Actual behaviour

connexion.exceptions.BadRequestProblem: 400: 1000 is greater than the maximum of 31.06 - 'radiusInMiles'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/lewiscowles/Library/Caches/pypoetry/virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/lewiscowles/Library/Caches/pypoetry/virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/lewiscowles/Library/Caches/pypoetry/virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/connexion/middleware/main.py", line 483, in __call__
    await self.app(scope, receive, send)
  File "/Users/lewiscowles/Library/Caches/pypoetry/virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/connexion/middleware/exceptions.py", line 101, in __call__
    await super().__call__(scope, receive, send)
  File "/Users/lewiscowles/Library/Caches/pypoetry/virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/Users/lewiscowles/Library/Caches/pypoetry/virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/starlette/_exception_handler.py", line 63, in wrapped_app
    response = await handler(conn, exc)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/lewiscowles/Library/Caches/pypoetry/virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2106, in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
  File "/Users/lewiscowles/Library/Caches/pypoetry/virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 833, in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: invalid_api_usage() takes 1 positional argument but 2 were given

Steps to reproduce

We have a field in our OpenAPI

              radiusInMiles: 
                type: number
                example: 25
                minimum: 0
                maximum: 31.06

it looks like when it fails to validate due to invalid input (1000) then we get this error.

Additional info:

Output of the commands:

  • python --version 3.11
  • pip show connexion | grep "^Version\:" 3.0.1 we use poetry (see below for locked hashes and pyproject.toml partial)

pyproject.toml

[tool.poetry.dependencies]
python = "~=3.11.4"
connexion = { extras = ["swagger-ui", "flask", "uvicorn"], version = "^3.0.1" }
[[package]]
name = "connexion"
version = "3.0.1"
description = "Connexion - API first applications with OpenAPI/Swagger"
optional = false
python-versions = ">=3.8,<4.0"
files = [
    {file = "connexion-3.0.1-py3-none-any.whl", hash = "sha256:b8a4d07ceab452d5033bdf0ebf3489a19217abbdfb0b0b02eddf1d5da2d7e997"},
    {file = "connexion-3.0.1.tar.gz", hash = "sha256:df1edd47fad7fc6c8571845a274ba9571e8b1ed96258ff98dd2ffd713517672a"},
]
@LewisCowlesMotive
Copy link
Author

virtualenvs/b2b-gateway-pnc-C83Fr2yq-py3.11/lib/python3.11/site-packages/connexion/middleware/exceptions.py

Why is the flask app using starlette exceptions? Flask and Starlette are two different frameworks.

@LewisCowlesMotive
Copy link
Author

LewisCowlesMotive commented Nov 16, 2023

@RobbeSneyders this is entirely an issue with 3.0.1 and not 3.0.0 Still an issue in 3.0.1

https://github.com/spec-first/connexion/releases/tag/3.0.1 asserts it "fixes" FlaskApp, but clearly it does not.

I Updated this comment as it seems that 3.0.0. causes silent test failure to run.

@LewisCowlesMotive
Copy link
Author

Closing... This is due to API changes (which I should have expected from a 2->3 upgrade. Apologies

APP.add_error_handler(ProblemException, invalid_api_usage)
APP.add_error_handler(ValueError, value_error)
APP.add_error_handler(Exception, unknown_error)

These lines needed their callback function signature (so our code) updated to the new format shown in
https://connexion.readthedocs.io/en/stable/exceptions.html

😊 😂 (apologies) 🏳️

@RobbeSneyders
Copy link
Member

Thanks for reporting back @LewisCowlesMotive. I'm working on a migration guide which will highlight this better.

@LewisCowlesMotive
Copy link
Author

Thanks and sorry. I kept looking in other folks repo's swearing about the function erroring, only to realise it was our own.

Do you know if Magnum should work for this in API-Gateway Lambda? We used to use a wsgi adapter, but it looks like one of the big changes is using ASGI now instead. I Shipped it working in pipelines and local only to find an error about call not getting enough arguments. It seems like it now needs ASGI

@LewisCowlesMotive
Copy link
Author

I'm happy to share anything I can if you point me at where you'd like to document. I Actually like the greater encapsulation provided in all the changes I've made.

  • No more APP.app.test_client is now APP.test_client (so no drilling in to borrow from flask)
  • No more response.json["data"] json` is now a method (I'm assuming that comes with late-evaluation benefits)
  • Our tests feel like they run a little faster.
  • No application code changed for endpoints.

@RobbeSneyders
Copy link
Member

In theory we can support both ASGI and WSGI middleware (if you're using the FlaskApp), but they should be inserted at different places in the middleware stack. We currently don't provide an interface to insert WSGI middleware, but I can see the value in this and it should be a superficial change.

I'll try to include it in a new release next week.

If you're blocked, here is a (really ugly and untested) workaround.

app = connexion.FlaskApp()

app._middleware_app.asgi_app.app = YourWSGIMiddleware(app._middleware_app.asgi_app.app)

I'm happy to share anything I can if you point me at where you'd like to document.

I already have the migration guide partially written locally. I'll try to push it over the weekend. You can either add any input as a response here and I'll include it, or you can add it yourself to the guide afterwards.

I Actually like the greater encapsulation provided in all the changes I've made.

No more APP.app.test_client is now APP.test_client (so no drilling in to borrow from flask)
No more response.json["data"] json` is now a method (I'm assuming that comes with late-evaluation benefits)
Our tests feel like they run a little faster.
No application code changed for endpoints.

Really appreciate the feedback!
Let us know if you have any proposals for improvement or want to contribute something.

@LewisCowlesMotive
Copy link
Author

LewisCowlesMotive commented Nov 16, 2023

Oh please don't support WSGI [on my account], I'm happy to switch to ASGI, just working it out if I just replace our dependency with Magnum and expect it to work.

@RobbeSneyders
Copy link
Member

I think it would still be good to enable migration for other users as well. Eg. Apache Airflow uses Connexion with WSGI middleware as well and I'm helping them migrate.

The stack looks like this:

Flask
WSGI middleware
WSGI-to-ASGI middleware
ASGI middleware
ASGI server

So we don't have to do any effort to support it. Just provide an interface.

RobbeSneyders added a commit that referenced this issue Nov 19, 2023
As discussed in #1807.

Allowing the injection of WSGI middleware can enable easier migration
from Connexion 2 to Connexion 3. The use cases are limited though, as
this will only work for middleware that can work at the end of the
middleware stack.
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

No branches or pull requests

2 participants