-
Notifications
You must be signed in to change notification settings - Fork 3
Routing
Routing is chocs
' component in charge of translating each incoming HTTP request to a controller.
Every HTTP request has a URL. A URL identifies a resource. The following "diagram" describes different parts of a URL:
GET http://www.example.org:8080/pets/categories?order_by=creation_date&order=asc
\_/ \__/ \_____________/ \__/\ \_______// \______________________________/
| | | | \_____segment/ |
method | host port | query string
scheme path
When chocs receives requests when the scheme is http (or https) and the host refers to a machine where the application is running. Therefore application cares about method
, path
and the query_string
. Those attributes are available in the request object passed to your controller. Before a controller is called, method
and path
parts are used by router. Router, which contains list of registered routes and corresponding controllers starts translating a request to a controller. The process is called `routing.
Component responsible for routing is called
router
, and chocs implementation can be found inchocs.HttpRouter
class.
import chocs
http = chocs.Application()
@http.get("/hello")
def hello(req: chocs.HttpRequest) -> chocs.HttpResponse:
...
The above example will register hello
function to handle a GET /hello
request.
First we initialise new application and store it in http
variable. Application class defines decorator methods, all decorator method's name corresponds to a different http verb. The following list contains supported methods by chocs library:
delete
get
head
options
patch
post
put
trace
Those methods accept an argument which is called a route. Route is a pattern which application will use to identify corresponding controller. The decorated function is your controller. A Controller should always accept chocs.HttpRequest
parameter and return chocs.HttpResponse
object.
Routes can contain parameterised parts. Parameters must be enclosed within {
and }
. A path parameter captures the value from a path segment and can be retrieved from the request object.
import chocs
http = Application()
@http.get("/pet/{id}")
def get_pet(request: chocs.HttpRequest) -> chocs.HttpResponse:
pet_id = request.path_parameters.get("id")
...
The above application will handle the following requests:
-
GET /pet/1
(pet_id
will be1
in the current scenario) -
GET /pet/abc
(pet_id
will beabc
in the current scenario) -
GET /pet/abc1
(pet_id
will beabc1
in the current scenario)
A route pattern may contain at the end a special 'match-all' token, the asterisk (*). This token allows for any remaining request's path to be matched, regardless of its contents or length.
Keep in mind that routes which do not contain wildcards are prioritised over routes with wildcards.
import chocs
http = chocs.Application()
@http.get("/pet/*")
def get_pet(request: chocs.HttpRequest) -> chocs.HttpResponse:
...
The above application will handle the following requests:
/pet/a
/pet/a/b/c
/pet/1
/pet/1/2/3
/pet/can/go/like/that/forever
Chocs supports route groups. Route groups is implemented through context lib interface. If you need to split your application in smaller chunks with standalone req/res handlers this feature may come in very handy:
from threading import Thread
from chocs.wsgi import serve
from chocs import Application
from chocs import HttpRequest
from chocs import HttpResponse
main_app = Application()
with main_app.group("/users/{id}") as user_module:
@user_module.post("/profile_picture") # POST /users/{id}/profile_pictures
def create_profile_picture(request: HttpRequest) -> HttpResponse:
...
@user_module.get("/profile_picture") # GET /users/{id}/profile_pictures
def get_profile_picture(request: HttpRequest) -> HttpResponse:
...
@user_module.get("/badges") # GET /users/{id}/badges
def badges(request: HttpRequest) -> HttpResponse:
...
with main_app.group("/payments") as payment_module:
@payment_module.get("/analytics") # GET /payments/analytics
def get_analytics(request: HttpRequest) -> HttpResponse:
...
if __name__ == '__main__':
def wsgi_user_module():
serve(user_module, port=8081)
def wsgi_payment_module():
serve(payment_module, port=8082)
Thread(target=wsgi_user_module).start()
payment_module()
The above example shows how to run two different modules, which support their own routes on two different ports in the one process.
- Creating new application
- Registering a controller
- Grouping controllers
- Registering middleware
- Dynamically loading modules
TBA
TBA
- Reading request's body
- Accessing request's parsed body
- Accessing request's headers
- Accessing path's parameters
- Reading client cookies
- Comparing requests objects
- API