Making HTTP requests to the Flask API

Now, we can run the api/api.py script that launches Flask's development server to compose and send HTTP requests to our unsecure and simple Web API (we will definitely add security later). Execute the following command.

python api/api.py

The following lines show the output after we execute the previous command. The development server is listening at port 5000.

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger pin code: 294-714-594

With the previous command, we will start Flask development server and we will only be able to access it in our development computer. The previous command starts the development server in the default IP address, that is, 127.0.0.1 (localhost). It is not possible to access this IP address from other computers or devices connected on our LAN. Thus, if we want to make HTTP requests to our API from other computers or devices connected to our LAN, we should use the development computer IP address, 0.0.0.0 (for IPv4 configurations) or :: (for IPv6 configurations), as the desired IP address for our development server.

If we specify 0.0.0.0 as the desired IP address for IPv4 configurations, the development server will listen on every interface on port 5000. In addition, it is necessary to open the default port 5000 in our firewalls (software and/or hardware) and configure port-forwarding to the computer that is running the development server.

We just need to specify '0.0.0.0' as the value for the host argument in the call to the app.run method, specifically, the last line in the api/api.py file. The following line shows the new call to app.run that launches Flask's development server in an IPv4 configuration and allows requests to be made from other computers and devices connected to our LAN. The line generates an externally visible server. The code file for the sample is included in the restful_python_chapter_05_02 folder:

if __name__ == '__main__': 
    app.run(host='0.0.0.0', debug=True)

Tip

If you decide to compose and send HTTP requests from other computers or devices connected to the LAN, remember that you have to use the development computer's assigned IP address instead of localhost. For example, if the computer's assigned IPv4 IP address is 192.168.1.103, instead of localhost:5000, you should use 192.168.1.103:5000. Of course, you can also use the host name instead of the IP address. The previously explained configurations are very important because mobile devices might be the consumers of our RESTful APIs and we will always want to test the apps that make use of our APIs in our development environments. In addition, we can work with useful tools such as ngrok that allow us to generate secure tunnels to localhost. You can read more information about ngrok at http://www.ngrok.com.

The Flask development server is running on localhost (127.0.0.1), listening on port 5000, and waiting for our HTTP requests. Now, we will compose and send HTTP requests locally in our development computer or from other computer or devices connected to our LAN.

Working with command-line tools – curl and httpie

We will start composing and sending HTTP requests with the command-line tools we have introduced in Chapter 1 , Developing RESTful APIs with Django, curl and HTTPie. In case you haven't installed HTTPie, make sure you activate the virtual environment and then run the following command in the terminal or command prompt to install the HTTPie package.

pip install --upgrade httpie

Tip

In case you don't remember how to activate the virtual environment that we created for this example, read the following section in this chapter-Setting up the virtual environment with Django REST framework.

Open a Cygwin Terminal in Windows or a Terminal in macOS or Linux, and run the following command. It is very important that you enter the ending slash (/) when specified /api/messages won't match any of the configured URL routes. Thus, we must enter /api/messages/, including the ending slash (/). We will compose and send an HTTP request to create a new message:

http POST :5000/api/messages/ message='Welcome to IoT' duration=10 message_category='Information'

The following is the equivalent curl command. It is very important to use the -H "Content-Type: application/json" option to indicate curl to send the data specified after the -d option as application/json instead of the default application/x-www-form-urlencoded:

curl -iX POST -H "Content-Type: application/json" -d '{"message":"Welcome to IoT", "duration":10, "message_category": "Information"}' :5000/api/messages/

The previous commands will compose and send the following HTTP request: POST http://localhost:5000/api/messages/ with the following JSON key-value pairs:

{  
    "message": "Welcome to IoT",  
    "duration": 10,  
    "message_category": "Information" 
} 

The request specifies /api/messages/, and therefore, it will match '/api/messages/' and run the MessageList.post method. The method doesn't receive arguments because the URL route doesn't include any parameters. As the HTTP verb for the request is POST, Flask calls the post method. If the new MessageModel was successfully persisted in the dictionary, the function returns an HTTP 201 Created status code and the recently persisted MessageModel serialized serialized to JSON in the response body. The following lines show an example response for the HTTP request, with the new MessageModel object in the JSON response:

HTTP/1.0 201 CREATED
Content-Length: 245
Content-Type: application/json
Date: Wed, 20 Jul 2016 04:43:24 GMT
Server: Werkzeug/0.11.10 Python/3.5.1
{
    "creation_date": "Wed, 20 Jul 2016 04:43:24 -0000", 
    "duration": 10, 
    "id": 1, 
    "message": "Welcome to IoT", 
    "message_category": "Information", 
    "printed_once": false, 
    "printed_times": 0, 
    "uri": "/api/messages/1"
}

We will compose and send an HTTP request to create another message. Go back to the Cygwin terminal in Windows or the Terminal in macOS or Linux, and run the following command:

http POST :5000/api/messages/ message='Measuring ambient temperature' duration=5 message_category='Information'

The following is the equivalent curl command:

curl -iX POST -H "Content-Type: application/json" -d '{"message":"Measuring ambient temperature", "duration":5, "message_category": "Information"}' :5000/api/messages/

The previous commands will compose and send the following HTTP request, POST http://localhost:5000/api/messages/, with the following JSON key-value pairs:

{  
    "message": "Measuring ambient temperature",  
    "duration": 5,  
    "message_category": "Information" 
} 

The following lines show an example response for the HTTP request, with the new MessageModel object in the JSON response:

HTTP/1.0 201 CREATED
Content-Length: 259
Content-Type: application/json
Date: Wed, 20 Jul 2016 18:27:05 GMT
Server: Werkzeug/0.11.10 Python/3.5.1
{
    "creation_date": "Wed, 20 Jul 2016 18:27:05 -0000", 
    "duration": 5, 
    "id": 2, 
    "message": "Measuring ambient temperature", 
    "message_category": "Information", 
    "printed_once": false, 
    "printed_times": 0, 
    "uri": "/api/messages/2"
}

We will compose and send an HTTP request to retrieve all the messages. Go back to the Cygwin terminal in Windows or the Terminal in macOS or Linux, and run the following command:

http :5000/api/messages/

The following is the equivalent curl command:

curl -iX GET -H :5000/api/messages/

The previous commands will compose and send the following HTTP request: GET http://localhost:5000/api/messages/. The request specifies /api/messages/, and therefore, it will match '/api/messages/' and run the MessageList.get method. The method doesn't receive arguments because the URL route doesn't include any parameters. As the HTTP verb for the request is GET, Flask calls the get method. The method retrieves all the MessageModel objects and generates a JSON response with all of these MessageModel objects serialized.

The following lines show an example response for the HTTP request. The first lines show the HTTP response headers, including the status (200 OK) and the Content-type (application/json). After the HTTP response headers, we can see the details for the two MessageModel objects in the JSON response:

HTTP/1.0 200 OK
Content-Length: 589
Content-Type: application/json
Date: Wed, 20 Jul 2016 05:32:28 GMT
Server: Werkzeug/0.11.10 Python/3.5.1
[
    {
        "creation_date": "Wed, 20 Jul 2016 05:32:06 -0000", 
        "duration": 10, 
        "id": 1, 
        "message": "Welcome to IoT", 
        "message_category": "Information", 
        "printed_once": false, 
        "printed_times": 0, 
        "uri": "/api/messages/1"
    }, 
    {
        "creation_date": "Wed, 20 Jul 2016 05:32:18 -0000", 
        "duration": 5, 
        "id": 2, 
        "message": "Measuring ambient temperature", 
        "message_category": "Information", 
        "printed_once": false, 
        "printed_times": 0, 
        "uri": "/api/messages/2"
    }
]

After we run the three requests, we will see the following lines in the window that is running the Flask development server. The output indicates that the server received three HTTP requests, specifically two POST requests and one GET request with /api/messages/ as the URI. The server processed the three HTTP requests, returned status code 201 for the first two requests and 200 for the last request:

127.0.0.1 - - [20/Jul/2016 02:32:06] "POST /api/messages/ HTTP/1.1" 201 -
127.0.0.1 - - [20/Jul/2016 02:32:18] "POST /api/messages/ HTTP/1.1" 201 -
127.0.0.1 - - [20/Jul/2016 02:32:28] "GET /api/messages/ HTTP/1.1" 200 -

The following image shows two Terminal windows side-by-side on macOS. The Terminal window at the left-hand side is running the Flask development server and displays the received and processed HTTP requests. The Terminal window at the right-hand side is running http commands to generate the HTTP requests. It is a good idea to use a similar configuration to check the output while we compose and send the HTTP requests:

Working with command-line tools – curl and httpie

Now, we will compose and send an HTTP request to retrieve a message that doesn't exist. For example, in the previous list, there is no message with an id value equal to 800. Run the following command to try to retrieve this message. Make sure you use an id value that doesn't exist. We must make sure that the utilities display the headers as part of the response to see the returned status code:

http :5000/api/messages/800

The following is the equivalent curl command:

curl -iX GET :5000/api/messages/800

The previous commands will compose and send the following HTTP request: GET http://localhost:5000/api/messages/800. The request is the same than the previous one we have analyzed, with a different number for the id parameter. The server will run the Message.get method with 800 as the value for the id argument. The method will execute the code that retrieves the MessageModel object whose id matches the id value received as an argument. However, the first line in the MessageList.get method calls the abort_if_message_doesnt_exist method that won't find the id in the dictionary keys and it will call the flask_restful.abort function because there is no message with the specified id value. Thus, the code will return an HTTP 404 Not Found status code. The following lines show an example header response for the HTTP request and the message included in the body. In this case, we just leave the default message. Of course, we can customize it based on our specific needs:

HTTP/1.0 404 NOT FOUND
Content-Length: 138
Content-Type: application/json
Date: Wed, 20 Jul 2016 18:08:04 GMT
Server: Werkzeug/0.11.10 Python/3.5.1
{
    "message": "Message 800 doesn't exist. You have requested this URI [/api/messages/800] but did you mean /api/messages/<int:id> ?"
}

Our API is able to update a single field for an existing resource, and therefore, we provide an implementation for the PATCH method. For example, we can use the PATCH method to update two fields for an existing message and set the value for its printed_once field to true and printed_times to 1. We don't want to use the PUT method because this method is meant to replace an entire message. The PATCH method is meant to apply a delta to an existing message, and therefore, it is the appropriate method to just change the value of the printed_once and printed_times fields.

Now, we will compose and send an HTTP request to update an existing message, specifically, to update the value of two fields. Make sure you replace 2 with the id of an existing message in your configuration:

http PATCH :5000/api/messages/2 printed_once=true printed_times=1

The following is the equivalent curl command:

curl -iX PATCH -H "Content-Type: application/json" -d '{"printed_once":"true", "printed_times":1}' :5000/api/messages/2

The previous command will compose and send a PATCH HTTP request with the specified JSON key-value pairs. The request has a number after /api/messages/, and therefore, it will match '/api/messages/<int:id>' and run the Message.patch method, that is, the patch method for the Message class. If a MessageModel instance with the specified id exists and it was successfully updated, the call to the method will return an HTTP 200 OK status code and the recently updated MessageModel instance serialized to JSON in the response body. The following lines show a sample response:

HTTP/1.0 200 OK
Content-Length: 231
Content-Type: application/json
Date: Wed, 20 Jul 2016 18:28:01 GMT
Server: Werkzeug/0.11.10 Python/3.5.1
{
"creation_date": "Wed, 20 Jul 2016 18:27:05 -0000",
"duration": 0,
"id": 2,
"message": "Measuring ambient temperature",
"message_category": "Information",
"printed_once": true,
"printed_times": 1,
"uri": "/api/messages/2"
    }
  

Tip

The IoT device will make the previously explained HTTP request when it displays the message for the first time. Then, it will make additional PATCH requests to update the value for the printed_times field.

Now, we will compose and send an HTTP request to delete an existing message, specifically, the last message we added. As happened in our last HTTP requests, we have to check the value assigned to id in the previous response and replace 2 in the command with the returned value:

http DELETE :5000/api/messages/2

The following is the equivalent curl command:

curl -iX DELETE :5000/api/messages/2

The previous commands will compose and send the following HTTP request: DELETE http://localhost:5000/api/messages/2. The request has a number after /api/messages/, and therefore, it will match '/api/messages/<int:id>' and run the Message.delete method, that is, the delete method for the Message class. If a MessageModel instance with the specified id exists and it was successfully deleted, the call to the method will return an HTTP 204 No Content status code. The following lines show a sample response:

HTTP/1.0 204 NO CONTENT
Content-Length: 0
Content-Type: application/json
Date: Wed, 20 Jul 2016 18:50:12 GMT
Server: Werkzeug/0.11.10 Python/3.5.1

Working with GUI tools - Postman and others

So far, we have been working with two terminal-based or command-line tools to compose and send HTTP requests to our Flask development server-cURL and HTTPie. Now, we will work with one of the GUI tools we used when composing and sending HTTP requests to the Django development server-Postman.

Now, we will use the Builder tab in Postman to easily compose and send HTTP requests to localhost:5000 and test the RESTful API with this GUI tool. Remember that Postman doesn't support curl-like shorthands for localhost, and therefore, we cannot use the same shorthands we have been using when composing requests with curl and HTTPie.

Select GET in the dropdown menu at the left-hand side of the Enter request URL textbox, and enter localhost:5000/api/messages/ in this textbox at the right-hand side of the dropdown. Then, click Send and Postman will display the Status (200 OK), the time it took for the request to be processed and the response body with all the games formatted as JSON with syntax highlighting (Pretty view). The following screenshot shows the JSON response body in Postman for the HTTP GET request.

Working with GUI tools - Postman and others

Click on Headers at the right-hand side of Body and Cookies to read the response headers. The following screenshot shows the layout for the response headers that Postman displays for the previous response. Notice that Postman displays the Status at the right-hand side of the response and doesn't include it as the first line of the Headers, as happened when we worked with both the cURL and HTTPie utilities:

Working with GUI tools - Postman and others

Now, we will use the Builder tab in Postman to compose and send an HTTP request to create a new message, specifically, a POST request. Follow the next steps:

  1. Select POST in the drop-down menu at the left-hand side of the Enter request URL textbox, and enter localhost:5000/api/messages/ in this textbox at the right-hand side of the dropdown.
  2. Click Body at the right-hand side of Authorization and Headers, within the panel that composes the request.
  3. Activate the raw radio button and select JSON (application/json) in the dropdown at the right-hand side of the binary radio button. Postman will automatically add a Content-type = application/json header, and therefore, you will notice the Headers tab will be renamed to Headers (1), indicating us that there is one key-value pair specified for the request headers.
  4. Enter the following lines in the textbox below the radio buttons, within the Body tab:
{ 
   "message": "Measuring distance", 
   "duration": 5, 
   "message_category": "Information" 
} 

The following screenshot shows the request body in Postman:

Working with GUI tools - Postman and others

We followed the necessary steps to create an HTTP POST request with a JSON body that specifies the necessary key-value pairs to create a new game. Click Send and Postman will display the Status (201 Created), the time it took for the request to be processed and the response body with the recently added game formatted as JSON with syntax highlighting (Pretty view). The following screenshot shows the JSON response body in Postman for the HTTP POST request:

Working with GUI tools - Postman and others

Tip

If we want to compose and send an HTTP PATCH request for our API with Postman, it is necessary to follow the previously explained steps to provide JSON data within the request body.

Click or tap on the value for the url field in the JSON response body-/api/messages/2. You will notice that the value will be underlined when you hover the mouse pointer over it. Postman will automatically generate a GET request to localhost:5000/api/messages/2. Click Send to run it and retrieve the recently added message. The field is useful to browse the API with a tool such as Postman.

Because we made the necessary changes to generate an externally visible Flask development server, we can also use apps that can compose and send HTTP requests from mobile devices to work with the RESTful API. For example, we can work with the iCurlHTTP App on iOS devices such as iPad Pro and iPhone. In Android devices, we can work with the previously introduced HTTP Request App.

The following screenshot shows the results of composing and sending the following HTTP request with the iCurlHTTP App: GET http://192.168.2.3:5000/api/messages/. Remember that you have to perform the previously explained configurations in your LAN and router to be able to access the Flask development server from other devices connected to your LAN. In this case, the IP assigned to the computer running the Flask Web server is 192.168.2.3, and therefore, you must replace this IP with the IP assigned to your development computer.

Working with GUI tools - Postman and others

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

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