Python Falcon - Hooks



Hooks are the user defined functions that are executed automatically when a specific responder method in the resource class is invoked in response to the client request. Falcon supports before and after hooks.

A function to be used as a hook is defined with the request, response and resource class as parameters, in additional to any optional parameters as may be necessary.

def hookfunction(req, resp, resource):
   . . . . .
   . . . . .

Such a function is attached to either an individual responder or the entire resource class by applying one of the following decorators −

  • @falcon.before(hookfunction)

  • @falcon.after(hookfunction)

To apply the before hook to the on_post() responder −

@falcon.before(hookfunction)
def on_post(self, req, resp):
   . . .
   . . .

To apply an after hook −

@falcon.after(hookfunction)
def on_get(self, req, resp):
   . . .
   . . .

To decorate the entire resource class, use the decorator above the declaration of the class −

@falcon.after(hookfunction)
class SomeResource:
 def on_get(self, req, resp):
   . . .
   . . .
   def on_post(self, req, resp):
   . . .
   . . .

In the following example, we have the StudentResource class in which on_get() and on_post() responders have been defined. The on_post() responder is invoked when a POST request sends some data and a new dict object created with it is added in the Students list.

The data received needs to be validated before processing. For this purpose, the following function has been defined. It checks whether value of percent parameter is between 0 and 100. Only if the data passes this condition, it is passed to the responder.

def checkinput(req, resp, resource,params):
   student = json.load(req.bounded_stream)
   if "name" not in student:
      raise falcon.HTTPBadRequest(
         title="Bad request", description="Bad input, name must be provided."
      )

   per=int(student['percent'])
   if per<0 or per>100:
      raise falcon.HTTPBadRequest(
         title="Bad request", description="Bad input, invalid percentage"
      )
      req.context.data = student

This function is applied as a hook on the on_post() responder of the StudentResource class.

import falcon
import json
from waitress import serve
students = [
   {"id": 1, "name": "Ravi", "percent": 75.50},
   {"id": 2, "name": "Mona", "percent": 80.00},
   {"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
   def on_get(self, req, resp):
      resp.text = json.dumps(students)
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON
   @falcon.before(checkinput)
   def on_post(self, req, resp):
      student = json.load(req.context.data)
      students.append(student)
      resp.text = "Student added successfully."
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_TEXT

   def on_get_student(self, req, resp, id):
      resp.text = json.dumps(students[id-1])
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON
app = falcon.App()
app.add_route("/students", StudentResource())
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

Let us run the Waitress server and initiate the POST request.

http POST localhost:8000/students id=4 percent=50
HTTP/1.1 400 Bad Request
Content-Length: 76
Content-Type: application/json
Date: Tue, 26 Apr 2022 14:49:07 GMT
Server: waitress
Vary: Accept {
   "description": "Bad input, name must be provided.",
   "title": "Bad request"
}

Since the data doesn't contain value of name parameter, the exception is raised.

In another POST request as shown below, the value of percent parameter fails to meet the required criteria, hence the exception.

http POST localhost:8000/students id=4 name="aaa" percent=500
HTTP/1.1 400 Bad Request
Content-Length: 72
Content-Type: application/json
Date: Tue, 26 Apr 2022 15:01:20 GMT
Server: waitress
Vary: Accept {
   "description": "Bad input, invalid percentage",
   "title": "Bad request"
}
Advertisements