Connexion uses the operationId
from each Operation Object to identify which Python function should handle each URL.
Explicit Routing:
paths:
/hello_world:
post:
operationId: myapp.api.hello_world
If you provided this path in your specification POST requests to http://MYHOST/hello_world
, it would be handled by the function hello_world
in myapp.api
module.
Optionally, you can include x-swagger-router-controller
in your operation definition, making operationId
relative:
paths:
/hello_world:
post:
x-swagger-router-controller: myapp.api
operationId: hello_world
NOTE: If you are using an OpenAPI spec, you should use x-openapi-router-controller
in your operation definition, making operationId
relative:
paths:
/hello_world:
post:
x-openapi-router-controller: myapp.api
operationId: hello_world
If all your operations are relative, you can use the RelativeResolver
class instead of repeating the same x-swagger-router-controller
or x-openapi-router-controller
in every operation:
from connexion.resolver import RelativeResolver
app = connexion.FlaskApp(__name__)
app.add_api('swagger.yaml', resolver=RelativeResolver('api'))
Keep in mind that Connexion follows how HTTP methods work in Flask and therefore HEAD requests will be handled by the operationId
specified under GET in the specification. If both methods are supported, connexion.request.method
can be used to determine which request was made.
By default, Connexion strictly enforces the presence of a handler function for any path defined in your specification. Because of this, adding new paths without implementing a corresponding handler function will produce runtime errors and your application will not start. To allow new paths to be added to your specification, e.g. in an API design first workflow, set the resolver_error
to configure Connexion to provide an error response for paths that are not yet implemented:
app = connexion.FlaskApp(__name__)
app.add_api('swagger.yaml', resolver_error=501)
To customize this behavior, Connexion can use alternative Resolvers
—for example, RestyResolver
. The RestyResolver
will compose an operationId
based on the path and HTTP method of the endpoints in your specification:
from connexion.resolver import RestyResolver
app = connexion.FlaskApp(__name__)
app.add_api('swagger.yaml', resolver=RestyResolver('api'))
paths:
/:
get:
# Implied operationId: api.get
/foo:
get:
# Implied operationId: api.foo.search
post:
# Implied operationId: api.foo.post
'/foo/{id}':
get:
# Implied operationId: api.foo.get
put:
# Implied operationId: api.foo.put
copy:
# Implied operationId: api.foo.copy
delete:
# Implied operationId: api.foo.delete
'/foo/{id}/bar':
get:
# Implied operationId: api.foo.bar.search
'/foo/{id}/bar/{name}':
get:
# Implied operationId: api.foo.bar.get
# Handler signature: `def get(id, name): ...`
RestyResolver
will give precedence to any operationId
encountered in the specification. It will also respect x-swagger-router-controller
and x-openapi-router-controller
. You may import and extend connexion.resolver.Resolver
to implement your own operationId
(and function) resolution algorithm. Note that when using multiple parameters in the path, they will be collected and all passed to the endpoint handlers.
MethodViewResolver
is an customised Resolver based on RestyResolver
to take advantage of MethodView structure of building Flask APIs. The MethodViewResolver
will compose an operationId
based on the path and HTTP method of the endpoints in your specification. The path will be based on the path you provide in the app.add_api and the path provided in the URL endpoint (specified in the swagger or openapi3).
from connexion.resolver import MethodViewResolver
app = connexion.FlaskApp(__name__)
app.add_api('swagger.yaml', resolver=MethodViewResolver('api'))
And associated YAML
paths:
/foo:
get:
# Implied operationId: api.FooView.search
post:
# Implied operationId: api.FooView.post
'/foo/{id}':
get:
# Implied operationId: api.FooView.get
put:
# Implied operationId: api.FooView.put
copy:
# Implied operationId: api.FooView.copy
delete:
# Implied operationId: api.FooView.delete
The structure expects a Class to exists inside the directory api
that conforms to the naming <<Classname with Capitalised name>>View
. In the above yaml the necessary MethodView implementation is as follows:
import datetime
from connexion import NoContent
from flask import request
from flask.views import MethodView
class PetsView(MethodView):
""" Create Pets service
"""
method_decorators = []
pets = {}
def post(self):
body= request.json
name = body.get("name")
tag = body.get("tag")
count = len(self.pets)
pet = {}
pet['id'] = count + 1
pet["tag"] = tag
pet["name"] = name
pet['last_updated'] = datetime.datetime.now()
self.pets[pet['id']] = pet
return pet, 201
def put(self, petId):
body = request.json
name = body["name"]
tag = body.get("tag")
id_ = int(petId)
pet = self.pets.get(petId, {"id": id_})
pet["name"] = name
pet["tag"] = tag
pet['last_updated'] = datetime.datetime.now()
self.pets[id_] = pet
return self.pets[id_], 201
def delete(self, petId):
id_ = int(petId)
if self.pets.get(id_) is None:
return NoContent, 404
del self.pets[id_]
return NoContent, 204
def get(self, petId):
id_ = int(petId)
if self.pets.get(id_) is None:
return NoContent, 404
return self.pets[id_]
def search(self, limit=100):
# NOTE: we need to wrap it with list for Python 3 as dict_values is not JSON serializable
return list(self.pets.values())[0:limit]
and a __init__.py file to make the Class visible in the api directory.
from .petsview import PetsView
MethodViewResolver
will give precedence to any operationId
encountered in the specification. It will also respect x-swagger-router-controller
and x-openapi-router-controller
. You may import and extend connexion.resolver.MethodViewResolver
to implement your own operationId
(and function) resolution algorithm.
The names of query and form parameters, as well as the name of the body parameter are sanitized by removing characters that are not allowed in Python symbols. I.e. all characters that are not letters, digits or the underscore are removed, and finally characters are removed from the front until a letter or an under-score is encountered. As an example:
>>> re.sub('^[^a-zA-Z_]+', '', re.sub('[^0-9a-zA-Z_]', '', '$top'))
'top'
Without this sanitation it would e.g. be impossible to implement an OData API.
You can also convert CamelCase parameters to snake_case automatically using pythonic_params option:
app = connexion.FlaskApp(__name__)
app.add_api('api.yaml', ..., pythonic_params=True)
With this option enabled, Connexion firstly converts CamelCase names to snake_case. Secondly it looks to see if the name matches a known built-in and if it does it appends an underscore to the name.
Connexion supports Flask's int
, float
, and path
route parameter variable converters. Specify a route parameter's type as integer
or number
or its type as string
and its format as path
to use these converters. For example:
paths:
/greeting/{name}:
# ...
parameters:
- name: name
in: path
required: true
type: string
format: path
will create an equivalent Flask route /greeting/<path:name>
, allowing requests to include forward slashes in the name
url variable.
Setting a base path is useful for versioned APIs. An example of a base path would be the 1.0
in http://MYHOST/1.0/hello_world
.
If you are using OpenAPI 3.x.x, you set your base URL path in the servers block of the specification. You can either specify a full URL, or just a relative path.
servers:
- url: https://MYHOST/1.0
description: full url example
- url: /1.0
description: relative path example
paths:
...
If you are using OpenAPI 2.0, you can define a basePath
on the top level of your OpenAPI 2.0 specification.
basePath: /1.0
paths:
...
If you don't want to include the base path in your specification, you can provide it when adding the API to your application:
app.add_api('my_api.yaml', base_path='/1.0')
Swagger UI is available at /ui/
by default.
You can choose another path through options:
options = {'swagger_url': '/'}
app = connexion.App(__name__, options=options)
Connexion makes the OpenAPI/Swagger specification in JSON format available from swagger.json
in the base path of the API.
You can disable the Swagger JSON at the application level:
app = connexion.FlaskApp(__name__, specification_dir='swagger/',
swagger_json=False)
app.add_api('my_api.yaml')
You can also disable it at the API level:
app = connexion.FlaskApp(__name__, specification_dir='swagger/')
app.add_api('my_api.yaml', swagger_json=False)