The objective of this post is to explain how to change the content-type of the HTTP answer of a request made to a Picoweb route. The tests were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board.
The objective of this post is to explain how to change the content-type of the HTTP answer of a request made to a Picoweb route. You can check an introductory tutorial about Picoweb and how to install it here.
For the successful completion of this tutorial, we assume a previous connection of the ESP32 to a WiFi network. You can check here how to do it.
Most of the code shown here will be similar to the one explained in the previous tutorial, so we will focus on the changes. In order to show the differences, we will set two routes to serve the same content (some HTML), in one case returned as text/html, and in the other just as text/plain. You can check more about the text content-type here.
We will then perform some requests using a web browser and thus, in the first route, it should show the rendered HTML (no tags should appear in the content shown to the user) and in the second route the source code should be shown without rendering the HTML.
The tests were performed using uPyCraft, a MicroPython IDE. You can check how to use uPyCraft in this previous post.
If you prefer, you can check a video version at my YouTube Channel.
We will start by importing the Picoweb module. Next we will create our app object and name it as we want.
import picoweb app = picoweb.WebApp("myApp")
Since we will be serving the same content just with two different content—types, we will put it in a variable. As mentioned, it will be some simple HTML, in this case to render a table with an header and two rows. Please note that this is a simple HTML code for exemplification, and thus we are not respecting the structure of a well formatted HTML document.
Also note that since our HTML string variable spawns across multiple lines, we need to enclose it in triple quotes. Nonetheless, we could have put everything in one line, although it would be harder to read.
htmlContent = ''' <table> <tr> <th>value</th> <th>timestamp</th> </tr> <tr> <td>10</td> <td>10:00</td> </tr> <tr> <td>11</td> <td>11:00</td> </tr> </table> '''
Next we will start defining our routes by using the route decorator of our app object. As input it will receive the URLs that will trigger the execution of the corresponding functions. We first route will be listening on the “/html” endpoint and the second one on “/text”.
Bellow each decorator we declare the corresponding handling function, which receives as input a HTTPRequest object and a StreamWriter object. The second one, we will use to send the response to the client.
@app.route("/html") def html(req, resp): (...) @app.route("/text") def text(req, resp): (...)
The two handling functions will be similar, being the only exception the initial call to the start_response function of the picoweb module, which is made to start building the HTTP response. One of the optional parameters of this function is the content_type, where we will specify for the first route “text/html” and for the second one “text/plain”.
Note that passing “text/html” in the first case is redundant, since it is the default value of the content_type argument. Nonetheless we are passing it for illustration purposes.
Note that this function also receives as input the previously mentioned StreamWriter object.
@app.route("/html") def html(req, resp): yield from picoweb.start_response(resp, content_type = "text/html") (...) @app.route("/text") def text(req, resp): yield from picoweb.start_response(resp, content_type = "text/plain") (...)
To finalize both functions, we call the awrite method of the StreamWriter object that the route handling function receives, and pass it the HTML content we previously defined on a variable.
Note that both function calls use the yeld from keywords, which are related to asynchronous functioning.
Finally we call the run method on our app object. As explained in the previous post, we need to pass the IP of the ESP32 we obtained when connecting to the WiFi in the host parameter, so it is possible for the Picoweb app to receive the requests.
You can check the full source code below, which already includes these final calls.
import picoweb app = picoweb.WebApp("myApp") htmlContent = ''' <table> <tr> <th>value</th> <th>timestamp</th> </tr> <tr> <td>10</td> <td>10:00</td> </tr> <tr> <td>11</td> <td>11:00</td> </tr> </table> ''' @app.route("/html") def html(req, resp): yield from picoweb.start_response(resp, content_type = "text/html") yield from resp.awrite(htmlContent) @app.route("/text") def text(req, resp): yield from picoweb.start_response(resp, content_type = "text/plain") yield from resp.awrite(htmlContent) app.run(debug=True, host = "192.168.1.87")
Testing the code
To test the code, just upload the previous script to your ESP32. In my case, I’m using the uPyCraft IDE, so it will automatically run the script after the upload.
Upon execution, a message like the one shown in figure 1 gets printed to the MicroPython prompt.
Figure 1 – Output of the MicroPython prompt, upon running the Picoweb app.
You can copy the URL highlighted and paste it on a web browser. Then, to test the first route, which will serve the content as HTML, just append a “html” word to the previously mentioned URL. you should get something similar to figure 2, corresponding to the rendered HTML.
Figure 2 – Output of the /html route.
Now to test the other route, just append “text” word to the original URL. you should get something similar to figure 3, where the source HTML code is returned as plain text.
Figure 3 – Output of the /text route.