def on_options(self, req, resp, **kwargs): """Handle options requests""" method_map = routing.create_http_method_map(self) for method in method_map: if method_map.get(method).__name__ != 'method_not_allowed': resp.append_header('Allow', method) resp.status = falcon.HTTP_200
def add_route(self, uri_template, resource): """Associates a templatized URI path with a resource. A resource is an instance of a class that defines various on_* "responder" methods, one for each HTTP method the resource allows. For example, to support GET, simply define an `on_get` responder. If a client requests an unsupported method, Falcon will respond with "405 Method not allowed". Responders must always define at least two arguments to receive request and response objects, respectively. For example:: def on_post(self, req, resp): pass In addition, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also `add_sink`). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. """ # NOTE(richardolsson): Doing the validation here means it doesn't have # to be duplicated in every future router implementation. if not isinstance(uri_template, six.string_types): raise TypeError('uri_template is not a string') if not uri_template.startswith('/'): raise ValueError("uri_template must start with '/'") if '//' in uri_template: raise ValueError("uri_template may not contain '//'") method_map = routing.create_http_method_map(resource, self._before, self._after) self._router.add_route(uri_template, method_map, resource)
def add_route(self, uri_template, resource): """Associates a templatized URI path with a resource. A resource is an instance of a class that defines various on_* "responder" methods, one for each HTTP method the resource allows. For example, to support GET, simply define an `on_get` responder. If a client requests an unsupported method, Falcon will respond with "405 Method not allowed". Responders must always define at least two arguments to receive request and response objects, respectively. For example:: def on_post(self, req, resp): pass In addition, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also `add_sink`). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. """ # NOTE(richardolsson): Doing the validation here means it doesn't have # to be duplicated in every future router implementation. if not isinstance(uri_template, six.string_types): raise TypeError('uri_template is not a string') if not uri_template.startswith('/'): raise ValueError("uri_template must start with '/'") if '//' in uri_template: raise ValueError("uri_template may not contain '//'") method_map = routing.create_http_method_map( resource, self._before, self._after) self._router.add_route(uri_template, method_map, resource)
def add_route(self, uri_template, resource, *args, **kwargs): """Associates a templatized URI path with a resource. Falcon routes incoming requests to resources based on a set of URI templates. If the path requested by the client matches the template for a given route, the request is then passed on to the associated resource for processing. If no route matches the request, control then passes to a default responder that simply raises an instance of :class:`~.HTTPNotFound`. (See also: :ref:`Routing <routing>`) Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered. (See also: :meth:`~.add_sink`) resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. Note: Any additional args and kwargs not defined above are passed through to the underlying router's ``add_route()`` method. The default router does not expect any additional arguments, but custom routers may take advantage of this feature to receive additional options when setting up routes. """ # NOTE(richardolsson): Doing the validation here means it doesn't have # to be duplicated in every future router implementation. if not isinstance(uri_template, six.string_types): raise TypeError('uri_template is not a string') if not uri_template.startswith('/'): raise ValueError("uri_template must start with '/'") if '//' in uri_template: raise ValueError("uri_template may not contain '//'") method_map = routing.create_http_method_map(resource) self._router.add_route(uri_template, method_map, resource, *args, **kwargs)
def add_route(self, uri_template, resource): """Associates a templatized URI path with a resource. A resource is an instance of a class that defines various on_* "responder" methods, one for each HTTP method the resource allows. For example, to support GET, simply define an `on_get` responder. If a client requests an unsupported method, Falcon will respond with "405 Method not allowed". Responders must always define at least two arguments to receive request and response objects, respectively. For example:: def on_post(self, req, resp): pass In addition, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also `add_sink`). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. """ uri_fields, path_template = routing.compile_uri_template(uri_template) method_map = routing.create_http_method_map( resource, uri_fields, self._before, self._after) # Insert at the head of the list in case we get duplicate # adds (will cause the last one to win). self._routes.insert(0, (path_template, method_map, resource))
def add_route(self, uri_template, resource): """Associates a templatized URI path with a resource. A resource is an instance of a class that defines various on_* "responder" methods, one for each HTTP method the resource allows. For example, to support GET, simply define an `on_get` responder. If a client requests an unsupported method, Falcon will respond with "405 Method not allowed". Responders must always define at least two arguments to receive request and response objects, respectively. For example:: def on_post(self, req, resp): pass In addition, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also ``add_sink``). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. """ uri_fields, path_template = routing.compile_uri_template(uri_template) method_map = routing.create_http_method_map(resource, uri_fields, self._before, self._after) # Insert at the head of the list in case we get duplicate # adds (will cause the last one to win). self._routes.insert(0, (path_template, method_map, resource))
def add_route(self, uri_template, resource, *args, **kwargs): """Associates a templatized URI path with a resource. A resource is an instance of a class that defines various "responder" methods, one for each HTTP method the resource allows. Responder names start with `on_` and are named according to which HTTP method they handle, as in `on_get`, `on_post`, `on_put`, etc. If your resource does not support a particular HTTP method, simply omit the corresponding responder and Falcon will reply with "405 Method not allowed" if that method is ever requested. Responders must always define at least two arguments to receive request and response objects, respectively. For example:: def on_post(self, req, resp): pass In addition, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Individual path segments may contain one or more field expressions. For example:: /repos/{org}/{repo}/compare/{usr0}:{branch0}...{usr1}:{branch1} Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also `add_sink`). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. Note: Any additional args and kwargs not defined above are passed through to the underlying router's ``add_route()`` method. The default router does not expect any additional arguments, but custom routers may take advantage of this feature to receive additional options when setting up routes. """ # NOTE(richardolsson): Doing the validation here means it doesn't have # to be duplicated in every future router implementation. if not isinstance(uri_template, six.string_types): raise TypeError('uri_template is not a string') if not uri_template.startswith('/'): raise ValueError("uri_template must start with '/'") if '//' in uri_template: raise ValueError("uri_template may not contain '//'") method_map = routing.create_http_method_map(resource) self._router.add_route(uri_template, method_map, resource, *args, **kwargs)
def add_route(self, uri_template, resource, *args, **kwargs): """Associates a templatized URI path with a resource. Note: The following information describes the behavior of Falcon's default router. A resource is an instance of a class that defines various "responder" methods, one for each HTTP method the resource allows. Responder names start with `on_` and are named according to which HTTP method they handle, as in `on_get`, `on_post`, `on_put`, etc. Note: If your resource does not support a particular HTTP method, simply omit the corresponding responder and Falcon will reply with "405 Method not allowed" if that method is ever requested. Responders must always define at least two arguments to receive :class:`~.Request` and :class:`~.Response` objects, respectively. For example:: def on_post(self, req, resp): pass The :class:`~.Request` object represents the incoming HTTP request. It exposes properties and methods for examining headers, query string parameters, and other metadata associated with the request. A file-like stream is also provided for reading any data that was included in the body of the request. The :class:`~.Response` object represents the application's HTTP response to the above request. It provides properties and methods for setting status, header and body data. The :class:`~.Response` object also exposes a dict-like :attr:`~.Response.context` property for passing arbitrary data to hooks and middleware methods. This property could be used, for example, to provide a resource representation to a middleware component that would then serialize the representation according to the client's preferred media type. Note: Rather than directly manipulate the :class:`~.Response` object, a responder may raise an instance of either :class:`~.HTTPError` or :class:`~.HTTPStatus`. In addition to the standard `req` and `resp` parameters, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Individual path segments may contain one or more field expressions, and fields need not span the entire path segment. For example:: /repos/{org}/{repo}/compare/{usr0}:{branch0}...{usr1}:{branch1} /serviceRoot/People('{name}') Note: Because field names correspond to argument names in responder methods, they must be valid Python identifiers. Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also `add_sink`). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. Note: Any additional args and kwargs not defined above are passed through to the underlying router's ``add_route()`` method. The default router does not expect any additional arguments, but custom routers may take advantage of this feature to receive additional options when setting up routes. """ # NOTE(richardolsson): Doing the validation here means it doesn't have # to be duplicated in every future router implementation. if not isinstance(uri_template, six.string_types): raise TypeError('uri_template is not a string') if not uri_template.startswith('/'): raise ValueError("uri_template must start with '/'") if '//' in uri_template: raise ValueError("uri_template may not contain '//'") method_map = routing.create_http_method_map(resource) self._router.add_route(uri_template, method_map, resource, *args, **kwargs)
def add_route(self, uri_template, resource, *args, **kwargs): """Associates a templatized URI path with a resource. Note: The following information describes the behavior of Falcon's default router. A resource is an instance of a class that defines various "responder" methods, one for each HTTP method the resource allows. Responder names start with `on_` and are named according to which HTTP method they handle, as in `on_get`, `on_post`, `on_put`, etc. If your resource does not support a particular HTTP method, simply omit the corresponding responder and Falcon will reply with "405 Method not allowed" if that method is ever requested. Responders must always define at least two arguments to receive request and response objects, respectively. For example:: def on_post(self, req, resp): pass In addition, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. Note: Since field names correspond to argument names in responder methods, they must be valid Python identifiers. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Individual path segments may contain one or more field expressions:: /repos/{org}/{repo}/compare/{usr0}:{branch0}...{usr1}:{branch1} Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also `add_sink`). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. Note: Any additional args and kwargs not defined above are passed through to the underlying router's ``add_route()`` method. The default router does not expect any additional arguments, but custom routers may take advantage of this feature to receive additional options when setting up routes. """ # NOTE(richardolsson): Doing the validation here means it doesn't have # to be duplicated in every future router implementation. if not isinstance(uri_template, six.string_types): raise TypeError('uri_template is not a string') if not uri_template.startswith('/'): raise ValueError("uri_template must start with '/'") if '//' in uri_template: raise ValueError("uri_template may not contain '//'") method_map = routing.create_http_method_map(resource) self._router.add_route(uri_template, method_map, resource, *args, **kwargs)
def find(self, uri): segments = uri.split(SLASH) # Since the path is always rooted at /, skip the first segment, which # will always be the empty string. segments.pop(0) this_segment = segments.pop(0) resource = self._root context = {} while True: # Plumb the API through to all child resources. api = getattr(resource, 'api', None) # See if any of the resource's child links match the next segment. for name in dir(resource): if name.startswith('__') and name.endswith('__'): continue attribute = getattr(resource, name, MISSING) assert attribute is not MISSING, name matcher = getattr(attribute, '__matcher__', MISSING) if matcher is MISSING: continue result = None if isinstance(matcher, str): # Is the matcher string a regular expression or plain # string? If it starts with a caret, it's a regexp. if matcher.startswith('^'): cre = re.compile(matcher) # Search against the entire remaining path. tmp_segments = segments[:] tmp_segments.insert(0, this_segment) remaining_path = SLASH.join(tmp_segments) mo = cre.match(remaining_path) if mo: result = attribute(context, segments, **mo.groupdict()) elif matcher == this_segment: result = attribute(context, segments) else: # The matcher is a callable. It returns None if it # doesn't match, and if it does, it returns a 3-tuple # containing the positional arguments, the keyword # arguments, and the remaining segments. The attribute is # then called with these arguments. Note that the matcher # wants to see the full remaining path components, which # includes the current hop. tmp_segments = segments[:] tmp_segments.insert(0, this_segment) matcher_result = matcher(tmp_segments) if matcher_result is not None: positional, keyword, segments = matcher_result result = attribute(context, segments, *positional, **keyword) # The attribute could return a 2-tuple giving the resource and # remaining path segments, or it could just return the result. # Of course, if the result is None, then the matcher did not # match. if result is None: continue elif isinstance(result, tuple): resource, segments = result else: resource = result # See if the context set an API and set it on the next # resource in the chain, falling back to the parent resource's # API if there is one. resource.api = context.pop('api', api) # The method could have truncated the remaining segments, # meaning, it's consumed all the path segments, or this is the # last path segment. In that case the resource we're left at # is the responder. if len(segments) == 0: # We're at the end of the path, so the root must be the # responder. method_map = create_http_method_map(resource) return resource, method_map, context this_segment = segments.pop(0) break else: # None of the attributes matched this path component, so the # response is a 404. return None, None, None
def _get_responder(self, req): path = req.path method = req.method path_segments = path.split("/") # Since the path is always rooted at /, skip the first segment, which # will always be the empty string. path_segments.pop(0) this_segment = path_segments.pop(0) resource = self._root while True: # See if there's a child matching the current segment. # See if any of the resource's child links match the next segment. for name in dir(resource): if name.startswith("__") and name.endswith("__"): continue attribute = getattr(resource, name, _missing) assert attribute is not _missing, name matcher = getattr(attribute, "__matcher__", _missing) if matcher is _missing: continue result = None if isinstance(matcher, str): # Is the matcher string a regular expression or plain # string? If it starts with a caret, it's a regexp. if matcher.startswith("^"): cre = re.compile(matcher) # Search against the entire remaining path. tmp_path_segments = path_segments[:] tmp_path_segments.insert(0, this_segment) remaining_path = SLASH.join(tmp_path_segments) mo = cre.match(remaining_path) if mo: result = attribute(req, path_segments, **mo.groupdict()) elif matcher == this_segment: result = attribute(req, path_segments) else: # The matcher is a callable. It returns None if it # doesn't match, and if it does, it returns a 3-tuple # containing the positional arguments, the keyword # arguments, and the remaining segments. The attribute is # then called with these arguments. Note that the matcher # wants to see the full remaining path components, which # includes the current hop. tmp_path_segments = path_segments[:] tmp_path_segments.insert(0, this_segment) matcher_result = matcher(req, tmp_path_segments) if matcher_result is not None: positional, keyword, path_segments = matcher_result result = attribute(req, path_segments, *positional, **keyword) # The attribute could return a 2-tuple giving the resource and # remaining path segments, or it could just return the # result. Of course, if the result is None, then the matcher # did not match. if result is None: continue elif isinstance(result, tuple): resource, path_segments = result else: resource = result # The method could have truncated the remaining segments, # meaning, it's consumed all the path segments, or this is the # last path segment. In that case the resource we're left at # is the responder. if len(path_segments) == 0: # We're at the end of the path, so the root must be the # responder. method_map = create_http_method_map(resource, None, None) responder = method_map[method] return responder, {}, resource this_segment = path_segments.pop(0) break else: # None of the attributes matched this path component, so the # response is a 404. return path_not_found, {}, None
def _get_responder(self, req): path = req.path method = req.method path_segments = path.split('/') # Since the path is always rooted at /, skip the first segment, which # will always be the empty string. path_segments.pop(0) this_segment = path_segments.pop(0) resource = self._root while True: # See if there's a child matching the current segment. # See if any of the resource's child links match the next segment. for name in dir(resource): if name.startswith('__') and name.endswith('__'): continue attribute = getattr(resource, name, _missing) assert attribute is not _missing, name matcher = getattr(attribute, '__matcher__', _missing) if matcher is _missing: continue result = None if isinstance(matcher, str): # Is the matcher string a regular expression or plain # string? If it starts with a caret, it's a regexp. if matcher.startswith('^'): cre = re.compile(matcher) # Search against the entire remaining path. tmp_path_segments = path_segments[:] tmp_path_segments.insert(0, this_segment) remaining_path = SLASH.join(tmp_path_segments) mo = cre.match(remaining_path) if mo: result = attribute(req, path_segments, **mo.groupdict()) elif matcher == this_segment: result = attribute(req, path_segments) else: # The matcher is a callable. It returns None if it # doesn't match, and if it does, it returns a 3-tuple # containing the positional arguments, the keyword # arguments, and the remaining segments. The attribute is # then called with these arguments. Note that the matcher # wants to see the full remaining path components, which # includes the current hop. tmp_path_segments = path_segments[:] tmp_path_segments.insert(0, this_segment) matcher_result = matcher(req, tmp_path_segments) if matcher_result is not None: positional, keyword, path_segments = matcher_result result = attribute(req, path_segments, *positional, **keyword) # The attribute could return a 2-tuple giving the resource and # remaining path segments, or it could just return the # result. Of course, if the result is None, then the matcher # did not match. if result is None: continue elif isinstance(result, tuple): resource, path_segments = result else: resource = result # The method could have truncated the remaining segments, # meaning, it's consumed all the path segments, or this is the # last path segment. In that case the resource we're left at # is the responder. if len(path_segments) == 0: # We're at the end of the path, so the root must be the # responder. method_map = create_http_method_map( resource, None, None, None) responder = method_map[method] return responder, {}, resource this_segment = path_segments.pop(0) break else: # None of the attributes matched this path component, so the # response is a 404. return path_not_found, {}, None
def find(self, uri): segments = uri.split(SLASH) # Since the path is always rooted at /, skip the first segment, which # will always be the empty string. segments.pop(0) this_segment = segments.pop(0) resource = self._root context = {} while True: # Plumb the API through to all child resources. api = getattr(resource, 'api', None) # See if any of the resource's child links match the next segment. for name in dir(resource): if name.startswith('__') and name.endswith('__'): continue attribute = getattr(resource, name, MISSING) assert attribute is not MISSING, name matcher = getattr(attribute, '__matcher__', MISSING) if matcher is MISSING: continue result = None if isinstance(matcher, str): # Is the matcher string a regular expression or plain # string? If it starts with a caret, it's a regexp. if matcher.startswith('^'): cre = re.compile(matcher) # Search against the entire remaining path. tmp_segments = segments[:] tmp_segments.insert(0, this_segment) remaining_path = SLASH.join(tmp_segments) mo = cre.match(remaining_path) if mo: result = attribute( context, segments, **mo.groupdict()) elif matcher == this_segment: result = attribute(context, segments) else: # The matcher is a callable. It returns None if it # doesn't match, and if it does, it returns a 3-tuple # containing the positional arguments, the keyword # arguments, and the remaining segments. The attribute is # then called with these arguments. Note that the matcher # wants to see the full remaining path components, which # includes the current hop. tmp_segments = segments[:] tmp_segments.insert(0, this_segment) matcher_result = matcher(tmp_segments) if matcher_result is not None: positional, keyword, segments = matcher_result result = attribute( context, segments, *positional, **keyword) # The attribute could return a 2-tuple giving the resource and # remaining path segments, or it could just return the result. # Of course, if the result is None, then the matcher did not # match. if result is None: continue elif isinstance(result, tuple): resource, segments = result else: resource = result # See if the context set an API and set it on the next # resource in the chain, falling back to the parent resource's # API if there is one. resource.api = context.pop('api', api) # The method could have truncated the remaining segments, # meaning, it's consumed all the path segments, or this is the # last path segment. In that case the resource we're left at # is the responder. if len(segments) == 0: # We're at the end of the path, so the root must be the # responder. method_map = create_http_method_map(resource) return resource, method_map, context this_segment = segments.pop(0) break else: # None of the attributes matched this path component, so the # response is a 404. return None, None, None