import difflib
import inspect
from itertools import chain
import logging
import operator
import re
import sys
import warnings
from collections import OrderedDict
from functools import wraps , partial
from types import MethodType
from flask import url_for , request , current_app
from flask import make_response as original_flask_make_response
from flask . signals import got_request_exception
from jsonschema import RefResolver
from werkzeug . utils import cached_property
from werkzeug . datastructures import Headers
from werkzeug . exceptions import (
HTTPException ,
MethodNotAllowed ,
NotFound ,
NotAcceptable ,
InternalServerError ,
)
from . import apidoc
from . mask import ParseError , MaskError
from . namespace import Namespace
from . postman import PostmanCollectionV1
from . resource import Resource
from . swagger import Swagger
from . utils import (
default_id ,
camel_to_dash ,
unpack ,
import_check_view_func ,
BaseResponse ,
)
from . representations import output_json
from . _http import HTTPStatus
endpoint_from_view_func = import_check_view_func ( )
RE_RULES = re . compile ( " (<.*>) " )
# List headers that should never be handled by Flask-RESTX
HEADERS_BLACKLIST = ( " Content-Length " , )
DEFAULT_REPRESENTATIONS = [ ( " application/json " , output_json ) ]
log = logging . getLogger ( __name__ )
class Api ( object ) :
"""
The main entry point for the application .
You need to initialize it with a Flask Application : : :
>> > app = Flask ( __name__ )
>> > api = Api ( app )
Alternatively , you can use : meth : ` init_app ` to set the Flask application
after it has been constructed .
The endpoint parameter prefix all views and resources :
- The API root / documentation will be ` ` { endpoint } . root ` `
- A resource registered as ' resource ' will be available as ` ` { endpoint } . resource ` `
: param flask . Flask | flask . Blueprint app : the Flask application object or a Blueprint
: param str version : The API version ( used in Swagger documentation )
: param str title : The API title ( used in Swagger documentation )
: param str description : The API description ( used in Swagger documentation )
: param str terms_url : The API terms page URL ( used in Swagger documentation )
: param str contact : A contact email for the API ( used in Swagger documentation )
: param str license : The license associated to the API ( used in Swagger documentation )
: param str license_url : The license page URL ( used in Swagger documentation )
: param str endpoint : The API base endpoint ( default to ' api).
: param str default : The default namespace base name ( default to ' default ' )
: param str default_label : The default namespace label ( used in Swagger documentation )
: param str default_mediatype : The default media type to return
: param bool validate : Whether or not the API should perform input payload validation .
: param bool ordered : Whether or not preserve order models and marshalling .
: param str doc : The documentation path . If set to a false value , documentation is disabled .
( Default to ' / ' )
: param list decorators : Decorators to attach to every resource
: param bool catch_all_404s : Use : meth : ` handle_error `
to handle 404 errors throughout your app
: param dict authorizations : A Swagger Authorizations declaration as dictionary
: param bool serve_challenge_on_401 : Serve basic authentication challenge with 401
responses ( default ' False ' )
: param FormatChecker format_checker : A jsonschema . FormatChecker object that is hooked into
the Model validator . A default or a custom FormatChecker can be provided ( e . g . , with custom
checkers ) , otherwise the default action is to not enforce any format validation .
: param url_scheme : If set to a string ( e . g . http , https ) , then the specs_url and base_url will explicitly use this
scheme regardless of how the application is deployed . This is necessary for some deployments behind a reverse
proxy .
: param str default_swagger_filename : The default swagger filename .
"""
def __init__ (
self ,
app = None ,
version = " 1.0 " ,
title = None ,
description = None ,
terms_url = None ,
license = None ,
license_url = None ,
contact = None ,
contact_url = None ,
contact_email = None ,
authorizations = None ,
security = None ,
doc = " / " ,
default_id = default_id ,
default = " default " ,
default_label = " Default namespace " ,
validate = None ,
tags = None ,
prefix = " " ,
ordered = False ,
default_mediatype = " application/json " ,
decorators = None ,
catch_all_404s = False ,
serve_challenge_on_401 = False ,
format_checker = None ,
url_scheme = None ,
default_swagger_filename = " swagger.json " ,
* * kwargs
) :
self . version = version
self . title = title or " API "
self . description = description
self . terms_url = terms_url
self . contact = contact
self . contact_email = contact_email
self . contact_url = contact_url
self . license = license
self . license_url = license_url
self . authorizations = authorizations
self . security = security
self . default_id = default_id
self . ordered = ordered
self . _validate = validate
self . _doc = doc
self . _doc_view = None
self . _default_error_handler = None
self . tags = tags or [ ]
self . error_handlers = OrderedDict (
{
ParseError : mask_parse_error_handler ,
MaskError : mask_error_handler ,
}
)
self . _schema = None
self . models = { }
self . _refresolver = None
self . format_checker = format_checker
self . namespaces = [ ]
self . default_swagger_filename = default_swagger_filename
self . ns_paths = dict ( )
self . representations = OrderedDict ( DEFAULT_REPRESENTATIONS )
self . urls = { }
self . prefix = prefix
self . default_mediatype = default_mediatype
self . decorators = decorators if decorators else [ ]
self . catch_all_404s = catch_all_404s
self . serve_challenge_on_401 = serve_challenge_on_401
self . blueprint_setup = None
self . endpoints = set ( )
self . resources = [ ]
self . app = None
self . blueprint = None
# must come after self.app initialisation to prevent __getattr__ recursion
# in self._configure_namespace_logger
self . default_namespace = self . namespace (
default ,
default_label ,
endpoint = " {0} -declaration " . format ( default ) ,
validate = validate ,
api = self ,
path = " / " ,
)
self . url_scheme = url_scheme
if app is not None :
self . app = app
self . init_app ( app )
# super(Api, self).__init__(app, **kwargs)
def init_app ( self , app , * * kwargs ) :
"""
Allow to lazy register the API on a Flask application : :
>> > app = Flask ( __name__ )
>> > api = Api ( )
>> > api . init_app ( app )
: param flask . Flask app : the Flask application object
: param str title : The API title ( used in Swagger documentation )
: param str description : The API description ( used in Swagger documentation )
: param str terms_url : The API terms page URL ( used in Swagger documentation )
: param str contact : A contact email for the API ( used in Swagger documentation )
: param str license : The license associated to the API ( used in Swagger documentation )
: param str license_url : The license page URL ( used in Swagger documentation )
: param url_scheme : If set to a string ( e . g . http , https ) , then the specs_url and base_url will explicitly use
this scheme regardless of how the application is deployed . This is necessary for some deployments behind a
reverse proxy .
"""
self . app = app
self . title = kwargs . get ( " title " , self . title )
self . description = kwargs . get ( " description " , self . description )
self . terms_url = kwargs . get ( " terms_url " , self . terms_url )
self . contact = kwargs . get ( " contact " , self . contact )
self . contact_url = kwargs . get ( " contact_url " , self . contact_url )
self . contact_email = kwargs . get ( " contact_email " , self . contact_email )
self . license = kwargs . get ( " license " , self . license )
self . license_url = kwargs . get ( " license_url " , self . license_url )
self . url_scheme = kwargs . get ( " url_scheme " , self . url_scheme )
self . _add_specs = kwargs . get ( " add_specs " , True )
self . _register_specs ( app )
self . _register_doc ( app )
# If app is a blueprint, defer the initialization
try :
app . record ( self . _deferred_blueprint_init )
# Flask.Blueprint has a 'record' attribute, Flask.Api does not
except AttributeError :
self . _init_app ( app )
else :
self . blueprint = app
def _init_app ( self , app ) :
"""
Perform initialization actions with the given : class : ` flask . Flask ` object .
: param flask . Flask app : The flask application object
"""
app . handle_exception = partial ( self . error_router , app . handle_exception )
app . handle_user_exception = partial (
self . error_router , app . handle_user_exception
)
if len ( self . resources ) > 0 :
for resource , namespace , urls , kwargs in self . resources :
self . _register_view ( app , resource , namespace , * urls , * * kwargs )
for ns in self . namespaces :
self . _configure_namespace_logger ( app , ns )
self . _register_apidoc ( app )
self . _validate = (
self . _validate
if self . _validate is not None
else app . config . get ( " RESTX_VALIDATE " , False )
)
app . config . setdefault ( " RESTX_MASK_HEADER " , " X-Fields " )
app . config . setdefault ( " RESTX_MASK_SWAGGER " , True )
app . config . setdefault ( " RESTX_INCLUDE_ALL_MODELS " , False )
# check for deprecated config variable names
if " ERROR_404_HELP " in app . config :
app . config [ " RESTX_ERROR_404_HELP " ] = app . config [ " ERROR_404_HELP " ]
warnings . warn (
" ' ERROR_404_HELP ' config setting is deprecated and will be "
" removed in the future. Use ' RESTX_ERROR_404_HELP ' instead. " ,
DeprecationWarning ,
)
def __getattr__ ( self , name ) :
try :
return getattr ( self . default_namespace , name )
except AttributeError :
raise AttributeError ( " Api does not have {0} attribute " . format ( name ) )
def _complete_url ( self , url_part , registration_prefix ) :
"""
This method is used to defer the construction of the final url in
the case that the Api is created with a Blueprint .
: param url_part : The part of the url the endpoint is registered with
: param registration_prefix : The part of the url contributed by the
blueprint . Generally speaking , BlueprintSetupState . url_prefix
"""
parts = ( registration_prefix , self . prefix , url_part )
return " " . join ( part for part in parts if part )
def _register_apidoc ( self , app ) :
conf = app . extensions . setdefault ( " restx " , { } )
if not conf . get ( " apidoc_registered " , False ) :
app . register_blueprint ( apidoc . apidoc )
conf [ " apidoc_registered " ] = True
def _register_specs ( self , app_or_blueprint ) :
if self . _add_specs :
endpoint = str ( " specs " )
self . _register_view (
app_or_blueprint ,
SwaggerView ,
self . default_namespace ,
" / " + self . default_swagger_filename ,
endpoint = endpoint ,
resource_class_args = ( self , ) ,
)
self . endpoints . add ( endpoint )
def _register_doc ( self , app_or_blueprint ) :
if self . _add_specs and self . _doc :
# Register documentation before root if enabled
app_or_blueprint . add_url_rule ( self . _doc , " doc " , self . render_doc )
app_or_blueprint . add_url_rule ( self . prefix or " / " , " root " , self . render_root )
def register_resource ( self , namespace , resource , * urls , * * kwargs ) :
endpoint = kwargs . pop ( " endpoint " , None )
endpoint = str ( endpoint or self . default_endpoint ( resource , namespace ) )
kwargs [ " endpoint " ] = endpoint
self . endpoints . add ( endpoint )
if self . app is not None :
self . _register_view ( self . app , resource , namespace , * urls , * * kwargs )
else :
self . resources . append ( ( resource , namespace , urls , kwargs ) )
return endpoint
def _configure_namespace_logger ( self , app , namespace ) :
for handler in app . logger . handlers :
namespace . logger . addHandler ( handler )
namespace . logger . setLevel ( app . logger . level )
def _register_view ( self , app , resource , namespace , * urls , * * kwargs ) :
endpoint = kwargs . pop ( " endpoint " , None ) or camel_to_dash ( resource . __name__ )
resource_class_args = kwargs . pop ( " resource_class_args " , ( ) )
resource_class_kwargs = kwargs . pop ( " resource_class_kwargs " , { } )
# NOTE: 'view_functions' is cleaned up from Blueprint class in Flask 1.0
if endpoint in getattr ( app , " view_functions " , { } ) :
previous_view_class = app . view_functions [ endpoint ] . __dict__ [ " view_class " ]
# if you override the endpoint with a different class, avoid the
# collision by raising an exception
if previous_view_class != resource :
msg = " This endpoint ( %s ) is already set to the class %s . "
raise ValueError ( msg % ( endpoint , previous_view_class . __name__ ) )
resource . mediatypes = self . mediatypes_method ( ) # Hacky
resource . endpoint = endpoint
resource_func = self . output (
resource . as_view (
endpoint , self , * resource_class_args , * * resource_class_kwargs
)
)
# Apply Namespace and Api decorators to a resource
for decorator in chain ( namespace . decorators , self . decorators ) :
resource_func = decorator ( resource_func )
for url in urls :
# If this Api has a blueprint
if self . blueprint :
# And this Api has been setup
if self . blueprint_setup :
# Set the rule to a string directly, as the blueprint is already
# set up.
self . blueprint_setup . add_url_rule (
url , view_func = resource_func , * * kwargs
)
continue
else :
# Set the rule to a function that expects the blueprint prefix
# to construct the final url. Allows deferment of url finalization
# in the case that the associated Blueprint has not yet been
# registered to an application, so we can wait for the registration
# prefix
rule = partial ( self . _complete_url , url )
else :
# If we've got no Blueprint, just build a url with no prefix
rule = self . _complete_url ( url , " " )
# Add the url to the application or blueprint
app . add_url_rule ( rule , view_func = resource_func , * * kwargs )
def output ( self , resource ) :
"""
Wraps a resource ( as a flask view function ) ,
for cases where the resource does not directly return a response object
: param resource : The resource as a flask view function
"""
@wraps ( resource )
def wrapper ( * args , * * kwargs ) :
resp = resource ( * args , * * kwargs )
if isinstance ( resp , BaseResponse ) :
return resp
data , code , headers = unpack ( resp )
return self . make_response ( data , code , headers = headers )
return wrapper
def make_response ( self , data , * args , * * kwargs ) :
"""
Looks up the representation transformer for the requested media
type , invoking the transformer to create a response object . This
defaults to default_mediatype if no transformer is found for the
requested mediatype . If default_mediatype is None , a 406 Not
Acceptable response will be sent as per RFC 2616 section 14.1
: param data : Python object containing response data to be transformed
"""
default_mediatype = (
kwargs . pop ( " fallback_mediatype " , None ) or self . default_mediatype
)
mediatype = request . accept_mimetypes . best_match (
self . representations ,
default = default_mediatype ,
)
if mediatype is None :
raise NotAcceptable ( )
if mediatype in self . representations :
resp = self . representations [ mediatype ] ( data , * args , * * kwargs )
resp . headers [ " Content-Type " ] = mediatype
return resp
elif mediatype == " text/plain " :
resp = original_flask_make_response ( str ( data ) , * args , * * kwargs )
resp . headers [ " Content-Type " ] = " text/plain "
return resp
else :
raise InternalServerError ( )
def documentation ( self , func ) :
""" A decorator to specify a view function for the documentation """
self . _doc_view = func
return func
def render_root ( self ) :
self . abort ( HTTPStatus . NOT_FOUND )
def render_doc ( self ) :
""" Override this method to customize the documentation page """
if self . _doc_view :
return self . _doc_view ( )
elif not self . _doc :
self . abort ( HTTPStatus . NOT_FOUND )
return apidoc . ui_for ( self )
def default_endpoint ( self , resource , namespace ) :
"""
Provide a default endpoint for a resource on a given namespace .
Endpoints are ensured not to collide .
Override this method specify a custom algorithm for default endpoint .
: param Resource resource : the resource for which we want an endpoint
: param Namespace namespace : the namespace holding the resource
: returns str : An endpoint name
"""
endpoint = camel_to_dash ( resource . __name__ )
if namespace is not self . default_namespace :
endpoint = " {ns.name} _ {endpoint} " . format ( ns = namespace , endpoint = endpoint )
if endpoint in self . endpoints :
suffix = 2
while True :
new_endpoint = " {base} _ {suffix} " . format ( base = endpoint , suffix = suffix )
if new_endpoint not in self . endpoints :
endpoint = new_endpoint
break
suffix + = 1
return endpoint
def get_ns_path ( self , ns ) :
return self . ns_paths . get ( ns )
def ns_urls ( self , ns , urls ) :
path = self . get_ns_path ( ns ) or ns . path
return [ path + url for url in urls ]
def add_namespace ( self , ns , path = None ) :
"""
This method registers resources from namespace for current instance of api .
You can use argument path for definition custom prefix url for namespace .
: param Namespace ns : the namespace
: param path : registration prefix of namespace
"""
if ns not in self . namespaces :
self . namespaces . append ( ns )
if self not in ns . apis :
ns . apis . append ( self )
# Associate ns with prefix-path
if path is not None :
self . ns_paths [ ns ] = path
# Register resources
for r in ns . resources :
urls = self . ns_urls ( ns , r . urls )
self . register_resource ( ns , r . resource , * urls , * * r . kwargs )
# Register models
for name , definition in ns . models . items ( ) :
self . models [ name ] = definition
if not self . blueprint and self . app is not None :
self . _configure_namespace_logger ( self . app , ns )
def namespace ( self , * args , * * kwargs ) :
"""
A namespace factory .
: returns Namespace : a new namespace instance
"""
kwargs [ " ordered " ] = kwargs . get ( " ordered " , self . ordered )
ns = Namespace ( * args , * * kwargs )
self . add_namespace ( ns )
return ns
def endpoint ( self , name ) :
if self . blueprint :
return " {0} . {1} " . format ( self . blueprint . name , name )
else :
return name
@property
def specs_url ( self ) :
"""
The Swagger specifications relative url ( ie . ` swagger . json ` ) . If
the spec_url_scheme attribute is set , then the full url is provided instead
( e . g . http : / / localhost / swaggger . json ) .
: rtype : str
"""
external = None if self . url_scheme is None else True
return url_for (
self . endpoint ( " specs " ) , _scheme = self . url_scheme , _external = external
)
@property
def base_url ( self ) :
"""
The API base absolute url
: rtype : str
"""
return url_for ( self . endpoint ( " root " ) , _scheme = self . url_scheme , _external = True )
@property
def base_path ( self ) :
"""
The API path
: rtype : str
"""
return url_for ( self . endpoint ( " root " ) , _external = False )
@cached_property
def __schema__ ( self ) :
"""
The Swagger specifications / schema for this API
: returns dict : the schema as a serializable dict
"""
if not self . _schema :
try :
self . _schema = Swagger ( self ) . as_dict ( )
except Exception :
# Log the source exception for debugging purpose
# and return an error message
msg = " Unable to render schema "
log . exception ( msg ) # This will provide a full traceback
return { " error " : msg }
return self . _schema
@property
def _own_and_child_error_handlers ( self ) :
rv = OrderedDict ( )
rv . update ( self . error_handlers )
for ns in self . namespaces :
for exception , handler in ns . error_handlers . items ( ) :
rv [ exception ] = handler
return rv
def errorhandler ( self , exception ) :
""" A decorator to register an error handler for a given exception """
if inspect . isclass ( exception ) and issubclass ( exception , Exception ) :
# Register an error handler for a given exception
def wrapper ( func ) :
self . error_handlers [ exception ] = func
return func
return wrapper
else :
# Register the default error handler
self . _default_error_handler = exception
return exception
def owns_endpoint ( self , endpoint ) :
"""
Tests if an endpoint name ( not path ) belongs to this Api .
Takes into account the Blueprint name part of the endpoint name .
: param str endpoint : The name of the endpoint being checked
: return : bool
"""
if self . blueprint :
if endpoint . startswith ( self . blueprint . name ) :
endpoint = endpoint . split ( self . blueprint . name + " . " , 1 ) [ - 1 ]
else :
return False
return endpoint in self . endpoints
def _should_use_fr_error_handler ( self ) :
"""
Determine if error should be handled with FR or default Flask
The goal is to return Flask error handlers for non - FR - related routes ,
and FR errors ( with the correct media type ) for FR endpoints . This
method currently handles 404 and 405 errors .
: return : bool
"""
adapter = current_app . create_url_adapter ( request )
try :
adapter . match ( )
except MethodNotAllowed as e :
# Check if the other HTTP methods at this url would hit the Api
valid_route_method = e . valid_methods [ 0 ]
rule , _ = adapter . match ( method = valid_route_method , return_rule = True )
return self . owns_endpoint ( rule . endpoint )
except NotFound :
return self . catch_all_404s
except Exception :
# Werkzeug throws other kinds of exceptions, such as Redirect
pass
def _has_fr_route ( self ) :
""" Encapsulating the rules for whether the request was to a Flask endpoint """
# 404's, 405's, which might not have a url_rule
if self . _should_use_fr_error_handler ( ) :
return True
# for all other errors, just check if FR dispatched the route
if not request . url_rule :
return False
return self . owns_endpoint ( request . url_rule . endpoint )
def error_router ( self , original_handler , e ) :
"""
This function decides whether the error occurred in a flask - restx
endpoint or not . If it happened in a flask - restx endpoint , our
handler will be dispatched . If it happened in an unrelated view , the
app ' s original error handler will be dispatched.
In the event that the error occurred in a flask - restx endpoint but
the local handler can ' t resolve the situation, the router will fall
back onto the original_handler as last resort .
: param function original_handler : the original Flask error handler for the app
: param Exception e : the exception raised while handling the request
"""
if self . _has_fr_route ( ) :
try :
return self . handle_error ( e )
except Exception as f :
return original_handler ( f )
return original_handler ( e )
def _propagate_exceptions ( self ) :
"""
Returns the value of the ` ` PROPAGATE_EXCEPTIONS ` ` configuration
value in case it ' s set, otherwise return true if app.debug or
app . testing is set . This method was deprecated in Flask 2.3 but
we still need it for our error handlers .
"""
rv = current_app . config . get ( " PROPAGATE_EXCEPTIONS " )
if rv is not None :
return rv
return current_app . testing or current_app . debug
def handle_error ( self , e ) :
"""
Error handler for the API transforms a raised exception into a Flask response ,
with the appropriate HTTP status code and body .
: param Exception e : the raised Exception object
"""
# When propagate_exceptions is set, do not return the exception to the
# client if a handler is configured for the exception.
if (
not isinstance ( e , HTTPException )
and self . _propagate_exceptions ( )
and not isinstance ( e , tuple ( self . _own_and_child_error_handlers . keys ( ) ) )
) :
exc_type , exc_value , tb = sys . exc_info ( )
if exc_value is e :
raise
else :
raise e
include_message_in_response = current_app . config . get (
" ERROR_INCLUDE_MESSAGE " , True
)
default_data = { }
headers = Headers ( )
for typecheck , handler in self . _own_and_child_error_handlers . items ( ) :
if isinstance ( e , typecheck ) :
result = handler ( e )
default_data , code , headers = unpack (
result , HTTPStatus . INTERNAL_SERVER_ERROR
)
break
else :
# Flask docs say: "This signal is not sent for HTTPException or other exceptions that have error handlers
# registered, unless the exception was raised from an error handler."
got_request_exception . send ( current_app . _get_current_object ( ) , exception = e )
if isinstance ( e , HTTPException ) :
code = None
if e . code is not None :
code = HTTPStatus ( e . code )
elif e . response is not None :
code = HTTPStatus ( e . response . status_code )
if include_message_in_response :
default_data = { " message " : e . description or code . phrase }
headers = e . get_response ( ) . headers
elif self . _default_error_handler :
result = self . _default_error_handler ( e )
default_data , code , headers = unpack (
result , HTTPStatus . INTERNAL_SERVER_ERROR
)
else :
code = HTTPStatus . INTERNAL_SERVER_ERROR
if include_message_in_response :
default_data = {
" message " : code . phrase ,
}
if include_message_in_response :
default_data [ " message " ] = default_data . get ( " message " , str ( e ) )
data = getattr ( e , " data " , default_data )
fallback_mediatype = None
if code > = HTTPStatus . INTERNAL_SERVER_ERROR :
exc_info = sys . exc_info ( )
if exc_info [ 1 ] is None :
exc_info = None
current_app . log_exception ( exc_info )
elif (
code == HTTPStatus . NOT_FOUND
and current_app . config . get ( " RESTX_ERROR_404_HELP " , True )
and include_message_in_response
) :
data [ " message " ] = self . _help_on_404 ( data . get ( " message " , None ) )
elif code == HTTPStatus . NOT_ACCEPTABLE and self . default_mediatype is None :
# if we are handling NotAcceptable (406), make sure that
# make_response uses a representation we support as the
# default mediatype (so that make_response doesn't throw
# another NotAcceptable error).
supported_mediatypes = list ( self . representations . keys ( ) )
fallback_mediatype = (
supported_mediatypes [ 0 ] if supported_mediatypes else " text/plain "
)
# Remove blacklisted headers
for header in HEADERS_BLACKLIST :
headers . pop ( header , None )
resp = self . make_response (
data , code , headers , fallback_mediatype = fallback_mediatype
)
if code == HTTPStatus . UNAUTHORIZED :
resp = self . unauthorized ( resp )
return resp
def _help_on_404 ( self , message = None ) :
rules = dict (
[
( RE_RULES . sub ( " " , rule . rule ) , rule . rule )
for rule in current_app . url_map . iter_rules ( )
]
)
close_matches = difflib . get_close_matches ( request . path , rules . keys ( ) )
if close_matches :
# If we already have a message, add punctuation and continue it.
message = " " . join (
(
( message . rstrip ( " . " ) + " . " ) if message else " " ,
" You have requested this URI [ " ,
request . path ,
" ] but did you mean " ,
" or " . join ( ( rules [ match ] for match in close_matches ) ) ,
" ? " ,
)
)
return message
def as_postman ( self , urlvars = False , swagger = False ) :
"""
Serialize the API as Postman collection ( v1 )
: param bool urlvars : whether to include or not placeholders for query strings
: param bool swagger : whether to include or not the swagger . json specifications
"""
return PostmanCollectionV1 ( self , swagger = swagger ) . as_dict ( urlvars = urlvars )
@property
def payload ( self ) :
""" Store the input payload in the current request context """
return request . get_json ( )
@property
def refresolver ( self ) :
if not self . _refresolver :
self . _refresolver = RefResolver . from_schema ( self . __schema__ )
return self . _refresolver
@staticmethod
def _blueprint_setup_add_url_rule_patch (
blueprint_setup , rule , endpoint = None , view_func = None , * * options
) :
"""
Method used to patch BlueprintSetupState . add_url_rule for setup
state instance corresponding to this Api instance . Exists primarily
to enable _complete_url ' s function.
: param blueprint_setup : The BlueprintSetupState instance ( self )
: param rule : A string or callable that takes a string and returns a
string ( _complete_url ) that is the url rule for the endpoint
being registered
: param endpoint : See BlueprintSetupState . add_url_rule
: param view_func : See BlueprintSetupState . add_url_rule
: param * * options : See BlueprintSetupState . add_url_rule
"""
if callable ( rule ) :
rule = rule ( blueprint_setup . url_prefix )
elif blueprint_setup . url_prefix :
rule = blueprint_setup . url_prefix + rule
options . setdefault ( " subdomain " , blueprint_setup . subdomain )
if endpoint is None :
endpoint = endpoint_from_view_func ( view_func )
defaults = blueprint_setup . url_defaults
if " defaults " in options :
defaults = dict ( defaults , * * options . pop ( " defaults " ) )
blueprint_setup . app . add_url_rule (
rule ,
" %s . %s " % ( blueprint_setup . blueprint . name , endpoint ) ,
view_func ,
defaults = defaults ,
* * options
)
def _deferred_blueprint_init ( self , setup_state ) :
"""
Synchronize prefix between blueprint / api and registration options , then
perform initialization with setup_state . app : class : ` flask . Flask ` object .
When a : class : ` flask_restx . Api ` object is initialized with a blueprint ,
this method is recorded on the blueprint to be run when the blueprint is later
registered to a : class : ` flask . Flask ` object . This method also monkeypatches
BlueprintSetupState . add_url_rule with _blueprint_setup_add_url_rule_patch .
: param setup_state : The setup state object passed to deferred functions
during blueprint registration
: type setup_state : flask . blueprints . BlueprintSetupState
"""
self . blueprint_setup = setup_state
if setup_state . add_url_rule . __name__ != " _blueprint_setup_add_url_rule_patch " :
setup_state . _original_add_url_rule = setup_state . add_url_rule
setup_state . add_url_rule = MethodType (
Api . _blueprint_setup_add_url_rule_patch , setup_state
)
if not setup_state . first_registration :
raise ValueError ( " flask-restx blueprints can only be registered once. " )
self . _init_app ( setup_state . app )
def mediatypes_method ( self ) :
""" Return a method that returns a list of mediatypes """
return lambda resource_cls : self . mediatypes ( ) + [ self . default_mediatype ]
def mediatypes ( self ) :
""" Returns a list of requested mediatypes sent in the Accept header """
return [
h
for h , q in sorted (
request . accept_mimetypes , key = operator . itemgetter ( 1 ) , reverse = True
)
]
def representation ( self , mediatype ) :
"""
Allows additional representation transformers to be declared for the
api . Transformers are functions that must be decorated with this
method , passing the mediatype the transformer represents . Three
arguments are passed to the transformer :
* The data to be represented in the response body
* The http status code
* A dictionary of headers
The transformer should convert the data appropriately for the mediatype
and return a Flask response object .
Ex : :
@api.representation ( ' application/xml ' )
def xml ( data , code , headers ) :
resp = make_response ( convert_data_to_xml ( data ) , code )
resp . headers . extend ( headers )
return resp
"""
def wrapper ( func ) :
self . representations [ mediatype ] = func
return func
return wrapper
def unauthorized ( self , response ) :
""" Given a response, change it to ask for credentials """
if self . serve_challenge_on_401 :
realm = current_app . config . get ( " HTTP_BASIC_AUTH_REALM " , " flask-restx " )
challenge = ' {0} realm= " {1} " ' . format ( " Basic " , realm )
response . headers [ " WWW-Authenticate " ] = challenge
return response
def url_for ( self , resource , * * values ) :
"""
Generates a URL to the given resource .
Works like : func : ` flask . url_for ` .
"""
endpoint = resource . endpoint
if self . blueprint :
endpoint = " {0} . {1} " . format ( self . blueprint . name , endpoint )
return url_for ( endpoint , * * values )
class SwaggerView ( Resource ) :
""" Render the Swagger specifications as JSON """
def get ( self ) :
schema = self . api . __schema__
return (
schema ,
HTTPStatus . INTERNAL_SERVER_ERROR if " error " in schema else HTTPStatus . OK ,
)
def mediatypes ( self ) :
return [ " application/json " ]
def mask_parse_error_handler ( error ) :
""" When a mask can ' t be parsed """
return { " message " : " Mask parse error: {0} " . format ( error ) } , HTTPStatus . BAD_REQUEST
def mask_error_handler ( error ) :
""" When any error occurs on mask """
return { " message " : " Mask error: {0} " . format ( error ) } , HTTPStatus . BAD_REQUEST