1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
Routing in hug
==============
The most basic function of any framework meant to enable external interaction with an API, is routing how the external
interaction will correspond to internal function calls and business logic. hug provides flexible and powerful routers
that aim to scale intuitively from simple use-cases to complex.
To enable this all hug routers share 3 attributes:
- Can be used directly as function decorators
- Can be used separately from the function
- Can be stored, modified, and chained before being used
And, while hug uses functions in most of its examples, it supports applying routes to methods and objects as well. All hug routers enforce type annotation
and enable automatic argument supplying via directives.
Using a router as a decorator
=============================
The most basic use case is to simply define the route directly above the function you need to expose as a decorator:
import hug
@hug.get('/home')
def root():
return 'Welcome home!'
This is clear, explicit, and obvious. As such, this is recommended for most basic APIs.
Declaring a router separate from a function
===========================================
Sometimes, in more complex use-cases, it's necessary to define routing separate from where the code itself is defined.
hug aims to make this as easy and intuitive as it can be:
Internal API:
# internal.py
def root():
return 'Welcome home!'
External API:
# external.py
import hug
import internal
router = hug.route.API(__name__)
router.get('/home')(internal.root)
Or, alternatively:
# external.py
import hug
import internal
api = hug.API(__name__)
hug.get('/home', api=api)(internal.root)
Chaining routers for easy re-use
================================
A very common scenario when using hug routers, because they are so powerful, is duplication between routers.
For instance: if you decide you want every route to return the 404 page when a validation error occurs or you want to
require validation for a collection of routes. hug makes this extremely simple by allowing all routes to be chained
and reused:
import hug
api = hug.get(on_invalid=hug.redirect.not_found)
@api.urls('/do-math', examples='number_1=1&number_2=2')
def math(number_1: hug.types.number, number_2: hug.types.number):
return number_1 + number_2
@api
def happy_birthday(name, age: hug.types.number):
"""Says happy birthday to a user"""
return "Happy {age} Birthday {name}!".format(**locals())
It's important to note that to chain you simply call the argument you would normally pass in to the routers init function
as a method on the existing router. Then you pass in any additional parameters you would like to override as **kwargs - as
shown in the math example above.
Common router parameters
========================
There are a few parameters that are shared between all router types, as they are globally applicable to all currently supported interfaces:
- `api`: The API to register the route with. You can always retrieve the API singleton for the current module by doing `hug.API(__name__)`
- `transform`: A function to call on the the data returned by the function to transform it in some way specific to this interface
- `output`: An output format to apply to the outputted data (after return and optional transformation)
- `requires`: A list or single function that must all return `True` for the function to execute when called via this interface (commonly used for authentication)
HTTP Routers
============
in addition to `hug.http` hug includes convenience decorators for all common HTTP METHODS (`hug.connect`, `hug.delete`, `hug.get`, `hug.head`, `hug.options`, `hug.patch`, `hug.post`, `hug.put`, `hug.get_post`, `hug.put_post`, and `hug.trace`). These methods are functionally the same as calling `@hug.http(accept=(METHOD, ))` and are otherwise identical to the http router.
- `urls`: A list of or a single URL that should be routed to the function. Supports defining variables within the URL that will automatically be passed to the function when `{}` notation is found in the URL: `/website/{page}`. Defaults to the name of the function being routed to.
- `accept`: A list of or a single HTTP METHOD value to accept. Defaults to all common HTTP methods.
- `examples`: A list of or a single example set of parameters in URL query param format. For example: `examples="argument_1=x&argument_2=y"`
- `versions`: A list of or a single integer version of the API this endpoint supports. To support a range of versions the Python builtin range function can be used.
- `suffixes`: A list of or a single suffix to add to the end of all URLs using this router.
- `prefixes`: A list of or a single prefix to add before all URLs using this router.
- `response_headers`: An optional dictionary of response headers to set automatically on every request to this endpoint.
- `status`: An optional status code to automatically apply to the response on every request to this endpoint.
- `parse_body`: If `True` and the format of the request body matches one known by hug, hug will run the specified input formatter on the request body before passing it as an argument to the routed function. Defaults to `True`.
- `on_invalid`: A transformation function to run outputed data through, only if the request fails validation. Defaults to the endpoints specified general transform function, can be set to not run at all by setting to `None`.
- `output_invalid`: Specifies an output format to attach to the endpoint only on the case that validation fails. Defaults to the endpoints specified output format.
- `raise_on_invalid`: If set to true, instead of collecting validation errors in a dictionary, hug will simply raise them as they occur.
Handling for 404 Responses
===========
By default, Hug will call `documentation_404()` if a user tries to access a nonexistant route when serving. If you want to specify something different, you can use the "sink" decorator, such as in the example below. The `@hug.sink()` decorator serves as a "catch all" for unassigned routes.
```Python
import hug
@hug.sink('/all')
def my_sink(request):
return request.path.replace('/all', '')
```
In this case, the server routes requests to anything that's no an assigned route to the landing page. To test the functionality of your sink decorator, serve your application locally, then attempt to access an unassigned route. Using this code, if you try to access `localhost:8000/this-route-is-invalid`, you will be rerouted to `localhost:8000`.
CLI Routing
===========
Any endpoint can also be exposed to the command line as well, using `@hug.cli`:
- `name`: The name that should execute the command from the command line. Defaults to the name of the function being routed.
- `version`: The optional version associated with this command line application.
- `doc`: Documentation to provide to users of this command line tool. Defaults to the functions doc string.
Local Routing
=============
By default all hug APIs are already valid local APIs. However, sometimes it can be useful to apply type annotations and/or directives to local use as well. For these cases hug provides `@hug.local`:
- `validate`: Apply type annotations to local use of the function. Defaults to `True`.
- `directives`: Apply directives to local use of the function. Defaults to `True`.
- `version`: Specify a version of the API for local use. If versions are being used, this generally should be the latest supported.
- `on_invalid`: A transformation function to run outputed data through, only if the request fails validation. Defaults to the endpoints specified general transform function, can be set to not run at all by setting to `None`.
- `output_invalid`: Specifies an output format to attach to the endpoint only on the case that validation fails. Defaults to the endpoints specified output format.
- `raise_on_invalid`: If set to `True`, instead of collecting validation errors in a dictionary, hug will simply raise them as they occur.
NOTE: unlike all other routers, this modifies the function in-place
|