Writing request handlers

The main building blocks for a RESTful API in tornado are subclasses of the tornado.web.RequestHandler class, that is, the base class for HTTP request handlers in Tornado. We just need to create a subclass of this class and declare the methods for each supported HTTP verb. We have to override the methods to handle HTTP requests. Then, we have to map the URL patterns to each subclass of tornado.web.RequestHandler in the tornado.web.Application instance that represents the Tornado Web application.

First, we will create a HexacopterHandler class that we will use to handle requests for the hexacopter resource. Create a new api.py file. The following lines show all the necessary imports for the classes that we will create and the code that declares the HexacopterHandler class in the drone.py file. Enter the next lines in the new api.py file. The code file for the sample is included in the restful_python_chapter_09_01 folder:

import status 
from datetime import date 
from tornado import web, escape, ioloop, httpclient, gen 
from drone import Altimeter, Drone, Hexacopter, LightEmittingDiode 
 
 
drone = Drone() 
 
 
class HexacopterHandler(web.RequestHandler): 
    SUPPORTED_METHODS = ("GET", "PATCH") 
    HEXACOPTER_ID = 1 
 
    def get(self, id): 
        if int(id) is not self.__class__.HEXACOPTER_ID: 
            self.set_status(status.HTTP_404_NOT_FOUND) 
            return 
        print("I've started retrieving hexacopter's status") 
        hexacopter_status = drone.hexacopter.get_hexacopter_status() 
        print("I've finished retrieving hexacopter's status") 
        response = {  
            'speed': hexacopter_status.motor_speed, 
            'turned_on': hexacopter_status.turned_on, 
            } 
        self.set_status(status.HTTP_200_OK) 
        self.write(response) 
     
    def patch(self, id): 
        if int(id) is not self.__class__.HEXACOPTER_ID: 
            self.set_status(status.HTTP_404_NOT_FOUND) 
            return 
        request_data = escape.json_decode(self.request.body)  
        if ('motor_speed' not in request_data.keys()) or  
            (request_data['motor_speed'] is None): 
            self.set_status(status.HTTP_400_BAD_REQUEST) 
            return 
        try: 
            motor_speed = int(request_data['motor_speed']) 
            print("I've started setting the hexacopter's motor speed") 
            hexacopter_status = drone.hexacopter.set_motor_speed(motor_speed) 
            print("I've finished setting the hexacopter's motor speed") 
            response = {  
                'speed': hexacopter_status.motor_speed, 
                'turned_on': hexacopter_status.turned_on, 
                } 
            self.set_status(status.HTTP_200_OK) 
            self.write(response) 
        except ValueError as e: 
            print("I've failed setting the hexacopter's motor speed") 
            self.set_status(status.HTTP_400_BAD_REQUEST) 
            response = { 
                'error': e.args[0] 
                } 
            self.write(response) 

The HexacopterHandler class is a subclass of tornado.web.RequestHandler and declares the following two methods that will be called when the HTTP method with the same name arrives as a request on this HTTP handler:

  • get: This method receives the id of the hexacopter whose status has to be retrieved in the id argument. If the received id doesn't match the value of the HEXACOPTER_ID class attribute, the code calls the self.set_status method with status.HTTP_404_NOT_FOUND as an argument to set the status code for the response to HTTP 404 Not Found. Otherwise, the code prints a message indicating that it started retrieving the hexacopter's status and calls the drone.hexacopter.get_hexacopter_status method with a synchronous execution and saves the result in the hexacopter_status variable. Then, the code writes a message indicating it finished retrieving the status and generates a response dictionary with the 'speed' and 'turned_on' keys and their values. Finally, the code calls the self.set_status method with status.HTTP_200_OK as an argument to set the status code for the response to HTTP 200 OK and calls the self.write method with the response dictionary as an argument. Because response is a dictionary, Tornado automatically writes the chunk as JSON and sets the value of the Content-Type header to application/json.
  • patch: This method receives the id of the hexacopter that has to be updated or patched in the id argument. As it happened in the previously explained get method, the code returns an HTTP 404 Not Found in case the received id doesn't match the value of the HEXACOPTER_ID class attribute. Otherwise, the code calls the tornado.escape.json_decode method with self.request.body as an argument to generate Python objects for the JSON string of the request body and saves the generated dictionary in the request_data variable. If the dictionary doesn't include a key named 'motor_speed', the code returns an HTTP 400 Bad Request status code. In case there is a key, the code prints a message indicating that it started setting the hexacopter's speed, calls the drone.hexacopter.set_motor_speed method with a synchronous execution and saves the result in the hexacopter_status variable. If the value specified for the motor speed is not valid, a ValueError exception will be caught and the code will return an HTTP 400 Bad Request status code and the validation error messages as the response body. Otherwise, the code writes a message indicating it finished setting the motor speed and generates a response dictionary with the 'speed' and 'turned_on' keys and their values. Finally, the code calls the self.set_status method with status.HTTP_200_OK as an argument to set the status code for the response to HTTP 200 OK and calls the self.write method with the response dictionary as an argument. Since response is a dictionary, Tornado automatically writes the chunk as JSON and sets the value of the Content-Type header to application/json.

The class overrides the SUPPORTED_METHODS class variable with a tuple that indicates the class just supports the GET and PATCH methods. This way, in case the handler is requested a method that isn't included in the SUPPORTED_METHODS tuple, the server will automatically return a 405 Method Not Allowed status code.

Now, we will create a LedHandler class that we will use to represent the LED resources. Open the previously created api.py file and add the following lines. The code file for the sample is included in the restful_python_chapter_09_01 folder:

class LedHandler(web.RequestHandler): 
    SUPPORTED_METHODS = ("GET", "PATCH") 
 
    def get(self, id): 
        int_id = int(id) 
        if int_id not in drone.leds.keys(): 
            self.set_status(status.HTTP_404_NOT_FOUND) 
            return 
        led = drone.leds[int_id] 
        print("I've started retrieving {0}'s status".format(led.description)) 
        brightness_level = led.get_brightness_level() 
        print("I've finished retrieving {0}'s status".format(led.description)) 
        response = { 
            'id': led.identifier, 
            'description': led.description, 
            'brightness_level': brightness_level 
            } 
        self.set_status(status.HTTP_200_OK) 
        self.write(response) 
 
    def patch(self, id): 
        int_id = int(id) 
        if int_id not in drone.leds.keys(): 
            self.set_status(status.HTTP_404_NOT_FOUND) 
            return 
        led = drone.leds[int_id] 
        request_data = escape.json_decode(self.request.body)  
        if ('brightness_level' not in request_data.keys()) or  
            (request_data['brightness_level'] is None): 
            self.set_status(status.HTTP_400_BAD_REQUEST) 
            return 
        try: 
            brightness_level = int(request_data['brightness_level']) 
            print("I've started setting the {0}'s brightness
            level".format(led.description)) 
            led.set_brightness_level(brightness_level) 
            print("I've finished setting the {0}'s brightness 
            level".format(led.description)) 
            response = { 
                'id': led.identifier, 
                'description': led.description, 
                'brightness_level': brightness_level 
                } 
            self.set_status(status.HTTP_200_OK) 
            self.write(response) 
        except ValueError as e: 
            print("I've failed setting the {0}'s brightness
            level".format(led.description)) 
            self.set_status(status.HTTP_400_BAD_REQUEST) 
            response = { 
                'error': e.args[0] 
                } 
            self.write(response) 

The LedHandler class is a subclass of tornado.web.RequestHandler. The class overrides the SUPPORTED_METHODS class variable with a tuple that indicates the class just supports the GET and PATCH methods. In addition, the class declares the following two methods that will be called when the HTTP method with the same name arrives as a request on this HTTP handler:

  • get: This method receives the id of the LED whose status has to be retrieved in the id argument. If the received id isn't one of the keys of the drone.leds dictionary, the code calls the self.set_status method with status.HTTP_404_NOT_FOUND as an argument to set the status code for the response to HTTP 404 Not Found. Otherwise, the code retrieves the value associated with the key whose value matches the id in the drone.leds dictionary and saves the retrieved LightEmittingDiode instance in the led variable. The code prints a message indicating that it started retrieving the LED's brightness level, calls the led.get_brightness_level method with a synchronous execution, and saves the result in the brightness_level variable. Then, the code writes a message indicating that it finished retrieving the brightness level and generates a response dictionary with the 'id', 'description', and 'brightness_level' keys and their values. Finally, the code calls the self.set_status method with status.HTTP_200_OK as an argument to set the status code for the response to HTTP 200 OK and calls the self.write method with the response dictionary as an argument. Since response is a dictionary, Tornado automatically writes the chunk as JSON and sets the value of the Content-Type header to application/json.
  • patch: This method receives the id of the LED that has to be updated or patched in the id argument. As happened in the previously explained get method, the code returns an HTTP 404 Not Found in case the received id doesn't match the any of the keys of the drone.leds dictionary. Otherwise, the code calls the tornado.escape.json_decode method with self.request.body as an argument to generate Python objects for the JSON string of the request body and saves the generated dictionary in the request_data variable. If the dictionary doesn't include a key named 'brightness_level', the code returns an HTTP 400 Bad Request status code. In case there is a key, the code prints a message indicating that it started setting the LED's brightness level, including the description for the LED, calls the drone.hexacopter.set_brightness_level method with a synchronous execution. If the value specified for the brightness_level is not valid, a ValueError exception will be caught and the code will return an HTTP 400 Bad Request status code and the validation error messages as the response body. Otherwise, the code writes a message indicating it finished setting the LED's brightness value and generates a response dictionary with the 'id', 'description', and 'brightness_level' keys and their values. Finally, the code calls the self.set_status method with status.HTTP_200_OK as an argument to set the status code for the response to HTTP 200 OK and calls the self.write method with the response dictionary as an argument. Since response is a dictionary, Tornado automatically writes the chunk as JSON and sets the value of the Content-Type header to application/json.

Now, we will create an AltimeterHandler class that we will use to represent the altimeter resource. Open the previously created api.py file and add the following lines. The code file for the sample is included in the restful_python_chapter_09_01 folder:

class AltimeterHandler(web.RequestHandler): 
    SUPPORTED_METHODS = ("GET") 
    ALTIMETER_ID = 1 
 
    def get(self, id): 
        if int(id) is not self.__class__.ALTIMETER_ID: 
            self.set_status(status.HTTP_404_NOT_FOUND) 
            return 
        print("I've started retrieving the altitude") 
        altitude = drone.altimeter.get_altitude() 
        print("I've finished retrieving the altitude") 
        response = {  
            'altitude': altitude 
            } 
        self.set_status(status.HTTP_200_OK) 
        self.write(response) 

The AltimeterHandler class is a subclass of tornado.web.RequestHandler. The class overrides the SUPPORTED_METHODS class variable with a tuple that indicates the class just supports the GET method. In addition, the class declares the get method that will be called when the HTTP method with the same name arrives as a request on this HTTP handler.

The get method receives the id of the altimeter whose altitude has to be retrieved in the id argument. If the received id doesn't match the value of the ALTIMETER_ID class attribute, the code calls the self.set_status method with status.HTTP_404_NOT_FOUND as an argument to set the status code for the response to HTTP 404 Not Found. Otherwise, the code prints a message indicating that it started retrieving the altimeter's altitude, calls the drone.hexacopter.get_altitude method with a synchronous execution, and saves the result in the altitude variable. Then, the code writes a message indicating it finished retrieving the altitude and generates a response dictionary with the 'altitude' key and its value. Finally, the code calls the self.set_status method with status.HTTP_200_OK as an argument to set the status code for the response to HTTP 200 OK and calls the self.write method with the response dictionary as an argument. Since response is a dictionary, Tornado automatically writes the chunk as JSON and sets the value of the Content-Type header to application/json.

The following table shows the method of our previously created HTTP handler classes that we want to be executed for each combination of HTTP verb and scope:

HTTP verb

Scope

Class and method

GET

Hexacopter

HexacopterHandler.get

PATCH

Hexacopter

HexacopterHandler.patch

GET

LED

LedHandler.get

PATCH

LED

LedHandler.patch

GET

Altimeter

AltimeterHandler.get

If the request results in the invocation of an HTTP handler class with an unsupported HTTP method, Tornado will return a response with the HTTP 405 Method Not Allowed status code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.130.199