Example #1
0
    def map_http_methods(self, resource, custom_map=None):
        if not custom_map:
            return routing.map_http_methods(resource)

        method_map = {}
        for method in COMBINED_METHODS:
            if method in custom_map:
                responder = getattr(resource, custom_map[method])
                if callable(responder):
                    method_map[method] = responder
        return method_map
Example #2
0
    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.map_http_methods(resource)
        routing.set_default_responders(method_map)
        self._router.add_route(uri_template, method_map, resource, *args,
                               **kwargs)
Example #3
0
    def add_route(self, uri_template, resource, *args, **kwargs):
        """Associate 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.map_http_methods(resource)
        routing.set_default_responders(method_map)
        self._router.add_route(uri_template, method_map, resource, *args,
                               **kwargs)
Example #4
0
    def add_route(self, uri_template, resource, *args, **kwargs):
        """Associate 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.

        Keyword Args:
            suffix (str): Optional responder name suffix for this route. If
                a suffix is provided, Falcon will map GET requests to
                ``on_get_{suffix}()``, POST requests to ``on_post_{suffix}()``,
                etc. In this way, multiple closely-related routes can be
                mapped to the same resource. For example, a single resource
                class can use suffixed responders to distinguish requests
                for a single item vs. a collection of those same items.
                Another class might use a suffixed responder to handle
                a shortlink route in addition to the regular route for the
                resource.

        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 '//'")

        # NOTE(santeyio): This is a not very nice way to catch the suffix
        # keyword. In python 3 it can be specified explicitly in the function
        # definition, e.g.
        # `add_route(self, uri_template, resource, *args, suffix=None, **kwargs)`
        # but python 2 won't accept args like this.
        suffix = kwargs.pop('suffix', None)

        method_map = routing.map_http_methods(resource, suffix=suffix)
        routing.set_default_responders(method_map)
        self._router.add_route(uri_template, method_map, resource, *args,
                               **kwargs)
 def find(self, uri, req=None):
     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 = map_http_methods(resource)
                 set_default_responders(method_map)
                 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
Example #6
0
    def add_route(self, uri_template, resource, suffix=None, **kwargs):
        """Associate 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.

        Keyword Args:
            suffix (str): Optional responder name suffix for this route. If
                a suffix is provided, Falcon will map GET requests to
                ``on_get_{suffix}()``, POST requests to ``on_post_{suffix}()``,
                etc. In this way, multiple closely-related routes can be
                mapped to the same resource. For example, a single resource
                class can use suffixed responders to distinguish requests
                for a single item vs. a collection of those same items.
                Another class might use a suffixed responder to handle
                a shortlink route in addition to the regular route for the
                resource.

        Note:
            Any additional keyword arguments not defined above are passed
            through to the underlying router's ``add_route()`` method. The
            default router ignores any additional keyword arguments, but
            custom routers may take advantage of this feature to receive
            additional options when setting up routes. Custom routers MUST
            accept such arguments using the variadic pattern (``**kwargs``), and
            ignore any keyword arguments that they don't support.
        """

        # 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.map_http_methods(resource, suffix=suffix)
        routing.set_default_responders(method_map)
        self._router.add_route(uri_template, method_map, resource, **kwargs)