Python Falcon – Suffixed Responders
”;
To understand the concept and the need of suffixed responders, let us define a StudentResource class. It consists of an on_get() responder that converts the students a list of dict objects to JSON and returns as its response.
Let us also add on_post() responder that reads the data from the incoming request and adds a new dict object in the list.
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 def on_post(self, req, resp): student = json.load(req.bounded_stream) students.append(student) resp.text = "Student added successfully." resp.status = falcon.HTTP_OK resp.content_type = falcon.MEDIA_TEXT
Using add_route() function of the Falcon”s App object, we add /students route.
app = falcon.App() app.add_route("/students", StudentResource())
After starting the server, we can test the GET and POST requests from HTTPie command line −
http GET localhost:8000/students HTTP/1.1 200 OK Content-Length: 187 Content-Type: application/json Date: Mon, 18 Apr 2022 06:21:02 GMT Server: waitress [ { "id": 1, "name": "Ravi", "percent": 75.5 }, { "id": 2, "name": "Mona", "percent": 80.0 }, { "id": 3, "name": "Mathews", "percent": 65.25 } ] http POST localhost:8000/students id=4 name="Prachi" percent=59.90 HTTP/1.1 200 OK Content-Length: 27 Content-Type: text/plain; charset=utf-8 Date: Mon, 18 Apr 2022 06:20:51 GMT Server: waitress Student added successfully.
Invoking on_get() again confirms the addition of new students resource.
http GET localhost:8000/students HTTP/1.1 200 OK Content-Length: 187 Content-Type: application/json Date: Mon, 18 Apr 2022 06:21:02 GMT Server: waitress [ { "id": 1, "name": "Ravi", "percent": 75.5 }, { "id": 2, "name": "Mona", "percent": 80.0 }, { "id": 3, "name": "Mathews", "percent": 65.25 }, { "id": "4", "name": "Prachi", "percent": "59.90" } ]
At this stage, we would like to have a GET responder method in StudentResource class that reads the id parameter from the URL and retrieves a corresponding dict object of from the list.
In other words, the URL of the format /student/{id} should be associated to the GET method in the resource class. But obviously, a class cannot have two methods of same name. Hence, we define to use suffix parameter for the add_route() method to distinguish between the two definitions of on_get() responders.
A route with id parameter is added to the Application object by specifying suffix =”student”.
app.add_route("/students/{id:int}", StudentResource(), suffix=''student'')
We can now add another definition of on_get() method with this suffix, so that the name of this responder is on_get_student(), as follows −
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
Start the Waitress server after adding the new route and on_get_student() responder and test this URL as follows −
http GET localhost:8000/students/2 HTTP/1.1 200 OK Content-Length: 42 Content-Type: application/json Date: Mon, 18 Apr 2022 06:21:05 GMTy Server: waitress { "id": 2, "name": "Mona", "percent": 80.0 }
Note that the on_put() responder (to update a resource) and on_delete() responder (to delete a resource) will also get invoked when the URL route /students/{id:int} is requested by the client with appropriate request header.
We have already added this route with student as the suffix. Hence, on_put_student() method parses the path parameter in an integer variable. The JSON representation of the item with given id is fetched and updated with the data provided in the PUT request.
def on_put_student(self, req, resp, id): student=students[id-1] data = json.load(req.bounded_stream) student.update(data) resp.text = json.dumps(student) resp.status = falcon.HTTP_OK resp.content_type = falcon.MEDIA_JSON
The on_delete_student() responder simply deletes the item with the id specified in the DELETE request. The list of remaining resources is returned.
def on_delete_student(self, req, resp, id): students.pop(id-1) resp.text = json.dumps(students) resp.status = falcon.HTTP_OK resp.content_type = falcon.MEDIA_JSON
We can test the PUT and DELETE operations of the API with HTTPie commands −
http PUT localhost:8000/students/2 id=3 name="Mathews" percent=55 HTTP/1.1 200 OK Content-Length: 46 Content-Type: application/json Date: Sat, 18 Apr 2022 10:13:00 GMT Server: waitress { "id": "3", "name": "Mathews", "percent": "55" } http DELETE localhost:8000/students/2 HTTP/1.1 200 OK Content-Length: 92 Content-Type: application/json Date: Sat, 18 Apr 2022 10:18:00 GMT Server: waitress [ { "id": 1, "name": "Ravi", "percent": 75.5 }, { "id": 3, "name": "Mathews", "percent": 65.25 } ]
The complete code of this API (studentapi.py) is as under −
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 def on_post(self, req, resp): student = json.load(req.bounded_stream) 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 def on_put_student(self, req, resp, id): student=students[id-1] data = json.load(req.bounded_stream) student.update(data) resp.text = json.dumps(student) resp.status = falcon.HTTP_OK resp.content_type = falcon.MEDIA_JSON def on_delete_student(self, req, resp, id): students.pop(id-1) print (students) resp.text = json.dumps(students) resp.status = falcon.HTTP_OK resp.content_type = falcon.MEDIA_JSON app = falcon.App() app.add_route("/students", StudentResource()) app.add_route("/students/{id:int}", StudentResource(), suffix=''student'') if __name__ == ''__main__'': serve(app, host=''0.0.0.0'', port=8000)
”;