Python Falcon - Routing



Falcon adopts RESTful architectural style. Hence it uses resource based routing. A resource class is responsible for handling the HTTP methods by the responders, which are essentially class methods with a name that starts with on_ and ends in the lowercased HTTP method name (e.g., on_get(), on_patch(), on_delete(), etc.). The add_route() method of the Falcon Application object associates its router with an instance of resource class.

In the Hellofalcon.py example used above, the on_get() and on_post() responders are invoked when the /hello route is requested by the client by GET and POST method respectively.

If no route matches the request, an instance of HTTPRouteNotFound is raised. On the other hand, if a route is matched but the resource does not implement a responder for the requested HTTP method, a default responder raises an instance of HTTPMethodNotAllowed.

Field Converters

Falcon's routing mechanism allows URLs to pass parameters to the responders. The URL comprises of three parts: The protocol (such as http:// or https://) followed by the IP address or hostname. The remaining part of the URL after first / after the hostname is called as the path or endpoint. Parameters to be passed are after the endpoint.

Routing

This acts as a resource identifier such as a unique ID or primary key. The parameter names are enclosed in curly brackets. Value of a path parameter goes to the argument defined in the responder method in addition to request and response.

In the following example, the router associates the resource class object with a URL consisting of a parameter after the endpoint.

from waitress import serve
import falcon
import json
class HelloResource:
   def on_get(self, req, resp, nm):
      """Handles GET requests"""
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT
      resp.text = (
         'Hello '+nm
      )
app = falcon.App()
hello = HelloResource()
app.add_route('/hello/{nm}', hello)
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

We can see that the on_get() responder method has an additional parameter nm to accept the data parsed from the URL route. Let us test http://localhost:8000/hello/Priya with HTTPie tool.

>http GET localhost:8000/hello/Priya
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=utf-8
Date: Mon, 18 Apr 2022 12:27:35 GMT
Server: waitress
Hello Priya

The default data type to which the path parameters are parsed to is str (i.e. string). However, Falcon's router engine has the following built-in field converters using which they can be read into other data types as well.

  • IntConverter − This class is defined in falcon.routing module. The constructor uses the following arguments −

IntConverter(num_digits=None, min=None, max=None)

    Where,

    • num_digits − The value must have given number of digits.

    • min − minimum required value of the parameter

    • max − maximum allowed value of the parameter.

    For example, the following add_route() function accepts an integer between 1 to 100 as rollno.

app.add_route('/student/{rollno:int(1,1,100}', StudentResource())
  • UUIDConverter − This class in the falcon.routing module gives converts a string of 32 hexadecimal digits into a UUID (Universal Unique Identifier).

  • DateTimeConverter − Converts the parameter string to a datetime variable. The parameter must be a string in any format recognized by strptime() function, the default being '%Y-%m-%dT%H:%M:%SZ'.

Format string uses the following format codes −

%a Abbreviated weekday name Sun, Mon
%A Full weekday name Sunday, Monday
%d Day of the month as a zero-padded decimal 01, 02
%-d day of the month as decimal number 1, 2..
%b Abbreviated month name Jan, Feb
%m month as a zero padded decimal number 01, 02
%B Full month name January, February
%-y year without century as a decimal number 0, 99
%Y year with century as a decimal number 2000, 1999
%H hour(24 hour clock) as a zero padded decimal number 01, 23
%p locale's AM or PM AM, PM
%-M Minute as a decimal number 1, 59
%-S Second as a decimal number 1, 59

In the following example, the add_route() function associates a URL with two parameters with the Resource object. First parameter nm is a string by default. The second parameter age uses IntConverter.

from waitress import serve
import falcon
import json
class HelloResource:
   def on_get(self, req, resp, nm,age):
      """Handles GET requests"""
      retvalue={"name":nm, "age":age}
      resp.body=json.dumps(retvalue)
      resp.status = falcon.HTTP_200 
      resp.content_type = falcon.MEDIA_JSON
app = falcon.App()
hello = HelloResource()
app.add_route('/hello/{nm}/{age:int}', hello)
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

Note that the on_get() responder uses the path parameters to form a dict object – retvalue. Its JSON representation is then assigned as the value of response body and returned to the client. As mentioned earlier, JSON is the default content type of Falcon's response object.

Start the Waitress server and check the response for the URL http://localhost:8000/hello/Priya/21 with the help of HTTPie.

http GET localhost:8000/hello/Priya/21
HTTP/1.1 200 OK
Content-Length: 28
Content-Type: application/json
Date: Fri, 22 Apr 2022 14:22:47 GMT
Server: waitress {
   "age": 21,
   "name": "Priya"
}

You can also check the response in a browser as follows −

Routing Hello
Advertisements