Skip to content

Error ServerNotFound when nesting Flask application with Werkzeug application dispatcher #436

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

Closed
SethThoburn opened this issue Nov 5, 2022 · 3 comments · Fixed by #449
Closed
Labels
area/contrib Indicates an issue on contrib area. kind/bug/confirmed

Comments

@SethThoburn
Copy link

I'm using the DispatcherMiddleware from Werkzeug to nest my application under /api/v1. In my spec, I have http://localhost:3991/api/v1 listed under servers. When I make a request to http://localhost:3991/api/v1/accounts I get the following error:

openapi_core.templating.paths.exceptions.ServerNotFound: Server not found for http://localhost:3991/accounts

Notice that the library interprets the url incorrectly, it just appends the /accounts endpoint from my spec to the hostname rather than to the full base url. There are two things that need changed for this to be fixed:

  • In lines 37 and 39 of openapi_core/templating/paths/finders.py urls are joined with urljoin, which removes everything after the hostname. E.g. https://test.com/test1 joined to /test2 would yield https://test.com/test2. A different function would have to be used
  • The requests from the library expect a host_url when it should probably be a base url. There would need to be a way to find this base url in the flask response transformer, but I'm sure it could be done.

An easier solution that would work for now would be to add an option to disable server validation. I'd be glad to submit a PR to do that or to fix the above issue, I just want to get confirmation before I work on the PR.

@SethThoburn
Copy link
Author

SethThoburn commented Nov 7, 2022

A workaround I found is to subclass the request transformer as well as append the base path to all spec routes:

spec = {
    # your spec
}
BASE_PATH = '/api/v1'

class MyFlaskOpenApiRequest(FlaskOpenAPIRequest):
    @property
    def path_pattern(self):
        return f'{BASE_PATH}{super().path_pattern}'

spec_for_validators = {
    **spec,
    'paths': {f'{BASE_PATH}{k}': v for k, v in spec['paths'].items()},
}
# also need to set server to relative root to make this work
spec_for_validators['servers'] = [{'url':'/'}]

You can still use the original spec with the original paths for serving docs etc.
FYI above code isn't tested, as my implementation is slightly different.

@p1c2u
Copy link
Collaborator

p1c2u commented Nov 25, 2022

Hi @SethThoburn

can you show more how you use DispatcherMiddleware ? What integration do you use? Do you use openapi decorator or low level? Thanks

@SethThoburn
Copy link
Author

I'm using dispatcher middleware like this:

application = DispatcherMiddleware(main_application, {
    '/api/v1': public_api
})

I am not using an openapi decorator, I'm validating responses and requests in functions in the public_api app using flask's before_request and after_request decorators.

@p1c2u p1c2u added kind/bug/confirmed area/contrib Indicates an issue on contrib area. labels Dec 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/contrib Indicates an issue on contrib area. kind/bug/confirmed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants