The HTTP Resource Class

Jivago uses Resource classes to define HTTP routes. Routable classes should be annotated with the @Resource decorator with an URL path on which it should be mounted. Each routing method should then be annotated with one of the HTTP verbs (@GET, @POST, etc.) to make them known to the framework, and can optionally define a subpath using the @Path annotation.

from jivago.wsgi.annotations import Resource, Path
from jivago.wsgi.invocation.parameters import PathParam, QueryParam
from jivago.wsgi.methods import GET, POST, PUT
from jivago.wsgi.request.request import Request
from jivago.wsgi.request.response import Response

class HelloWorldResource(object):

    def get_hello(self) -> str:
        return "Hello"

    def post_hello(self, name: PathParam[str]) -> str:
        return "name: {}".format(name)

    def read_request_body_from_dict(self, body: dict) -> dict:
        return {"the body": body}

    def with_query(self, name: QueryParam[str]) -> str:
        return "Hello {}!".format(name)

    def read_raw_request(self, request: Request) -> Response:
        return Response(200, {}, "body")

Allowed Parameter Types

When handling a specific request, Jivago reads declared parameter types before invoking the routing function. Passed arguments can come from the query itself, from the body, from the raw request or a combination. Below are all the allowed parameter types :

  • QueryParam[T] : Reads the parameter matching the variable name from the query string. T should be either str, int or float.
  • OptionalQueryParam[T] : Identical to the above, except that it allows None values to be passed in place of a missing one.
  • PathParam[T] : Reads the parameter from the url path. The variable name should match the declared name in the @Path or the @Resource annotation. Route definitions use the {path-parameter-name} to declare these parameters.
  • dict : The request body which has been deserialized to a dictionary. Requires the body to be deserializable to a dictionary. (e.g. JSON).
  • A user-defined DTO : Any declared @Serializable class will be instantiated before invoking. This effectively acts as a JSON-schema validation.
  • Request : The raw Request object, as handled by Jivago. Useful when direct access to headers, query strings or the body is required.
  • Headers : The raw Headers object, containing all request headers. This class is simply a case-insensitive dictionary.

Manual Route Registration

Additionnal URL routes can be registered by creating a new RoutingTable which references classes and their methods. Note that the appropriate classes should be imported beforehand. The referenced resource class can be either an instance, or the actual class. In that case, it will be instantiated by the ServiceLocator, and should therefore be registered manually in the configure_service_locator context method.

from jivago.wsgi.methods import GET, POST
from jivago.wsgi.routing.table.tree_routing_table import TreeRoutingTable

my_routing_table = TreeRoutingTable()

my_routing_table.register_route(GET, "/hello", MyResourceClass, MyResourceClass.get_hello)
my_routing_table.register_route(POST, "/hello", MyResourceClass, MyResourceClass.get_hello)

This new RoutingTable can then be used to configure the Router object, which is used to serve all requests. The recommended way of configuring your application is by inheriting from the ProductionJivagoContext class, and then overriding the create_router_config method.

from jivago.config.production_jivago_context import ProductionJivagoContext
from jivago.config.router.router_builder import RouterBuilder
from jivago.jivago_application import JivagoApplication
from jivago.wsgi.routing.routing_rule import RoutingRule

class MyApplicationContext(ProductionJivagoContext):

    def create_router_config(self) -> RouterBuilder:
        return super().create_router_config() \
            .add_rule(RoutingRule("/", my_routing_table))

app = JivagoApplication(my_package, context=MyApplicationContext)

Serving static files

While it is not generally recommended to serve static files from a WSGI application for performance reasons, Jivago supports static file serving. The StaticFileRoutingTable dynamically defines routes for serving files.

from jivago.config.production_jivago_context import ProductionJivagoContext
from jivago.config.router.router_builder import RouterBuilder
from jivago.lang.annotations import Override
from jivago.wsgi.routing.routing_rule import RoutingRule
from jivago.wsgi.routing.serving.static_file_routing_table import StaticFileRoutingTable

class MyApplicationContext(ProductionJivagoContext):

    def create_router_config(self) -> RouterBuilder:
        return super().create_router_config() \
            .add_rule(RoutingRule("/", StaticFileRoutingTable("/var/www"))) \
            .add_rule(RoutingRule("/", StaticFileRoutingTable("/var/www", allowed_extensions=['.html', '.xml'])))

The StaticFileRoutingTable can also be used with a allowed_extensions parameter to explicitly allow or disallow specific file types.

Additional router configuration options, including specific filter and CORS rules, can be found at Router Configuration.