<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Be Hai Nguyen</title>
    <description>The latest articles on Forem by Be Hai Nguyen (@behainguyen).</description>
    <link>https://forem.com/behainguyen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F878445%2F636e9f8c-d453-454b-93e8-179b82a2d5b6.jpeg</url>
      <title>Forem: Be Hai Nguyen</title>
      <link>https://forem.com/behainguyen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/behainguyen"/>
    <language>en</language>
    <item>
      <title>rlox: A Rust Implementation of “Crafting Interpreters” – Scanner</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Sat, 14 Jun 2025 02:13:25 +0000</pubDate>
      <link>https://forem.com/behainguyen/rlox-a-rust-implementation-of-crafting-interpreters-scanner-52fi</link>
      <guid>https://forem.com/behainguyen/rlox-a-rust-implementation-of-crafting-interpreters-scanner-52fi</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;br&gt;
I am attempting a Rust implementation of Robert Nystrom's Lox language discussed in &lt;a href="https://craftinginterpreters.com/" title="Crafting Interpreters" rel="noopener noreferrer"&gt;Crafting Interpreters&lt;/a&gt;. This post describes my Rust code equivalence for the &lt;a href="https://craftinginterpreters.com/scanning.html" title="Scanning" rel="noopener noreferrer"&gt;Scanning&lt;/a&gt; chapter. &lt;br&gt;
&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
🦀 &lt;a href="https://github.com/behai-nguyen/rlox" title="Index of the Complete Series" rel="noopener noreferrer"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

&lt;p&gt;This is the long list of existing &lt;a href="https://github.com/munificent/craftinginterpreters/wiki/Lox-Implementations#rust" title="Lox Implementations" rel="noopener noreferrer"&gt;Rust Lox Implementations&lt;/a&gt;. I downloaded and ran the first two, but I did not have a look at the code. I would like to take on this project as a challenge. If I complete it, I want it to reflect my own independent effort.&lt;/p&gt;

&lt;p&gt;&lt;a id="repository-cloning"&gt;&lt;/a&gt;&lt;br&gt;
🚀 &lt;strong&gt;Please note,&lt;/strong&gt; code for this post can be downloaded from GitHub with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.1.0 https://github.com/behai-nguyen/rlox.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="to-run"&gt;&lt;/a&gt;&lt;br&gt;
● To run interactively, first change to the &lt;code&gt;rlox/&lt;/code&gt; directory, then run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cargo run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter something like &lt;code&gt;var str2 = "秋の終わり";&lt;/code&gt;, and press &lt;code&gt;Enter&lt;/code&gt; — you will see the tokens printed out. Please refer to the screenshot below for an illustration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5o6x00k3bel3rtfllwwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5o6x00k3bel3rtfllwwh.png" alt="139-01.png" width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the moment, inputs are processed independently, meaning each new input does not retain any connection to previous inputs.&lt;/p&gt;

&lt;p&gt;To exit, simply press &lt;code&gt;Enter&lt;/code&gt; without entering anything.&lt;/p&gt;

&lt;p&gt;● To Run with a Lox script file, first change to the &lt;code&gt;rlox/&lt;/code&gt; directory, then run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cargo run ./tests/data/scanning/numbers.lox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are no errors, you will see the tokens printed out.&lt;/p&gt;

&lt;p&gt;&lt;a id="to-test"&gt;&lt;/a&gt;&lt;br&gt;
● To run existing tests, first change to the &lt;code&gt;rlox/&lt;/code&gt; directory, then run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cargo test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="repository-layout"&gt;&lt;/a&gt;&lt;br&gt;
❶ Repository Layout&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── Cargo.toml
├── README.md
├── src
│   ├── lib.rs
│   ├── lox_error.rs
│   ├── main.rs
│   ├── scanner_index.rs
│   ├── scanner.rs
│   ├── token.rs
│   └── token_type.rs
└── tests
    ├── data
    │   └── scanning
    │       ├── identifiers.lox
    │       ├── keywords.lox
    │       ├── numbers.lox
    │       ├── punctuators.lox
    │       ├── README.md
    │       ├── sample.lox
    │       ├── strings.lox
    │       ├── utf8_text.lox
    │       └── whitespace.lox
    ├── test_common.rs
    └── test_scanner.rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="code-annotation"&gt;&lt;/a&gt;&lt;br&gt;
❷ Let's briefly describe the project.&lt;/p&gt;

&lt;p&gt;● Identifier names follow Rust convention. In the &lt;a href="https://craftinginterpreters.com/scanning.html" title="Scanning" rel="noopener noreferrer"&gt;Scanning&lt;/a&gt; chapter, method names such as &lt;code&gt;scanTokens()&lt;/code&gt;, &lt;code&gt;peekNext()&lt;/code&gt; are &lt;code&gt;scan_tokens()&lt;/code&gt; and &lt;code&gt;peek_next()&lt;/code&gt; in Rust respectively.&lt;/p&gt;

&lt;p&gt;● Identifier names which are keywords in Rust will simply have an underscore (&lt;code&gt;&lt;em&gt;&lt;/em&gt;&lt;/code&gt;) suffix appended. For example, &lt;code&gt;match()&lt;/code&gt; becomes &lt;code&gt;match()&lt;/code&gt;, and &lt;code&gt;type&lt;/code&gt; becomes &lt;code&gt;type_&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;● The &lt;code&gt;src/scanner_index.rs&lt;/code&gt; module is not in the original Java version. It implements the Java variables &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;current&lt;/code&gt;, &lt;code&gt;line&lt;/code&gt; and some additional fields to support UTF-8 text scanning and slicing; please refer to &lt;a href="https://behainguyen.wordpress.com/2025/06/09/rust-working-with-utf-8-text/" title="Rust: Working with UTF-8 Text" rel="noopener noreferrer"&gt;this post&lt;/a&gt; for a full discussion on supporting UTF-8 text slicing.&lt;/p&gt;

&lt;p&gt;● In the &lt;code&gt;src/token.rs&lt;/code&gt; module, I am not sure if we need the &lt;code&gt;literal&lt;/code&gt; field in the &lt;code&gt;Token&lt;/code&gt; struct in the future. I leave it for the time being.&lt;/p&gt;

&lt;p&gt;● 💥 In the &lt;code&gt;src/scanner.rs&lt;/code&gt; module, the method &lt;code&gt;scan_tokens()&lt;/code&gt; returns an array (vector) of &lt;code&gt;Token&lt;/code&gt;; and the &lt;code&gt;run()&lt;/code&gt; function in the &lt;code&gt;src/main.rs&lt;/code&gt; module consumes this array and drops it. This array is local. In the Java implementation, it is a global class variable. This implementation might change in the future.&lt;/p&gt;

&lt;p&gt;● The &lt;code&gt;src/lox_error.rs&lt;/code&gt; module is also not in the original Java version. It implements a Rust specific error struct. &lt;/p&gt;

&lt;p&gt;● Under &lt;code&gt;tests/data/scanning/&lt;/code&gt; directory, except for &lt;code&gt;utf8_text.lox&lt;/code&gt; which is mine; the &lt;code&gt;README.md&lt;/code&gt; lists the original addresses of all other test data files.&lt;/p&gt;

&lt;p&gt;● The &lt;code&gt;tests/test_scanner.rs&lt;/code&gt; module implements test for each of the test data files in the &lt;code&gt;tests/data/scanning/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;a id="concluding-remarks"&gt;&lt;/a&gt;&lt;br&gt;
❸ The above points are specific to this implementation, otherwise the code adhere to &lt;a href="https://craftinginterpreters.com/" title="Crafting Interpreters" rel="noopener noreferrer"&gt;Crafting Interpreters&lt;/a&gt;, chapter &lt;a href="https://craftinginterpreters.com/scanning.html" title="Scanning" rel="noopener noreferrer"&gt;Scanning&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thank you for reading. I hope you find this post helpful. Stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2024/03/ubuntu-24-04-wallpaper" rel="noopener noreferrer"&gt;https://www.omgubuntu.co.uk/2024/03/ubuntu-24-04-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/" rel="noopener noreferrer"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;https://www.rust-lang.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/" rel="noopener noreferrer"&gt;https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://craftinginterpreters.com/" rel="noopener noreferrer"&gt;https://craftinginterpreters.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
🦀 &lt;a href="https://github.com/behai-nguyen/rlox" title="Index of the Complete Series" rel="noopener noreferrer"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

</description>
      <category>rust</category>
      <category>compiler</category>
      <category>interpreter</category>
      <category>scanner</category>
    </item>
    <item>
      <title>Raspberry Pi 4B: Natively Build a 64 Bit Fully Preemptible Kernel (Real-Time) with Desktop</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Sun, 03 Nov 2024 12:05:48 +0000</pubDate>
      <link>https://forem.com/behainguyen/raspberry-pi-4b-natively-build-a-64-bit-fully-preemptible-kernel-real-time-with-desktop-1afj</link>
      <guid>https://forem.com/behainguyen/raspberry-pi-4b-natively-build-a-64-bit-fully-preemptible-kernel-real-time-with-desktop-1afj</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;br&gt;
In this article, I present a step-by-step procedure to natively patch and build a 64-bit Fully Preemptible Kernel (Real-Time) for my Raspberry Pi 4B (Pi 4B). We start with a pre-made operating system image that includes a desktop environment. After building and installing the real-time patch kernel, querying the kernel information with the command &lt;code&gt;uname -a&lt;/code&gt; should report something similar to:&lt;br&gt;
&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Linux picnc 6.6.59-rt45-v8-behai-rt-build+ #1 SMP PREEMPT_RT Sat Nov  2 10:20:46 AEDT 2024 aarch64 GNU/Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This indicates that the token &lt;strong&gt;&lt;code&gt;PREEMPT_RT&lt;/code&gt;&lt;/strong&gt; is present in the output. &lt;strong&gt;Natively&lt;/strong&gt; in this instance means that we patch and build the kernel using the very Pi 4B on which the newly built kernel is installed.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Let's first describe the steps and then provide some background information on why I looked into this.&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-step-by-step"&gt;&lt;/a&gt;&lt;br&gt;
❶ We will now discuss the step-by-step procedure.&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-install-rpi-imager"&gt;&lt;/a&gt;&lt;br&gt;
⓵ I use a newly installed Ubuntu 24.04.1 LTS to write the target operating system (OS) image onto a microSD card. We need to install &lt;code&gt;Raspberry Pi Imager&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install rpi-imager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There should not be any problem. &lt;code&gt;Raspberry Pi Imager v1.8.5&lt;/code&gt; should appear in the Ubuntu application list. 💥 I first tried to install it on Ubuntu 22.10 Kinetic, but it reported a lot of errors, which I tried to fix without success.&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-install-pre-made-os"&gt;&lt;/a&gt;&lt;br&gt;
⓶ Use &lt;code&gt;Raspberry Pi Imager&lt;/code&gt; to write a pre-made OS image onto a microSD card. This application should be self-explanatory. For &lt;code&gt;Operating System&lt;/code&gt;, I select &lt;code&gt;Raspberry Pi OS (other)&lt;/code&gt;, then &lt;code&gt;Raspberry Pi OS Full (64-bit)&lt;/code&gt;. Please see the screenshots below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4rrtve7qglkdv0dy78l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4rrtve7qglkdv0dy78l.png" alt="125-01.png" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9bowz1q6jgyuqc9fnev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9bowz1q6jgyuqc9fnev.png" alt="125-02.png" width="708" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;NEXT&lt;/code&gt; button lets us configure the new OS. Please see the illustration below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5i4wcmgda858yb5cf9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5i4wcmgda858yb5cf9i.png" alt="125-03.png" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;SERVICES&lt;/code&gt; tab, we can enable SSH. The writing of the OS image should not take too long. After finishing writing, we should be able to boot without any issues. My &lt;code&gt;hostname&lt;/code&gt; is &lt;code&gt;picnc.local&lt;/code&gt;, and my &lt;code&gt;username&lt;/code&gt; is &lt;code&gt;behai&lt;/code&gt;. Both of the following commands should work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ping picnc.local
$ ssh behai@picnc.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="proc-install-pre-made-os-query-kernel-info"&gt;&lt;/a&gt;&lt;br&gt;
We can query the kernel information with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ uname -a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, the output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Linux picnc 6.6.51+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.6.51-1+rpt3 (2024-10-08) aarch64 GNU/Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;strong&gt;&lt;code&gt;PREEMPT&lt;/code&gt;&lt;/strong&gt; token. We are going to change that to &lt;strong&gt;&lt;code&gt;PREEMPT_RT&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We can also use the following commands to query the kernel information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ uname -r
$ uname -srm
$ hostnamectl
$ cat /proc/version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 The following steps are extracted from the Raspberry Pi page &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html" title="The Linux kernel" rel="noopener noreferrer"&gt;The Linux kernel&lt;/a&gt;. This official page covers multiple OSes. I have arranged the steps to suit the OS that I am using.&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-download-latest-kernel-code"&gt;&lt;/a&gt;&lt;br&gt;
⓷ Download the latest kernel source code. Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#download-kernel-source" title="Download kernel source" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;. Change to the &lt;code&gt;$HOME&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd $HOME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And run the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git clone --depth=1 https://github.com/raspberrypi/linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This downloads the kernel source code to &lt;code&gt;$HOME/linux&lt;/code&gt;. Please note that the &lt;code&gt;git&lt;/code&gt; utility is included with the OS image. It can be installed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="proc-real-time-kernel"&gt;&lt;/a&gt;&lt;br&gt;
⓸ Patch the real-time kernel. Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#patch-the-kernel" title="Patch the kernel" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;● Determine the current kernel version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ uname -r
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6.6.51+rpt-rpi-v8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;● Determine the kernel source version. Change to the kernel source directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following command to get the kernel source version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ head Makefile -n 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# SPDX-License-Identifier: GPL-2.0
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 6
SUBLEVEL = 59
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this instance, the kernel source version is &lt;code&gt;6.6.59&lt;/code&gt;. At the time, there was no version &lt;code&gt;6.6.59&lt;/code&gt; of the real-time kernel patch at &lt;a href="https://www.kernel.org/pub/linux/kernel/projects/rt/6.6/" title="Real-time kernel patches" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.kernel.org/pub/linux/kernel/projects/rt/6.6/" rel="noopener noreferrer"&gt;https://www.kernel.org/pub/linux/kernel/projects/rt/6.6/&lt;/a&gt;, so I used patch version &lt;code&gt;6.6.58&lt;/code&gt; instead. Please note that on the internet, there are numerous mentions of this issue. Sometimes patches are behind, and using a not-so-older version of the patch should be okay.&lt;/p&gt;

&lt;p&gt;● Apply the target real-time kernel patch. 🙏 Please recall that the present working directory is &lt;code&gt;$HOME/linux&lt;/code&gt;. The following commands download, uncompress, and patch the kernel source code with the target real-time kernel patch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wget https://www.kernel.org/pub/linux/kernel/projects/rt/6.6/patch-6.6.58-rt45.patch.gz
$ gunzip patch-6.6.58-rt45.patch.gz
$ cat patch-6.6.58-rt45.patch | patch -p1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="proc-config-kernel"&gt;&lt;/a&gt;&lt;br&gt;
⓹ Configure the kernel. Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#configure-the-kernel" title="Configure the kernel" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;● Install build tools/dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd $HOME
$ sudo apt install libncurses5-dev flex build-essential bison libssl-dev bc make
$ cd linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't think it's necessary to navigate to the &lt;code&gt;$HOME&lt;/code&gt; directory to perform the installation, but I prefer to do so.&lt;/p&gt;

&lt;p&gt;● Prepare the default kernel configuration: &lt;code&gt;$HOME/linux/.config&lt;/code&gt;. Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#native-build-configuration" title="Build configuration" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;. 🙏 The present working directory is &lt;code&gt;$HOME/linux&lt;/code&gt;. Run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ KERNEL=kernel8
$ make bcm2711_defconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last output lines of the second command are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
#
# configuration written to .config
#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;● Check real-time configuration items in &lt;code&gt;/home/behai/linux/.config&lt;/code&gt;. Open &lt;code&gt;/home/behai/linux/.config&lt;/code&gt; and look for the following real-time entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
CONFIG_PREEMPT_BUILD=y
# CONFIG_PREEMPT_NONE is not set
# CONFIG_PREEMPT_VOLUNTARY is not set
CONFIG_PREEMPT=y
# CONFIG_PREEMPT_RT is not set
CONFIG_PREEMPT_COUNT=y
CONFIG_PREEMPTION=y
# CONFIG_PREEMPT_DYNAMIC is not set
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Based on my previous failed attempts, I want to ascertain that &lt;code&gt;PREEMPT_RT&lt;/code&gt; is not turned on. And indeed, it is not.&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-config-localversion"&gt;&lt;/a&gt;&lt;br&gt;
⓺ Customise the kernel version using &lt;code&gt;LOCALVERSION&lt;/code&gt;. Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#native-customisation" title="Customise the kernel version using LOCALVERSION" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/home/behai/linux/.config&lt;/code&gt;, locate the &lt;code&gt;LOCALVERSION&lt;/code&gt; entry, which should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONFIG_LOCALVERSION="-v8"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And update it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONFIG_LOCALVERSION="-v8-behai-rt-build"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next section on &lt;code&gt;menuconfig&lt;/code&gt;, we will verify that this manual update takes effect.&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-config-menuconfig"&gt;&lt;/a&gt;&lt;br&gt;
⓻ Using the UI tool &lt;code&gt;menuconfig&lt;/code&gt; to configure the kernel to Fully Preemptible Kernel (Real-Time). Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#menuconfig" title="menuconfig" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;. 🙏 The present working directory is &lt;code&gt;$HOME/linux&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;-- Please note that this UI tool also updates the &lt;code&gt;$HOME/linux/.config&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Run the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ make menuconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes well, and it should, you will get the UI shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81sh8ofvmbyqdmxairc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81sh8ofvmbyqdmxairc1.png" alt="125-04.png" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the up and down arrow keys to move between the vertical entries, use the left and right arrow keys to move between the horizontal entries at the bottom of the screen. Press the Enter key to select the highlighted item. Activate the &lt;code&gt;&amp;lt; Load &amp;gt;&lt;/code&gt; menu item. The following pop-up appears:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnntt0n6l5ttvn4ie8r4b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnntt0n6l5ttvn4ie8r4b.png" alt="125-05.png" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;code&gt;&amp;lt; Ok &amp;gt;&lt;/code&gt; to confirm using the &lt;code&gt;$HOME/linux/.config&lt;/code&gt; configuration file, which has been loaded by default.&lt;/p&gt;

&lt;p&gt;Activate the first menu item &lt;code&gt;General setup ---&amp;gt;&lt;/code&gt;. On the next screen, select &lt;code&gt;Preemption Model (Preemptible Kernel (Low-Latency Desktop))  ---&amp;gt;&lt;/code&gt; as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ertmqj73accl33kdr34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ertmqj73accl33kdr34.png" alt="125-06.png" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the pop-up dialog, select &lt;code&gt;( ) Fully Preemptible Kernel (Real-Time)&lt;/code&gt; as per the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk67aj5b6f2erx39p3rys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk67aj5b6f2erx39p3rys.png" alt="125-07.png" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main screen should show &lt;code&gt;Preemption Model (Fully Preemptible Kernel (Real-Time))  ---&amp;gt;&lt;/code&gt; as per the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhnmyo10rrc3h33x37a4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhnmyo10rrc3h33x37a4.png" alt="125-08.png" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-config-localversion-confirm"&gt;&lt;/a&gt;&lt;br&gt;
Recall that in a previous section, we customised the kernel version &lt;code&gt;LOCALVERSION&lt;/code&gt;. The third entry in the previous screen confirms this manual customisation as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo55xj9ob0ublsz2trdxc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo55xj9ob0ublsz2trdxc.png" alt="125-09.png" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the changes and exit &lt;code&gt;menuconfig&lt;/code&gt;. 💥 As a precaution, rerun &lt;code&gt;menuconfig&lt;/code&gt; to confirm that your changes were saved.&lt;/p&gt;

&lt;p&gt;👉 As a final verification, ensure that &lt;code&gt;CONFIG_PREEMPT_RT&lt;/code&gt; is updated to &lt;code&gt;y&lt;/code&gt;. Open &lt;code&gt;/home/behai/linux/.config&lt;/code&gt; and look for the following real-time entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# CONFIG_PREEMPT_NONE is not set
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT is not set
CONFIG_PREEMPT_RT=y
CONFIG_PREEMPT_COUNT=y
CONFIG_PREEMPTION=y
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="proc-kernel-native-build"&gt;&lt;/a&gt;&lt;br&gt;
⓼ We now discuss the actual kernel build process.&lt;/p&gt;

&lt;p&gt;● Install kernel headers. Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#kernel-headers" title="Kernel headers" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;. Run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd $HOME
$ sudo apt install linux-headers-rpi-v8
$ cd linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;● Build the kernel. Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#native-build" title="Build" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;. 🙏 The present working directory is &lt;code&gt;$HOME/linux&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My Pi 4B has 8GB of RAM and 4 cores. The build process takes around 2 hours. To determine the number of cores, run the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nproc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the number of cores in the build command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ make -j4 Image.gz modules dtbs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes a few hours. We can do something else in the meantime, but keep an eye on it.&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-install-newly-built-kernel"&gt;&lt;/a&gt;&lt;br&gt;
⓽ Install the newly built kernel. Please refer to &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html#native-install" title="Install the kernel" rel="noopener noreferrer"&gt;this official section&lt;/a&gt;. 🙏 The present working directory is still &lt;code&gt;$HOME/linux&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;● First, install the kernel modules onto the boot media:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo make -j4 modules_install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;● Then, install the kernel and Device Tree blobs into the boot partition, backing up the original kernel with the commands listed below. Please recall that the temporary environment variable &lt;code&gt;$KERNEL&lt;/code&gt; is defined in this step. If you were interrupted and had to turn off the computer or terminated the SSH session, you will need to set it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo cp /boot/firmware/$KERNEL.img /boot/firmware/$KERNEL-backup.img
$ sudo cp arch/arm64/boot/Image.gz /boot/firmware/$KERNEL.img
$ sudo cp arch/arm64/boot/dts/broadcom/*.dtb /boot/firmware/
$ sudo cp arch/arm64/boot/dts/overlays/*.dtb* /boot/firmware/overlays/
$ sudo cp arch/arm64/boot/dts/overlays/README /boot/firmware/overlays/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="proc-reboot-verify"&gt;&lt;/a&gt;&lt;br&gt;
⓾ Finally, reboot to ensure that the OS works 😂 and the kernel is indeed a &lt;code&gt;Fully Preemptible Kernel (Real-Time)&lt;/code&gt; kernel.&lt;/p&gt;

&lt;p&gt;To reboot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ping should respond successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ping picnc.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SSH should work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh behai@picnc.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response I received was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Linux picnc 6.6.59-rt45-v8-behai-rt-build+ #1 SMP PREEMPT_RT Sat Nov  2 10:20:46 AEDT 2024 aarch64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Nov  2 12:03:31 2024
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note the tokens &lt;strong&gt;&lt;code&gt;6.6.59-rt45-v8-behai-rt-build+&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;PREEMPT_RT&lt;/code&gt;&lt;/strong&gt; in the first line. We have successfully built and installed a &lt;code&gt;Fully Preemptible Kernel (Real-Time)&lt;/code&gt; kernel. ✔️ I also ran the commands to query the kernel information as discussed in a prior section.&lt;/p&gt;

&lt;p&gt;&lt;a id="proc-xrdp"&gt;&lt;/a&gt;&lt;br&gt;
💥 As a final note on this OS, the open-source Remote Desktop Protocol server &lt;a href="https://www.xrdp.org/" title="an open-source Remote Desktop Protocol server" rel="noopener noreferrer"&gt;xrdp&lt;/a&gt; does not seem to work properly. &lt;strong&gt;After writing the&lt;/strong&gt; initial OS image, I installed this &lt;code&gt;xrdp&lt;/code&gt; server, but Windows Remote Desktop Connection just shows a blank black screen. A few days prior, I tried this same image, but the release date was 04/July/2024, as seen in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxnbp0fpjwkhcjr8s8q7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxnbp0fpjwkhcjr8s8q7.png" alt="002-pi-os-full-64-bit-debian-bookworm.png" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And Remote Desktop Connection worked perfectly. Raspberry Pi updated this image while I was still trying to resolve this &lt;code&gt;Fully Preemptible Kernel (Real-Time)&lt;/code&gt; issue.&lt;/p&gt;

&lt;p&gt;&lt;a id="background"&gt;&lt;/a&gt;&lt;br&gt;
❷ As mentioned at the beginning, in this section I'm discussing why I have looked into this issue. I am interested in learning CNC, and my goal is to build my own CNC machine. I know nothing about CNC and electronics. I have been doing a little bit of basic physical computing with the Pi 4B using Python. In the process, I have learned some basic electronics as well.&lt;/p&gt;

&lt;p&gt;My initial research on CNC pointed to the &lt;a href="https://www.machsupport.com/software/mach3/" title="Mach3 CNC machine controller" rel="noopener noreferrer"&gt;Mach3 CNC machine controller&lt;/a&gt;, which runs on Windows. Further research led me to &lt;a href="http://www.linuxcnc.org/docs/html/getting-started/system-requirements.html" title="LinuxCNC" rel="noopener noreferrer"&gt;LinuxCNC&lt;/a&gt;, which I found to be a more attractive option, especially since it can run on a Raspberry Pi computer. It became apparent that we need a real-time kernel.&lt;/p&gt;

&lt;p&gt;&lt;a id="background-source-info-01"&gt;&lt;/a&gt;&lt;br&gt;
⓵ My initial source of information is a 6-year-old YouTube series, &lt;a href="https://www.youtube.com/playlist?list=PLaamliiI72ntlrHKIFjh2VjmehRGgZpjm" title="LinuxCNC for the Hobbyist by Mr. Joe Hildreth" rel="noopener noreferrer"&gt;LinuxCNC for the Hobbyist&lt;/a&gt;, by Mr. Joe Hildreth. The 17th video, &lt;a href="https://www.youtube.com/watch?v=RjTfKF7gcIo&amp;amp;list=PLaamliiI72ntlrHKIFjh2VjmehRGgZpjm&amp;amp;index=17" title="Compiling a Realtime Kernel for LinuxCNC" rel="noopener noreferrer"&gt;Compiling a Realtime Kernel for LinuxCNC&lt;/a&gt;, discusses building a real-time kernel. I found the accompanying written tutorial, &lt;a href="https://myheap.com/cnc-stuff/linuxcnc-emc2/92-my-heap-articles/computer-numerical-control/linuxcnc/written-tutorials/198-compiling-a-realtime-kernel-for-linuxcnc.html" title="Compiling a Realtime Linux kernel with the preemp-rt kernel patch" rel="noopener noreferrer"&gt;Compiling a Realtime Linux kernel with the preemp-rt kernel patch&lt;/a&gt;, much easier to follow.&lt;/p&gt;

&lt;p&gt;&lt;a id="background-source-info-02"&gt;&lt;/a&gt;&lt;br&gt;
⓶ My second source of information is a 2-year-old LinuxCNC forum post titled &lt;a href="https://forum.linuxcnc.org/9-installing-linuxcnc/47662-installing-linuxcnc-2-9-on-raspberry-pi-4b-with-preempt-rt-kernel?start=0" title="Installing LinuxCNC 2.9 on Raspberry Pi 4B with Preempt-RT kernel" rel="noopener noreferrer"&gt;Installing LinuxCNC 2.9 on Raspberry Pi 4B with Preempt-RT kernel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="background-failed-attempts"&gt;&lt;/a&gt;&lt;br&gt;
⓷ I will not go into the details of my failed attempts to build a real-time kernel. However, between the instructions, I had about 6 tries over the span of 12 days 😂, and despite successfully building and installing, the kernel only showed &lt;code&gt;PREEMPT&lt;/code&gt;, not &lt;code&gt;PREEMPT_RT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On my last failed attempt, I went ahead and installed &lt;a href="http://www.linuxcnc.org/docs/html/getting-started/system-requirements.html" title="LinuxCNC" rel="noopener noreferrer"&gt;LinuxCNC&lt;/a&gt; anyway. A variation of it was installed successfully, and I was able to run it via Remote Desktop Connection as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyszd7sxu1xncl2584n3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyszd7sxu1xncl2584n3t.png" alt="125-10.png" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="background-break-through"&gt;&lt;/a&gt;&lt;br&gt;
⓸ This nearly 3-year-old Stack Overflow post, &lt;a href="https://stackoverflow.com/questions/71645509/raspberry-pi4-kernel-64bit-with-rt-extension" title="Raspberry pi4: kernel 64bit with RT extension" rel="noopener noreferrer"&gt;Raspberry pi4: kernel 64bit with RT extension&lt;/a&gt;, addresses the very same problem I had, and the author managed to solve it. This post eventually led me to the previously mentioned Raspberry Pi official page, &lt;a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html" title="The Linux kernel" rel="noopener noreferrer"&gt;The Linux kernel&lt;/a&gt;, and this successful build and installation.&lt;/p&gt;

&lt;p&gt;Compiling the Linux kernel is not my cup of tea. However, I wouldn't say my failed attempts were a waste of time. I have learned many things during these exercises, and some of the steps are similar to the final successful attempt.&lt;/p&gt;

&lt;p&gt;&lt;a id="background-concluding-remarks"&gt;&lt;/a&gt;&lt;br&gt;
⓹ This has been a new and interesting experience for me. I have learned quite a few things during this process. I am not sure if I can achieve my goal, as I have done some research into hardware, and there are many things I have no knowledge of. But I keep my fingers crossed...&lt;/p&gt;

&lt;p&gt;Thank you for reading. I hope you find the information in this post useful. Stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.instructables.com/Easy-Raspberry-Pi-Based-ScreensaverSlideshow-for-E/" rel="noopener noreferrer"&gt;https://www.instructables.com/Easy-Raspberry-Pi-Based-ScreensaverSlideshow-for-E/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>raspberrypi</category>
      <category>preempt</category>
      <category>rt</category>
      <category>kernel</category>
    </item>
    <item>
      <title>Python &amp; MariaDB: Which Driver? An Example of Executing a Stored Procedure That Returns Multiple Result Sets</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Sun, 28 Jul 2024 11:25:43 +0000</pubDate>
      <link>https://forem.com/behainguyen/python-mariadb-which-driver-an-example-of-executing-a-stored-procedure-that-returns-multiple-result-sets-i2b</link>
      <guid>https://forem.com/behainguyen/python-mariadb-which-driver-an-example-of-executing-a-stored-procedure-that-returns-multiple-result-sets-i2b</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;br&gt;
In this discussion, I explain why I prefer the Python MySQL driver, &lt;a href="https://pypi.org/project/mysql-connector-python/" title="MySQL driver written in Python" rel="noopener noreferrer"&gt;mysql-connector-python&lt;/a&gt;, for the &lt;a href="https://mariadb.com/" title="MariaDB" rel="noopener noreferrer"&gt;MariaDB&lt;/a&gt; database over the &lt;a href="https://pypi.org/project/mariadb/" title="MariaDB Connector/C client library for Python" rel="noopener noreferrer"&gt;mariadb&lt;/a&gt; driver. The latter appears to be recommended by the official &lt;a href="https://mariadb.com/resources/blog/how-to-connect-python-programs-to-mariadb/" title="How to connect Python programs to MariaDB" rel="noopener noreferrer"&gt;MariaDB documentation&lt;/a&gt; and is also mentioned on the SQLAlchemy page for &lt;a href="https://docs.sqlalchemy.org/en/20/dialects/mysql.html" title="MySQL and MariaDB" rel="noopener noreferrer"&gt;MySQL and MariaDB&lt;/a&gt;.&lt;br&gt;
&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="background-discussion"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
❶ This new post could be considered as related to some previous posts listed below:
&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2022/07/01/synology-ds218-mariadb-10-enabling-remote-connection/" title="Synology DS218: MariaDB 10 enabling remote connection" rel="noopener noreferrer"&gt;Synology DS218: MariaDB 10 enabling remote connection&lt;/a&gt;: This Synology DS218 Linux box, which serves as the MariaDB 10 database server, is used in this new post.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2022/11/09/python-executing-mysql-stored-procedures-which-return-multiple-result-sets/" title="Python: executing MySQL stored procedures which return multiple result sets" rel="noopener noreferrer"&gt;Python: executing MySQL stored procedures which return multiple result sets&lt;/a&gt;: This new post is an extension of the previous one. We will be using the same database content, the same stored procedure, and the same Python test code.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2022/11/15/python-executing-postgresql-stored-functions-which-return-multiple-result-sets/" title="Python: executing PostgreSQL stored functions which return multiple result sets" rel="noopener noreferrer"&gt;Python: executing PostgreSQL stored functions which return multiple result sets&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/09/17/rust-mysql-executing-mysql-stored-procedures-which-return-multiple-result-sets-using-crate-sqlx/" title="Rust &amp;amp; MySQL: executing MySQL stored procedures which return multiple result sets using crate sqlx" rel="noopener noreferrer"&gt;Rust &amp;amp; MySQL: executing MySQL stored procedures which return multiple result sets using crate sqlx&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="mariadb"&gt;&lt;/a&gt;&lt;br&gt;
❷ Around two years ago, when I bought my Synology DS218 box, I became aware of the MariaDB database. My research suggests that it is superior to and also compatible with the &lt;a href="https://www.mysql.com/" title="MySQL database" rel="noopener noreferrer"&gt;MySQL database&lt;/a&gt;. As demonstrated in a previously mentioned post, &lt;a href="https://behainguyen.wordpress.com/2022/07/01/synology-ds218-mariadb-10-enabling-remote-connection/" title="Synology DS218: MariaDB 10 enabling remote connection" rel="noopener noreferrer"&gt;Synology DS218: MariaDB 10 enabling remote connection&lt;/a&gt;, I can use all MySQL client tools with the MariaDB database. Please refer to the following post, &lt;a href="https://www.cloudways.com/blog/mariadb-vs-mysql/" title="MariaDB vs MySQL: A Detailed Comparison" rel="noopener noreferrer"&gt;MariaDB vs MySQL: A Detailed Comparison&lt;/a&gt;, for further information.&lt;/p&gt;

&lt;p&gt;&lt;a id="mariadb-driver"&gt;&lt;/a&gt;&lt;br&gt;
❸ I have not been using the MariaDB database in my development, as I have both the MySQL and &lt;a href="https://www.postgresql.org/" title="PostgreSQL" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; databases available.&lt;/p&gt;

&lt;p&gt;I have been exploring the MariaDB database recently. I tried out the recommended &lt;a href="https://pypi.org/project/mariadb/" title="MariaDB Connector/C client library for Python" rel="noopener noreferrer"&gt;mariadb&lt;/a&gt; driver. Instead of following the official example, I experimented with my own stored procedure which returns multiple result sets, replicating the example in &lt;a href="https://behainguyen.wordpress.com/2022/11/09/python-executing-mysql-stored-procedures-which-return-multiple-result-sets/" title="Python: executing MySQL stored procedures which return multiple result sets" rel="noopener noreferrer"&gt;a previously&lt;/a&gt; mentioned post.&lt;/p&gt;

&lt;p&gt;To recap:&lt;/p&gt;

&lt;p&gt;⓵ The source database is the &lt;code&gt;MySQL test data&lt;/code&gt; released by Oracle Corporation, downloadable from &lt;a href="https://github.com/datacharmer/test_db" title="MySQL test data " rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/datacharmer/test_db" rel="noopener noreferrer"&gt;https://github.com/datacharmer/test_db&lt;/a&gt;. Using MySQL tools, I backed up a MySQL database and restored the backup content to the MariaDB database server on the Synology DS218 Linux box.&lt;/p&gt;

&lt;p&gt;⓶ The stored procedure is reprinted below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;delimiter&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt;

&lt;span class="k"&gt;drop&lt;/span&gt; &lt;span class="k"&gt;procedure&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="n"&gt;DemoStoredProc1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;procedure&lt;/span&gt; &lt;span class="n"&gt;DemoStoredProc1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;pm_dept_no&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;reads&lt;/span&gt; &lt;span class="k"&gt;sql&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;departments&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;dept_no&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pm_dept_no&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dept_manager&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;dept_no&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pm_dept_no&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 I verified that this stored procedure works as expected with the MariaDB database. If there is a match, the first record set will have only a single record, the second record set will have one or more records: they are  distinct record sets. Please note, we could have more than two, but for the purpose of this post, two should suffice.&lt;/p&gt;

&lt;p&gt;⓷ Install the &lt;a href="https://pypi.org/project/mariadb/" title="MariaDB Connector/C client library for Python" rel="noopener noreferrer"&gt;mariadb&lt;/a&gt; driver into the active virtual environment.&lt;/p&gt;

&lt;p&gt;⓸ The Python code is copied from the &lt;a href="https://behainguyen.wordpress.com/2022/11/09/python-executing-mysql-stored-procedures-which-return-multiple-result-sets/" title="Python: executing MySQL stored procedures which return multiple result sets" rel="noopener noreferrer"&gt;previous post&lt;/a&gt; as mentioned several times above. &lt;strong&gt;The only modification is:&lt;/strong&gt; replacing the connection string as appropriate for the &lt;code&gt;mariadb&lt;/code&gt; driver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Content of mariadb-example.py:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;closing&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mariadb+mariadbconnector://behai:O,U#n*m:5QB3_zbQ@192.168.0.14:3306/employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# engine = create_engine( 'mysql+mysqlconnector://behai:O,U#n*m:5QB3_zbQ@192.168.0.14:3306/employees', echo = False )
&lt;/span&gt;
&lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raw_connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;closing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callproc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DemoStoredProc1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;d001&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sr&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stored_results&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="c1"&gt;#-- 
&lt;/span&gt;            &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;#--
&lt;/span&gt;            &lt;span class="n"&gt;sr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pprint&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Exception. Type &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;connection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note, &lt;code&gt;192.168.0.14&lt;/code&gt; is the IP address of my Synology DS218 Linux box.&lt;/p&gt;

&lt;p&gt;It produces the following runtime error:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;strong&gt;Exception. Type &amp;lt;class 'AttributeError'&amp;gt;: 'Cursor' object has no attribute 'stored_results'&lt;/strong&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Please also see the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.wordpress.com%2Fwp-content%2Fuploads%2F2024%2F07%2F118-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.wordpress.com%2Fwp-content%2Fuploads%2F2024%2F07%2F118-01.png" alt="118-01.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is not a database server error, but rather a driver error. However, we know the MariaDB server executes the above stored procedure correctly, so there is no problem on the server-side. The following page is the official MySQL documentation on the &lt;a href="https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-stored-results.html" title="MySQLCursor.stored_results() method" rel="noopener noreferrer"&gt;&lt;code&gt;MySQLCursor.stored_results()&lt;/code&gt; method&lt;/a&gt;. I have not been able to locate an equivalent for the MariaDB database.&lt;/p&gt;

&lt;p&gt;Having already installed the &lt;a href="https://pypi.org/project/mysql-connector-python/" title="MySQL driver written in Python" rel="noopener noreferrer"&gt;mysql-connector-python&lt;/a&gt; driver, switching the connection string to &lt;code&gt;mysql+mysqlconnector&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# engine = create_engine( 'mariadb+mariadbconnector://behai:O,U#n*m:5QB3_zbQ@192.168.0.14:3306/employees', echo = False )
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mysql+mysqlconnector://behai:O,U#n*m:5QB3_zbQ@192.168.0.14:3306/employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;produces the expected results. Please refer to the two screenshots below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.wordpress.com%2Fwp-content%2Fuploads%2F2024%2F07%2F118-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.wordpress.com%2Fwp-content%2Fuploads%2F2024%2F07%2F118-02.png" alt="118-02.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.wordpress.com%2Fwp-content%2Fuploads%2F2024%2F07%2F118-03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.wordpress.com%2Fwp-content%2Fuploads%2F2024%2F07%2F118-03.png" alt="118-03.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am not sure if I missed something about the &lt;a href="https://pypi.org/project/mariadb/" title="MariaDB Connector/C client library for Python" rel="noopener noreferrer"&gt;mariadb&lt;/a&gt; driver. I have looked through its documentation, and I have yet to find an equivalent of &lt;code&gt;MySQLCursor.stored_results()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;❹ Testing with the &lt;a href="https://pypi.org/project/bh-database/" title="bh-database" rel="noopener noreferrer"&gt;bh-database&lt;/a&gt; wrapper classes for SQLAlchemy.&lt;/p&gt;

&lt;p&gt;For the two &lt;a href="https://github.com/behai-nguyen/bh_database/tree/main/examples" title="bh-database examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; provided, if you want to access the MariaDB &lt;code&gt;employees&lt;/code&gt; database, simply modify the connection string to match the one used in the above example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SQLALCHEMY_DATABASE_URI = mysql+mysqlconnector://behai:O,U#n*m:5QB3_zbQ@192.168.0.14:3306/employees
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All functions from the two &lt;a href="https://github.com/behai-nguyen/bh_database/tree/main/examples" title="bh-database examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; will continue to operate correctly when using the MariaDB database.&lt;/p&gt;

&lt;p&gt;❺ Given all the observations discussed above, the &lt;a href="https://pypi.org/project/mysql-connector-python/" title="MySQL driver written in Python" rel="noopener noreferrer"&gt;mysql-connector-python&lt;/a&gt; driver appears to be suitable for the MariaDB database. I plan to undertake some development work with MariaDB in the future. I will share any interesting findings I encounter with the MariaDB database.&lt;/p&gt;

&lt;p&gt;Thank you for reading. I hope you find the information in this post useful. Stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper" rel="noopener noreferrer"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/" rel="noopener noreferrer"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.python.org/downloads/release/python-3124/" rel="noopener noreferrer"&gt;https://www.python.org/downloads/release/python-3124/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>mariadb</category>
      <category>driver</category>
    </item>
    <item>
      <title>Python FastAPI: Implementing Non-Blocking Logging with Built-In QueueHandler and QueueListener Classes</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Tue, 02 Jul 2024 10:57:17 +0000</pubDate>
      <link>https://forem.com/behainguyen/python-fastapi-implementing-non-blocking-logging-with-built-in-queuehandler-and-queuelistener-classes-2ahi</link>
      <guid>https://forem.com/behainguyen/python-fastapi-implementing-non-blocking-logging-with-built-in-queuehandler-and-queuelistener-classes-2ahi</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;br&gt;
Continuing with our &lt;a href="https://github.com/behai-nguyen/fastapi_learning" title="Index of the Python FastAPI Complete Series"&gt;Python FastAPI learning series&lt;/a&gt;, this post explores the implementation of non-blocking logging using Python’s built-in &lt;a href="https://docs.python.org/3/library/logging.config.html#configuring-queuehandler-and-queuelistener" title="Configuring QueueHandler and QueueListener"&gt;QueueHandler and QueueListener classes&lt;/a&gt;.&lt;br&gt;
&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
🐍 &lt;a href="https://github.com/behai-nguyen/fastapi_learning" title="Index of the Complete Series"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

&lt;p&gt;Starting from this post, the code will require Python 3.12.4. Please refer to the &lt;a href="https://github.com/behai-nguyen/fastapi_learning#the-code-after-tag-v040-requires-python-3124" title="The Code After Tag v0.4.0 Requires Python 3.12.4"&gt;following discussion&lt;/a&gt; on how to upgrade to Python 3.12.4.&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Please note,&lt;/strong&gt; complete code for this post can be downloaded from GitHub with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.5.0 https://github.com/behai-nguyen/fastapi_learning.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;Table Of Contents&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
❶ Definition and Complete Working Example of &lt;code&gt;Non-Blocking Logging&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
❷ Structure of Logging
&lt;/li&gt;
&lt;li&gt;
❸ Project Layout
&lt;/li&gt;
&lt;li&gt;
❹ Implementation of &lt;code&gt;Non-Blocking Logging&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;
⓵ YAML Logger Configuration File
&lt;/li&gt;
&lt;li&gt;
⓶ New Python Module: &lt;code&gt;common/queue_logging.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
⓷ Updates to the &lt;code&gt;main.py&lt;/code&gt; Module
&lt;/li&gt;
&lt;li&gt;
⓸ Incorporating Logging into Existing Modules
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;

&lt;li&gt;

❺ Essential Official Documentation
&lt;/li&gt;

&lt;li&gt;

❻ Concluding Remarks
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a id="non-blocking-logging"&gt;&lt;/a&gt;&lt;br&gt;
❶ In essence, &lt;code&gt;non-blocking logging&lt;/code&gt; means that the actual logging task does not hold up the thread performing the logging. This thread does not have to wait for the logging to complete and can move to the next instruction immediately.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Non-blocking logging&lt;/code&gt; is achieved via three principal built-in classes: &lt;a href="https://docs.python.org/3/library/queue.html#queue.Queue" title="queue.Queue"&gt;queue.Queue&lt;/a&gt;, &lt;a href="https://docs.python.org/3/library/logging.handlers.html#queuehandler" title="QueueHandler"&gt;QueueHandler&lt;/a&gt;, and &lt;a href="https://docs.python.org/3/library/logging.handlers.html#queuelistener" title="QueueListener"&gt;QueueListener&lt;/a&gt;. An instance of &lt;code&gt;queue.Queue&lt;/code&gt; is accessible by both a &lt;code&gt;non-blocking&lt;/code&gt; &lt;code&gt;QueueHandler&lt;/code&gt; instance and a &lt;code&gt;QueueListener&lt;/code&gt; instance. The &lt;code&gt;QueueListener&lt;/code&gt; instance passes the logging messages to its own &lt;code&gt;blocking&lt;/code&gt; handler(s), such as a &lt;a href="https://docs.python.org/3/library/logging.handlers.html#rotatingfilehandler" title="RotatingFileHandler"&gt;RotatingFileHandler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;According to the official documentation for &lt;a href="https://docs.python.org/3/library/logging.handlers.html#queuehandler" title="QueueHandler"&gt;QueueHandler&lt;/a&gt; and &lt;a href="https://docs.python.org/3/library/logging.handlers.html#queuelistener" title="QueueListener"&gt;QueueListener&lt;/a&gt;, they have their own separate thread for logging. This frees the main thread from waiting for the logging to finish, &lt;strong&gt;thereby preventing it from being blocked&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="non-blocking-logging-example"&gt;&lt;/a&gt;&lt;br&gt;
The complete working example below, adapted from a &lt;a href="https://stackoverflow.com/a/70716053" title="Python - asynchronous logging"&gt;Stack Overflow answer&lt;/a&gt; and a &lt;a href="https://medium.com/@dresraceran/implementing-async-logging-in-fastapi-middleware-b112aa9c0db8" title="Implementing Async Logging in FastAPI Middleware"&gt;Medium post&lt;/a&gt;, illustrates how the aforementioned classes fit together to implement &lt;code&gt;non-blocking logging&lt;/code&gt;. Logging messages are written to the &lt;code&gt;queue.log&lt;/code&gt; file in the same directory as the Python script file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;logging.handlers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QueueHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QueueListener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RotatingFileHandler&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;log_queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Non-blocking handler.
&lt;/span&gt;&lt;span class="n"&gt;queue_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;QueueHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_queue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

&lt;span class="n"&gt;queue_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%(asctime)s - %(levelname)s - %(message)s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Attached to the root logger.
&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue_handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           

&lt;span class="c1"&gt;# The blocking handler.
&lt;/span&gt;&lt;span class="n"&gt;rot_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RotatingFileHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;queue.log&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Sitting comfortably in its own thread, isolated from async code.
&lt;/span&gt;&lt;span class="n"&gt;queue_listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;QueueListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rot_handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Start listening.
&lt;/span&gt;&lt;span class="n"&gt;queue_listener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Application is shutting down.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Should stop listening.
&lt;/span&gt;    &lt;span class="n"&gt;queue_listener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I am &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello World&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am not entirely sure if the above logging approach can also be classified as &lt;code&gt;asynchronous logging&lt;/code&gt;, because only one thread can control the Python interpreter at a time, as discussed in &lt;a href="https://realpython.com/python-gil/" title="What Is the Python Global Interpreter Lock (GIL)?"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The final implementation for this post is slightly more complex than the example above, but the underlying technical principles remain the same.&lt;/p&gt;

&lt;p&gt;&lt;a id="logging-functionality"&gt;&lt;/a&gt;&lt;br&gt;
❷ Let’s discuss how the logging messages for each request should be structured.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
When a request is being served, the application automatically logs the following message: &lt;code&gt;* Request Started [&amp;lt; Session Id not available &amp;gt;][UUID session Id]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
The request's endpoint handler can optionally log its own messages.
&lt;/li&gt;
&lt;li&gt;
After a request has been served, the application automatically logs the following message: &lt;code&gt;* Request Finished [&amp;lt; Session Id not available &amp;gt;][UUID session Id]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will refer to the two messages &lt;code&gt;* Request Started ...&lt;/code&gt; and &lt;code&gt;* Request Finished ...&lt;/code&gt; as a &lt;strong&gt;&lt;em&gt;marker pair&lt;/em&gt;&lt;/strong&gt; throughout the rest of this post.&lt;/p&gt;

&lt;p&gt;As you may recall from the &lt;a href="https://behainguyen.wordpress.com/2024/05/21/python-fastapi-implementing-persistent-stateful-http-sessions-with-redis-session-middleware-and-extending-oauth2passwordbearer-for-oauth2-security/" title="Python FastAPI: Implementing Persistent Stateful HTTP Sessions with Redis Session Middleware and Extending OAuth2PasswordBearer for OAuth2 Security"&gt;third post&lt;/a&gt;, we implemented the &lt;code&gt;UUID session Id&lt;/code&gt;. An &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated session"&gt;&lt;code&gt;authenticated session&lt;/code&gt;&lt;/a&gt; is uniquely identified by a &lt;code&gt;UUID session Id&lt;/code&gt;. Thus, a &lt;code&gt;UUID session Id&lt;/code&gt; will be logged with the marker pair if one is available, otherwise the text &lt;code&gt;&amp;lt; Session Id not available &amp;gt;&lt;/code&gt; will be logged. Please see the following illustrative examples.&lt;/p&gt;

&lt;p&gt;&lt;a id="logging-func-without-uuid"&gt;&lt;/a&gt;&lt;br&gt;
⓵ Logging where a &lt;code&gt;UUID session Id&lt;/code&gt; is not yet available, i.e., the request is from an &lt;code&gt;&lt;strong&gt;un&lt;/strong&gt; &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated session"&gt;authenticated session&lt;/a&gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO:     ... * Request Started &amp;lt; Session Id not available &amp;gt;
INFO:     ... Path: http://192.168.0.16:5000/auth/login?state=0
DEBUG:    ... Attempt to deliver the login page.
DEBUG:    ... Delivering the login page.
INFO:     ... * Request Finished &amp;lt; Session Id not available &amp;gt;
INFO:     ... 192.168.0.2:61019 - "GET /auth/login?state=0 HTTP/1.1" 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="logging-func-with-uuid"&gt;&lt;/a&gt;&lt;br&gt;
⓶ Logging with a &lt;code&gt;UUID session Id&lt;/code&gt;, i.e., the request is from an &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated session"&gt;&lt;code&gt;authenticated session&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO:     ... * Request Started f9b96bcdab8b5153c44ca02e0a489c7d
INFO:     ... Path: http://192.168.0.16:5000/admin/me
DEBUG:    ... Returning a valid logged-in user.
INFO:     ... * Request Finished f9b96bcdab8b5153c44ca02e0a489c7d
INFO:     ... 192.168.0.2:61016 - "GET /admin/me HTTP/1.1" 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💥 &lt;strong&gt;Please note the last line&lt;/strong&gt; in each of the above two examples. It originates from the &lt;code&gt;httptools_impl.py&lt;/code&gt; module, specifically the method &lt;code&gt;send&lt;/code&gt; on line &lt;code&gt;466&lt;/code&gt;. It is outside of the marker pair, and I did not attempt to have it logged within the marker pair. I believe that, together with the &lt;code&gt;thread Id&lt;/code&gt; (not shown) and the path, we can visually trace the originating request.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout"&gt;&lt;/a&gt;&lt;br&gt;
❸ The changes to the code are quite minimal. Essentially, we’re adding a new &lt;a href="https://yaml.org/" title="YAML"&gt;YAML&lt;/a&gt; logging configuration file, a new Python module, and incorporating logging into other modules. The updated structure of the project is outlined below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-- Please note,&lt;/strong&gt; those marked with &lt;span&gt;★&lt;/span&gt; are updated, and those marked with &lt;span&gt;☆&lt;/span&gt; are new.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/behai/fastapi_learning/
.
├── logger_config.yaml ☆ 
├── main.py ★
├── pyproject.toml ★ 
├── pytest.ini
├── README.md ★
├── src
│ └── fastapi_learning
│     ├── common
│     │ ├── consts.py
│     │ └── queue_logging.py ☆
│     ├── controllers
│     │ ├── admin.py ★
│     │ ├── auth.py ★
│     │ └── __init__.py
│     ├── __init__.py
│     ├── models
│     │ └── employees.py
│     ├── static
│     │ └── styles.css
│     └── templates
│         ├── admin
│         │ └── me.html
│         ├── auth
│         │ ├── home.html
│         │ └── login.html
│         └── base.html
└── tests
    ├── conftest.py
    ├── __init__.py
    ├── integration
    │ ├── test_admin_itgt.py
    │ ├── test_api_itgt.py
    │ └── test_auth_itgt.py
    └── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="the-implementation"&gt;&lt;/a&gt;&lt;br&gt;
❹ In this section, we discuss the implementation of &lt;code&gt;non-blocking logging&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have used &lt;a href="https://docs.python.org/3/library/logging.handlers.html#rotatingfilehandler" title="RotatingFileHandler"&gt;RotatingFileHandler&lt;/a&gt; for logging via an external &lt;a href="https://yaml.org/" title="YAML"&gt;YAML&lt;/a&gt; configuration file before. I appreciate this approach because we can adjust the logging by tweaking the configuration file without having to refactor the code. I would like to adopt the same approach for this implementation. We will first examine the YAML configuration file and then the associated Python code.&lt;/p&gt;

&lt;p&gt;&lt;a id="impl-logger-config"&gt;&lt;/a&gt;&lt;br&gt;
⓵ The full content of the YAML configuration file, &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/logger_config.yaml" title="The YAML logger configuration file"&gt;&lt;code&gt;logger_config.yaml&lt;/code&gt;&lt;/a&gt;, is listed below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;disable_existing_loggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;False&lt;/span&gt;
&lt;span class="na"&gt;formatters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;()&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uvicorn.logging.DefaultFormatter&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{levelprefix}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;[{asctime}]&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{thread}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{filename}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{funcName}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{lineno}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{message}'&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{'&lt;/span&gt;
    &lt;span class="na"&gt;datefmt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%d-%m-%Y&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;%H:%M:%S'&lt;/span&gt;
  &lt;span class="na"&gt;colours_removed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;()&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uvicorn.logging.DefaultFormatter&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{levelname}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;[{asctime}]&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{thread}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{filename}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{funcName}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{lineno}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{message}'&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{'&lt;/span&gt;
&lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;console&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;formatter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logging.StreamHandler&lt;/span&gt;
    &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ext://sys.stdout&lt;/span&gt;
  &lt;span class="na"&gt;rotating_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;formatter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;colours_removed&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logging.handlers.RotatingFileHandler&lt;/span&gt;
    &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./logs/fastapi_learning.log&lt;/span&gt;
    &lt;span class="na"&gt;maxBytes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4096&lt;/span&gt;
    &lt;span class="na"&gt;backupCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;  &lt;span class="c1"&gt;# Keep 5 old log files    &lt;/span&gt;
    &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;utf-8&lt;/span&gt;
  &lt;span class="na"&gt;queue_rotating_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logging.handlers.QueueHandler&lt;/span&gt;
    &lt;span class="c1"&gt;# queue: fastapi_learning.common.queue_logging.queue_factory&lt;/span&gt;
    &lt;span class="c1"&gt;# listener: fastapi_learning.common.queue_logging.CustomListener&lt;/span&gt;
    &lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rotating_file&lt;/span&gt;
&lt;span class="na"&gt;loggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# uvicorn.error is also valid. It is the equivalence of root.&lt;/span&gt;
  &lt;span class="c1"&gt;# uvicorn.error: &lt;/span&gt;
  &lt;span class="c1"&gt;#  level: INFO  &lt;/span&gt;
  &lt;span class="c1"&gt;#  handlers:&lt;/span&gt;
  &lt;span class="c1"&gt;#    - console&lt;/span&gt;
  &lt;span class="c1"&gt;#    - qhand&lt;/span&gt;
  &lt;span class="c1"&gt;#  propagate: no&lt;/span&gt;
  &lt;span class="na"&gt;fastapi_learning.debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEBUG&lt;/span&gt;
    &lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;console&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;queue_rotating_file&lt;/span&gt;
    &lt;span class="na"&gt;propagate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
&lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;INFO&lt;/span&gt;
  &lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;console&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;queue_rotating_file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All aspects of the above configuration file are documented in the &lt;a href="https://docs.python.org/3/library/logging.html" title="logging — Logging facility for Python"&gt;official Python logging documentation&lt;/a&gt;. We will discuss some implementation details below.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
For completeness, we log to both the console (&lt;code&gt;stdout&lt;/code&gt;) and log files. The &lt;code&gt;default&lt;/code&gt; formatter is for the console, where we retain the default colours. The &lt;code&gt;colours_removed&lt;/code&gt; formatter is for the log file. Without removing the text colours, log files will have control codes written in place of text colours. For example, &lt;code&gt;^[[32mINFO^[[0m:&lt;/code&gt;, where &lt;code&gt;^[&lt;/code&gt; denotes the &lt;code&gt;ESC&lt;/code&gt; control character, whose ASCII code is &lt;code&gt;27&lt;/code&gt;. Please refer to &lt;a href="https://behainguyen.wordpress.com/wp-content/uploads/2024/07/114-01.png" title="Control colour codes print out illustrations"&gt;this screenshot&lt;/a&gt; for the exact printout. Please further note the following:
&lt;ul&gt;
&lt;li&gt;
Log attributes such as &lt;code&gt;levelname&lt;/code&gt;, &lt;code&gt;thread&lt;/code&gt; etc., are covered by the official documentation on &lt;a href="https://docs.python.org/3/library/logging.html#logrecord-attributes" title="LogRecord attributes"&gt;LogRecord attributes&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
I &lt;strong&gt;cannot find official documentation&lt;/strong&gt; for &lt;code&gt;levelprefix&lt;/code&gt;. However, it is mentioned and used in discussions about Python logging across the internet. I have observed that &lt;code&gt;levelprefix&lt;/code&gt; and &lt;code&gt;levelname&lt;/code&gt; both print out the text logging level such as &lt;code&gt;DEBUG&lt;/code&gt;, &lt;code&gt;INFO&lt;/code&gt; etc.; &lt;code&gt;levelprefix&lt;/code&gt; prints out with colours while &lt;code&gt;levelname&lt;/code&gt; does not.
&lt;/li&gt;
&lt;li&gt;
For the rather unusual entry &lt;code&gt;(): uvicorn.logging.DefaultFormatter&lt;/code&gt;, please refer to the official documentation on &lt;a href="https://docs.python.org/3/library/logging.config.html#logging-config-dict-userdef" title="User-defined objects"&gt;User-defined objects&lt;/a&gt;. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;a id="impl-logger-config-rotating-file"&gt;&lt;/a&gt;
&lt;li&gt;
For the &lt;code&gt;rotating_file&lt;/code&gt; handler, please note:
&lt;ul&gt;
&lt;li&gt;
💥 For the &lt;code&gt;filename: ./logs/fastapi_learning.log&lt;/code&gt; property, we configured the log files to be written to the &lt;code&gt;./logs&lt;/code&gt; sub-directory. This is not something supported out of the box. We will discuss this property in a later section.
&lt;/li&gt;
&lt;li&gt;
The values of &lt;code&gt;maxBytes&lt;/code&gt; and &lt;code&gt;backupCount&lt;/code&gt; are deliberately set low for debugging purposes.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
For the &lt;code&gt;queue_rotating_file&lt;/code&gt; handler, please note:
&lt;ul&gt;
&lt;a id="impl-logger-config-queue-listener"&gt;&lt;/a&gt;
&lt;li&gt;
&lt;p&gt;
The YAML configuration is a copy of the snippet from the Python documentation on &lt;a href="https://docs.python.org/3/library/logging.config.html#configuring-queuehandler-and-queuelistener" title="Configuring QueueHandler and QueueListener"&gt;Configuring QueueHandler and QueueListener&lt;/a&gt;. We left both the &lt;code&gt;queue&lt;/code&gt; key and the &lt;code&gt;listener&lt;/code&gt; out. We use the standard implementations as documented. 
&lt;/p&gt;
&lt;p&gt;
💥 It is the application’s responsibility to start and stop any &lt;a href="https://docs.python.org/3/library/logging.handlers.html#logging.handlers.QueueListener" title="QueueListener"&gt;QueueListener&lt;/a&gt; instances in use. We will discuss this in a later section.
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;
The &lt;a href="https://docs.python.org/3/library/logging.handlers.html#logging.handlers.QueueHandler" title="QueueHandler"&gt;QueueHandler&lt;/a&gt; is a special case: it has its own handlers, in this case, it is the &lt;code&gt;rotating_file&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
It is worth noting that the structure of the &lt;code&gt;queue_rotating_file&lt;/code&gt; is very similar to the non-blocking logging example presented in an earlier section.
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;a id="impl-logger-config-logger"&gt;&lt;/a&gt;
&lt;li&gt;
We configure only one logger: &lt;code&gt;fastapi_learning.debug&lt;/code&gt;. Its log messages are consumed by both the console and the &lt;code&gt;queue_rotating_file&lt;/code&gt; handlers.
&lt;/li&gt;
&lt;/ol&gt;   

&lt;p&gt;&lt;a id="impl-python-code"&gt;&lt;/a&gt;&lt;br&gt;
⓶ We will now discuss the new Python module, &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/src/fastapi_learning/common/queue_logging.py" title="Logging management common/queue_logging.py"&gt;&lt;code&gt;common/queue_logging.py&lt;/code&gt;&lt;/a&gt;, which works in conjunction with the above YAML configuration file. This is a straightforward module, comprising less than 90 lines.&lt;/p&gt;

&lt;ol&gt;
&lt;a id="impl-python-logs-sub-dir"&gt;&lt;/a&gt;
&lt;li&gt;
&lt;p&gt;
In the &lt;code&gt;rotating_file&lt;/code&gt; handler section, we mentioned that the &lt;code&gt;./logs&lt;/code&gt; sub-directory is not supported out of the box. As it is configured, it is a sub-directory immediately under the directory where the &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/main.py" title="fastapi_learning application entry script"&gt;&lt;code&gt;main.py&lt;/code&gt;&lt;/a&gt; module resides.
&lt;/p&gt;

&lt;p&gt;
We need to manage this sub-directory ourselves: we must ensure that this sub-directory exists before the YAML configuration file is loaded, otherwise Python will raise an exception. 
&lt;/p&gt;

&lt;p&gt;
💥 Therefore, passing the YAML configuration file in the command line, such as &lt;code&gt;uvicorn main:app --log-config=logger_config.yaml&lt;/code&gt;, is not possible, or more accurately, &lt;strong&gt;I don’t know how to enable that&lt;/strong&gt;. I tried and failed to run my code before the configuration file was loaded.
&lt;/p&gt;

&lt;p&gt;
The next best option is to load the configuration file ourselves: we have full control. Please refer to the function &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/src/fastapi_learning/common/queue_logging.py#L57-L65" title="prepare_logging_and_start_listeners"&gt;&lt;code&gt;prepare_logging_and_start_listeners&lt;/code&gt;, lines 57-65&lt;/a&gt;, in the new &lt;code&gt;common/queue_logging.py&lt;/code&gt; module. The actual code consists of only 4 lines:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Always create the &lt;code&gt;./logs&lt;/code&gt; sub-directory.
&lt;/li&gt;
&lt;li&gt;
We then load the configuration file and pass it to &lt;a href="https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig" title="logging.config.dictConfig"&gt;logging.config.dictConfig&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
💥 &lt;strong&gt;Please note that&lt;/strong&gt;, due to the above implementation, the loggers have not been configured yet when the application starts up. The startup messages below are not written to the current log file. The default existing loggers are still in use at this point.
&lt;/p&gt;

&lt;pre&gt;
INFO:     Started server process [29204]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
&lt;/pre&gt;


&lt;/li&gt;

&lt;p&gt;&lt;a id="impl-python-listeners"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;li&gt;

&lt;p&gt;
In a previous section, we mentioned that it is the application’s responsibility to start and stop any &lt;a href="https://docs.python.org/3/library/logging.handlers.html#logging.handlers.QueueListener" title="QueueListener"&gt;QueueListener&lt;/a&gt; instances. The last part of the function &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/src/fastapi_learning/common/queue_logging.py#L67-L69" title="prepare_logging_and_start_listeners"&gt;&lt;code&gt;prepare_logging_and_start_listeners&lt;/code&gt;&lt;/a&gt;, lines &lt;code&gt;67&lt;/code&gt; to &lt;code&gt;69&lt;/code&gt;, implements the code to get the listeners to start listening. 
&lt;/p&gt;

&lt;p&gt;
We retrieve all listener instances and start each one. The private helper function &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/src/fastapi_learning/common/queue_logging.py#L39-L47" title="__retrieve_queue_listeners"&gt;&lt;code&gt;__retrieve_queue_listeners&lt;/code&gt;&lt;/a&gt; should be self-explanatory. 
&lt;/p&gt;

&lt;p&gt;
We currently have only one listener instance, but in the future, we might configure more, such as for sending out emails. In such a case, we would need to update only the private function &lt;code&gt;__retrieve_queue_listeners&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Before the application shuts down, it should call &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/src/fastapi_learning/common/queue_logging.py#L79-L87" title="logging_stop_listeners"&gt;&lt;code&gt;logging_stop_listeners&lt;/code&gt;&lt;/a&gt; to get the listeners to stop listening.
&lt;/p&gt;


&lt;/li&gt;

&lt;p&gt;&lt;a id="impl-python-request-markers"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;li&gt;And finally, the &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/src/fastapi_learning/common/queue_logging.py#L12-L37" title="RequestLoggingMiddleware class"&gt;&lt;code&gt;RequestLoggingMiddleware&lt;/code&gt;&lt;/a&gt; class implements the request logging marker pair that was mentioned in an in an earlier section.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="impl-python-main-mod"&gt;&lt;/a&gt;&lt;br&gt;
⓷ The changes in &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/main.py" title="fastapi_learning application entry script"&gt;&lt;code&gt;main.py&lt;/code&gt;&lt;/a&gt; are straightforward and should be self-explanatory.&lt;/p&gt;

&lt;p&gt;For information on the new &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/main.py#L36-L45" title="fastapi_learning application entry script"&gt;&lt;code&gt;lifespan&lt;/code&gt;&lt;/a&gt; function, please refer to the official FastAPI documentation on &lt;a href="https://fastapi.tiangolo.com/ru/advanced/events/#lifespan" title="Lifespan"&gt;Lifespan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="impl-python-use-logging"&gt;&lt;/a&gt;&lt;br&gt;
⓸ Having implemented all of the above, we are finally able to incorporate logging into the methods in the two modules, &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/src/fastapi_learning/controllers/auth.py" title="controllers/auth.py"&gt;&lt;code&gt;controllers/auth.py&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/68890892801be114aab17cf656d8ebbd6eca06b0/src/fastapi_learning/controllers/admin.py" title="controllers/admin.py"&gt;&lt;code&gt;controllers/admin.py&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="documentation"&gt;&lt;/a&gt;&lt;br&gt;
❺ Some of the referenced official documentation has already been mentioned throughout the discussion. However, I believe it is rather essential, so I would like to reiterate it in this separate section. I have personally read through all the Python documentation on logging. They include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.python.org/3/library/logging.html" title="logging — Logging facility for Python"&gt;logging — Logging facility for Python&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.python.org/3/howto/logging.html#logging-basic-tutorial" title="Basic Logging Tutorial"&gt;Basic Logging Tutorial&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.python.org/3/howto/logging.html#logging-advanced-tutorial" title="Advanced Logging Tutorial"&gt;Advanced Logging Tutorial&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.python.org/3/howto/logging-cookbook.html#logging-cookbook" title="Logging Cookbook"&gt;Logging Cookbook&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The last one is particularly interesting. Python logging is indeed a powerful library.&lt;/p&gt;

&lt;p&gt;&lt;a id="concluding-remarks"&gt;&lt;/a&gt;&lt;br&gt;
❻ When I first started this logging process, I thought it was going to be simple. However, it took a bit longer than I anticipated. I encountered some problems, but I managed to find solutions. The code presented in this post has gone through several refactorings. This is the first time I have explored Python logging in detail. I learned a lot during the writing of this post. There is room for improvement, but overall, I think the implementation is acceptable.&lt;/p&gt;

&lt;p&gt;Thank you for reading. I hope you find the information in this post useful. Stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.python.org/downloads/release/python-3124/"&gt;https://www.python.org/downloads/release/python-3124/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fastapi.tiangolo.com/"&gt;https://fastapi.tiangolo.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://1000logos.net/download-image/"&gt;https://1000logos.net/download-image/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
🐍 &lt;a href="https://github.com/behai-nguyen/fastapi_learning" title="Index of the Complete Series"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

</description>
      <category>python</category>
      <category>logging</category>
      <category>non</category>
      <category>blocking</category>
    </item>
    <item>
      <title>Python FastAPI: Integrating OAuth2 Security with the Application's Own Authentication Process</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Tue, 14 May 2024 00:59:49 +0000</pubDate>
      <link>https://forem.com/behainguyen/python-fastapi-integrating-oauth2-security-with-the-applications-own-authentication-process-335i</link>
      <guid>https://forem.com/behainguyen/python-fastapi-integrating-oauth2-security-with-the-applications-own-authentication-process-335i</guid>
      <description>&lt;p&gt;&lt;em&gt;In the &lt;a href="https://behainguyen.wordpress.com/2024/05/11/python-fastapi-some-further-studies-on-oauth2-security/" title="First post" rel="noopener noreferrer"&gt;first post&lt;/a&gt;, we explore some aspects of &lt;code&gt;OAuth2&lt;/code&gt; authentication, focusing on the &lt;code&gt;/token&lt;/code&gt; path as illustrated in an example from the &lt;a href="https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/" title="Simple OAuth2 with Password and Bearer" rel="noopener noreferrer"&gt;Simple OAuth2 with Password and Bearer&lt;/a&gt; section of the &lt;a href="https://fastapi.tiangolo.com/tutorial/security/" title="Tutorial - User Guide Security" rel="noopener noreferrer"&gt;Tutorial - User Guide Security&lt;/a&gt;. In this subsequent post, we implement our own custom preliminary login process, leveraging the &lt;code&gt;/token&lt;/code&gt; path. This means that both the Swagger UI &lt;code&gt;Authorize&lt;/code&gt; button and our application's login button utilise the same server code.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
🐍 &lt;a href="https://github.com/behai-nguyen/fastapi_learning" title="Index of the Complete Series" rel="noopener noreferrer"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

&lt;p&gt;🚀 &lt;strong&gt;Please note,&lt;/strong&gt; complete code for this post can be downloaded from GitHub with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.2.0 https://github.com/behai-nguyen/fastapi_learning.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During my research on &lt;code&gt;“FastAPI application custom login process”&lt;/code&gt;, I've encountered implementations where there are two endpoints for handling authentication: the &lt;code&gt;/token&lt;/code&gt; endpoint, as discussed in the &lt;a href="https://fastapi.tiangolo.com/tutorial/security/" title="Tutorial - User Guide Security" rel="noopener noreferrer"&gt;Tutorial - User Guide Security&lt;/a&gt; section, and the application's own login endpoint. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💥 This approach doesn't seem right to me. In my view, the &lt;code&gt;/token&lt;/code&gt; endpoint should serve as the application's login endpoint as well.&lt;/strong&gt; In this post, we introduce a preliminary custom login process with the endpoint being the &lt;code&gt;/token&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;p&gt;The code developed in this post maintains the one-module application structure from the original example. However, we've added a login HTML page and organised the project directory structure to prepare for further code changes as we progress. The updated project layout is listed below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-- Please note,&lt;/strong&gt; those marked with &lt;span&gt;★&lt;/span&gt; are updated, and those marked with &lt;span&gt;☆&lt;/span&gt; are new.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout-chart"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/behai/fastapi_learning/
├── main.py ★
├── pyproject.toml
├── README.md ★
└── src ☆
    └── fastapi_learning
        ├── static
        │ └── styles.css
        └── templates
            ├── auth
            │ └── login.html
            └── base.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Changes to &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/main.py" title="Application module main.py" rel="noopener noreferrer"&gt;&lt;code&gt;main.py&lt;/code&gt;&lt;/a&gt; include the following:&lt;/p&gt;

&lt;p&gt;⓵ &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/main.py#L21-L23" title="Lines 21 to 23" rel="noopener noreferrer"&gt;Lines 21 to 23&lt;/a&gt;: Added required imports to support HTML output.&lt;/p&gt;

&lt;p&gt;⓶ &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/main.py#L25-L45" title="Lines 25 to 45" rel="noopener noreferrer"&gt;Lines 25 to 45&lt;/a&gt;: Completely refactored the &lt;code&gt;fake_users_db&lt;/code&gt; database to slowly match the &lt;a href="https://github.com/datacharmer/test_db" title="test employees MySQL database by the Oracle Corporation" rel="noopener noreferrer"&gt;test &lt;code&gt;employees&lt;/code&gt; MySQL database by Oracle Corporation&lt;/a&gt;, as utilised in the &lt;a href="https://github.com/behai-nguyen/bh_database/tree/main/examples/fastapir" title="SQLAlchemy database wrapper component FastAPI example" rel="noopener noreferrer"&gt;SQLAlchemy database wrapper component FastAPI example&lt;/a&gt;, and &lt;a href="https://behainguyen.wordpress.com/2024/01/14/rust-actix-web-endpoints-which-accept-both-application-x-www-form-urlencoded-and-application-json-content-types/#step-two-update-employees-table" title="Update the employees table, adding new fields email and password" rel="noopener noreferrer"&gt;Update the &lt;code&gt;employees&lt;/code&gt; table, adding new fields &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;⓷ &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/main.py#L49-L50" title="Lines 49 to 50" rel="noopener noreferrer"&gt;Lines 49 to 50&lt;/a&gt;: Added initialisation code to prepare support for HTML template processing.&lt;/p&gt;

&lt;p&gt;⓸ &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/main.py#L66-L72" title="Lines 66 to 72" rel="noopener noreferrer"&gt;Lines 66 to 72&lt;/a&gt;: Refactored models to align with the changes in &lt;code&gt;fake_users_db&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;⓹ &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/main.py#L95-L100" title="Lines 95 to 100" rel="noopener noreferrer"&gt;Lines 95 to 100&lt;/a&gt;: Refactored the &lt;code&gt;get_current_active_user(...)&lt;/code&gt; method to cease checking the &lt;code&gt;disabled&lt;/code&gt; attribute of the &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/main.py#L66" title="The User model" rel="noopener noreferrer"&gt;&lt;code&gt;User&lt;/code&gt; model&lt;/a&gt;, as this attribute has been removed from the model.&lt;/p&gt;

&lt;p&gt;⓺ &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/main.py#L120-L123" title="Lines 120 to 123" rel="noopener noreferrer"&gt;Lines 120 to 123&lt;/a&gt;: Implemented the new &lt;code&gt;/login&lt;/code&gt; endpoint, which simply returns the login HTML page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚀 Note that&lt;/strong&gt; the endpoint code for the &lt;code&gt;/token&lt;/code&gt; path, specifically the method &lt;code&gt;async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):&lt;/code&gt;, &lt;strong&gt;remains unchanged.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💥 Regarding the &lt;a href="https://github.com/behai-nguyen/fastapi_learning/blob/3fd102080ba8bef48b3eee1d14f4a066e89909f7/src/fastapi_learning/templates/auth/login.html" title="Login HTML page" rel="noopener noreferrer"&gt;&lt;code&gt;HTML login page&lt;/code&gt;&lt;/a&gt;, please take note of the following points:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/token"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"loginForm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h3 mb-3 fw-normal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Please login&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

        {% if message is defined %}
            &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{{ message }}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        {% endif %}

        &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email address:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"name@example.com"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Password:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
... 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⓵ The action of the login form is directed to the &lt;code&gt;/token&lt;/code&gt; path. In other words, when the login form is submitted, it sends a &lt;code&gt;POST&lt;/code&gt; login request to the same endpoint used by the &lt;code&gt;Authorize&lt;/code&gt; button on the Swagger UI page.&lt;/p&gt;

&lt;p&gt;⓶ The names of the two login fields are &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;. This requirement is specified in the tutorial in the section titled &lt;a href="https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/" title="Simple OAuth2 with Password and Bearer" rel="noopener noreferrer"&gt;Simple OAuth2 with Password and Bearer&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OAuth2 specifies that when using the "password flow" (that we are using) the client/user must send a &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; fields as form data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;🚀 Our application's login process now shares the same server code as the Swagger UI login process.&lt;/strong&gt; We have two separate “clients”:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;http://0.0.0.0:port/docs&lt;/code&gt;: The Swagger UI client page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http://0.0.0.0:port/login&lt;/code&gt;: Our own custom login page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On Ubuntu 22.10, run the application with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(venv) ...:~/fastapi_learning$ venv/bin/uvicorn main:app --host 0.0.0.0 --port 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When accessing the Swagger UI page on Windows 10 at &lt;a href="http://192.168.0.16:5000/docs" title="The Swagger UI client" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="http://192.168.0.16:5000/docs" rel="noopener noreferrer"&gt;http://192.168.0.16:5000/docs&lt;/a&gt;, we encounter a familiar page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-01.png" alt="106-01.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;GET&lt;/code&gt; &lt;code&gt;/login&lt;/code&gt; path should simply return the login HTML page, while the remaining paths should function as discussed in the &lt;a href="https://behainguyen.wordpress.com/2024/05/11/python-fastapi-some-further-studies-on-oauth2-security/" title="First post" rel="noopener noreferrer"&gt;first post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When accessing the application's login page on Windows 10 at &lt;a href="http://192.168.0.16:5000/login" title="The application login page" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="http://192.168.0.16:5000/login" rel="noopener noreferrer"&gt;http://192.168.0.16:5000/login&lt;/a&gt;, we are presented with our custom login page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-02.png" alt="106-02.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon logging in using one of the following credentials: &lt;code&gt;&lt;a href="mailto:behai_nguyen@hotmail.com"&gt;behai_nguyen@hotmail.com&lt;/a&gt;&lt;/code&gt;/&lt;code&gt;password&lt;/code&gt; or &lt;code&gt;&lt;a href="mailto:pranav.furedi.10198@gmail.com"&gt;pranav.furedi.10198@gmail.com&lt;/a&gt;&lt;/code&gt;/&lt;code&gt;password&lt;/code&gt;, we should receive a successful JSON response, as depicted in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-03.png" alt="106-03.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When attempting to log in using an invalid credential, we should receive an HTTP &lt;code&gt;400&lt;/code&gt; response, which indeed occurs, as seen in the screenshots below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-04a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-04a.png" alt="106-04a.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-04b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-04b.png" alt="106-04b.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-04c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F05%2F106-04c.png" alt="106-04c.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the current implementation, the login process is incomplete, but it serves as an appropriate preliminary step nonetheless. We will conclude this post here as I don't want to make it too long... In the next post or so, we will implement stateful sessions and extend &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt; to maintain &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated sessions" rel="noopener noreferrer"&gt;&lt;code&gt;authenticated sessions&lt;/code&gt;&lt;/a&gt;. This means that after a successful login, users can access protected application routes until they choose to log out.&lt;/p&gt;

&lt;p&gt;Thank you for reading. I hope you find the information in this post useful. Stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper" rel="noopener noreferrer"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/" rel="noopener noreferrer"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;https://fastapi.tiangolo.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
🐍 &lt;a href="https://github.com/behai-nguyen/fastapi_learning" title="Index of the Complete Series" rel="noopener noreferrer"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>oauth2</category>
      <category>security</category>
    </item>
    <item>
      <title>Python: A SQLAlchemy Wrapper Component That Works With Both Flask and FastAPI Frameworks</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Sat, 04 May 2024 03:46:58 +0000</pubDate>
      <link>https://forem.com/behainguyen/python-a-sqlalchemy-wrapper-component-that-works-with-both-flask-and-fastapi-frameworks-2bb9</link>
      <guid>https://forem.com/behainguyen/python-a-sqlalchemy-wrapper-component-that-works-with-both-flask-and-fastapi-frameworks-2bb9</guid>
      <description>&lt;p&gt;&lt;em&gt;In late 2022, I developed a &lt;a href="https://pypi.org/project/bh-database/" title="bh-database"&gt;database wrapper component&lt;/a&gt; for &lt;a href="https://www.sqlalchemy.org/" title="SQLAlchemy"&gt;SQLAlchemy&lt;/a&gt;. Initially designed for use with the &lt;a href="https://flask.palletsprojects.com/en/3.0.x/" title="Flask"&gt;Flask&lt;/a&gt; framework, it was discovered that this component also seamlessly integrates with the &lt;a href="https://fastapi.tiangolo.com/" title="FastAPI"&gt;FastAPI&lt;/a&gt; framework. In this post, I will describe this component and provide examples of how it is used within both frameworks.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This component is based on a concept I previously implemented using Delphi and later PHP. Essentially, it consists of base classes representing database tables, equipped with the ability to interact with databases and implement generic functionalities for CRUD operations.&lt;/p&gt;

&lt;p&gt;While learning the &lt;a href="https://flask.palletsprojects.com/en/3.0.x/" title="Flask"&gt;Flask&lt;/a&gt; framework, I found the conventional approach of accessing the database layer through the Flask application instance uncomfortable. In my previous projects, the database layer has always remained independent of other layers. The business layer ensures data validity and then delegates CRUD operations to the database layer. The UI layer, whether a desktop application or web client, communicates solely with the business layer, never directly accessing the database layer.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://www.sqlalchemy.org/" title="SQLAlchemy"&gt;SQLAlchemy&lt;/a&gt;, &lt;code&gt;model&lt;/code&gt;s representing database tables typically subclass &lt;a href="https://docs.sqlalchemy.org/en/20/orm/mapping_api.html#sqlalchemy.orm.DeclarativeBase" title="sqlalchemy.orm.DeclarativeBase"&gt;sqlalchemy.orm.DeclarativeBase&lt;/a&gt; (this class supersedes the &lt;a href="https://docs.sqlalchemy.org/en/20/orm/mapping_api.html#sqlalchemy.orm.declarative_base" title="sqlalchemy.orm.declarative_base"&gt;sqlalchemy.orm.declarative_base&lt;/a&gt; function). Accordingly, the abstract base class in this &lt;a href="https://pypi.org/project/bh-database/" title="bh-database"&gt;database wrapper component&lt;/a&gt; is a &lt;code&gt;sqlalchemy.orm.DeclarativeBase&lt;/code&gt; subclass, accompanied by another custom base class providing additional dunder methods.&lt;/p&gt;

&lt;p&gt;Further subclasses of this abstract base class implement additional functionalities. Application models inherit from either the &lt;a href="https://bh-database.readthedocs.io/en/latest/base_table.html#bh_database.base_table.ReadOnlyTable" title="ReadOnlyTable"&gt;ReadOnlyTable&lt;/a&gt; or the &lt;a href="https://bh-database.readthedocs.io/en/latest/base_table.html#bh_database.base_table.WriteCapableTable" title="WriteCapableTable"&gt;WriteCapableTable&lt;/a&gt; base classes.&lt;/p&gt;

&lt;p&gt;Application &lt;code&gt;model&lt;/code&gt;s are required to implement their own specific database reading methods. For example, selecting all customers with the surname &lt;code&gt;Nguyễn&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://bh-database.readthedocs.io/en/latest/core.html#bh_database.core.Database" title="Database"&gt;Database&lt;/a&gt; class is responsible for establishing connections to the target database. Once the database connection is established, application models can interact with the target database.&lt;/p&gt;

&lt;p&gt;🚀 The full documentation can be found at &lt;a href="https://bh-database.readthedocs.io/en/latest/" title="bh-database API documentation"&gt;&lt;/a&gt;&lt;a href="https://bh-database.readthedocs.io/en/latest/"&gt;https://bh-database.readthedocs.io/en/latest/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, we will explore some examples. &lt;strong&gt;&lt;em&gt;💥 The first two are simple, single-module web server applications where the web layer directly accesses the database layer.&lt;/em&gt; Although not ideal, it simplifies usage illustration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The latter two examples include complete business layers, where submitted data is validated before being passed to the database layer for CRUD operations.&lt;/p&gt;

&lt;p&gt;❶ &lt;code&gt;example.py&lt;/code&gt;: A Simple Single-Module &lt;a href="https://flask.palletsprojects.com/en/3.0.x/" title="Flask"&gt;Flask&lt;/a&gt; Application.&lt;/p&gt;

&lt;p&gt;● Windows 10: &lt;code&gt;F:\bh_database\examples\flaskr\example.py&lt;/code&gt;&lt;br&gt;&lt;br&gt;
● Ubuntu 22.10: &lt;code&gt;/home/behai/bh_database/examples/flaskr/example.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bh_database.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bh_database.base_table&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WriteCapableTable&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bh_apistatus.result_status&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ResultStatus&lt;/span&gt;

&lt;span class="n"&gt;SQLALCHEMY_DATABASE_SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;SQLALCHEMY_DATABASE_URI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mysql+mysqlconnector://root:pcb.2176310315865259@localhost:3306/employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;# Enable this for PostgreSQL.
# SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://postgres:pcb.2176310315865259@localhost/employees'
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WriteCapableTable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;__tablename__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="n"&gt;emp_no&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;birth_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hire_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select_by_partial_last_name_and_first_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ResultStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_stored_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Construct the core application.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance_relative_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;init_extensions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;init_app_database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;init_extensions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strict_slashes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;init_app_database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;    
    &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLALCHEMY_DATABASE_URI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SQLALCHEMY_DATABASE_SCHEMA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/employees/search/&amp;lt;last_name&amp;gt;/&amp;lt;first_name&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_employees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; last_name and first_name are partial using %.

    An example of a valid route: http://localhost:5000/employees/search/%nas%/%An
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Employees&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; \
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_by_partial_last_name_and_first_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
   &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To execute the &lt;code&gt;example.py&lt;/code&gt; application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;▶️&amp;lt;code&amp;gt;Windows 10:&amp;lt;/code&amp;gt; (venv) F:\bh_database\examples\flaskr&amp;gt;venv\Scripts\flask.exe --app example run --host 0.0.0.0 --port 5000
▶️&amp;lt;code&amp;gt;Ubuntu 22.10:&amp;lt;/code&amp;gt; (venv) behai@hp-pavilion-15:~/bh_database/examples/flaskr$ venv/bin/flask --app example run --host 0.0.0.0 --port 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing the &lt;code&gt;example.py&lt;/code&gt; application running locally from Windows 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5000/employees/search/%nas%/%An
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing the &lt;code&gt;example.py&lt;/code&gt; application running on Ubuntu 22.10 from Windows 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://192.168.0.16:5000/employees/search/%nas%/%An
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❷ &lt;code&gt;example.py&lt;/code&gt;: A Simple Single-Module &lt;a href="https://fastapi.tiangolo.com/" title="FastAPI"&gt;FastAPI&lt;/a&gt; Application.&lt;/p&gt;

&lt;p&gt;● Windows 10: &lt;code&gt;F:\bh_database\examples\fastapir\example.py&lt;/code&gt;&lt;br&gt;&lt;br&gt;
● Ubuntu 22.10: &lt;code&gt;/home/behai/bh_database/examples/fastapir/example.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JSONResponse&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bh_database.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bh_database.base_table&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WriteCapableTable&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bh_apistatus.result_status&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ResultStatus&lt;/span&gt;

&lt;span class="n"&gt;SQLALCHEMY_DATABASE_SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;SQLALCHEMY_DATABASE_URI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mysql+mysqlconnector://root:pcb.2176310315865259@localhost:3306/employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;# Enable this for PostgreSQL.
# SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://postgres:pcb.2176310315865259@localhost/employees'
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WriteCapableTable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;__tablename__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="n"&gt;emp_no&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;birth_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hire_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select_by_partial_last_name_and_first_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ResultStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_stored_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_employees&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLALCHEMY_DATABASE_URI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SQLALCHEMY_DATABASE_SCHEMA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/employees/search/{last_name}/{first_name}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JSONResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_employees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; last_name and first_name are partial using %.

    An example of a valid route: http://localhost:5000/employees/search/%nas%/%An
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Employees&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; \
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_by_partial_last_name_and_first_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To execute the &lt;code&gt;example.py&lt;/code&gt; application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;▶️&amp;lt;code&amp;gt;Windows 10:&amp;lt;/code&amp;gt; (venv) F:\bh_database\examples\fastapir&amp;gt;venv\Scripts\uvicorn.exe example:app --host 0.0.0.0 --port 5000
▶️&amp;lt;code&amp;gt;Ubuntu 22.10:&amp;lt;/code&amp;gt; (venv) behai@hp-pavilion-15:~/bh_database/examples/fastapir$ venv/bin/uvicorn example:app --host 0.0.0.0 --port 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing the &lt;code&gt;example.py&lt;/code&gt; application running locally from Windows 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5000/employees/search/%nas%/%An
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing the &lt;code&gt;example.py&lt;/code&gt; application running on Ubuntu 22.10 from Windows 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://192.168.0.16:5000/employees/search/%nas%/%An
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❸ A more comprehensive &lt;a href="https://flask.palletsprojects.com/en/3.0.x/" title="Flask"&gt;Flask&lt;/a&gt; application: a fully documented web server example with CRUD operations.&lt;/p&gt;

&lt;p&gt;Please refer to &lt;a href="https://github.com/behai-nguyen/bh_database/tree/main/examples/flaskr" title="Flask web server example with CRUD operations."&gt;&lt;/a&gt;&lt;a href="https://github.com/behai-nguyen/bh_database/tree/main/examples/flaskr"&gt;https://github.com/behai-nguyen/bh_database/tree/main/examples/flaskr&lt;/a&gt; for the full source code, instructions on setting up the environment, installing packages, running tests, and finally running the application.&lt;/p&gt;

&lt;p&gt;The layout of the example project is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/behai/bh_database/examples/flaskr
├── app.py
├── .env
├── pyproject.toml
├── pytest.ini
├── README.md
├── src
│ └── flaskr
│     ├── business
│     │ ├── app_business.py
│     │ ├── base_business.py
│     │ ├── base_validation.py
│     │ ├── employees_mgr.py
│     │ └── employees_validation.py
│     ├── config.py
│     ├── controllers
│     │ └── employees_admin.py
│     ├── __init__.py
│     ├── models
│     │ └── employees.py
│     ├── static
│     │ └── styles.css
│     └── templates
│         ├── admin
│         │ ├── emp_edit.html
│         │ ├── emp_search.html
│         │ └── emp_search_result.html
│         └── base.html
└── tests
    ├── business
    │ └── test_employees_mgr.py
    ├── conftest.py
    ├── __init__.py
    ├── integration
    │ └── test_employees_itgt.py
    └── unit
        └── test_employees.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❹ A more comprehensive &lt;a href="https://fastapi.tiangolo.com/" title="FastAPI"&gt;FastAPI&lt;/a&gt; application: a fully documented web server example with CRUD operations.&lt;/p&gt;

&lt;p&gt;Please refer to &lt;a href="https://github.com/behai-nguyen/bh_database/tree/main/examples/fastapir" title="FastAPI web server example with CRUD operations."&gt;&lt;/a&gt;&lt;a href="https://github.com/behai-nguyen/bh_database/tree/main/examples/fastapir"&gt;https://github.com/behai-nguyen/bh_database/tree/main/examples/fastapir&lt;/a&gt; for the full source code, instructions on setting up the environment, installing packages, running tests, and finally running the application.&lt;/p&gt;

&lt;p&gt;The layout of the example project is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/behai/bh_database/examples/fastapir
├── .env
├── main.py
├── pyproject.toml
├── pytest.ini
├── README.md
├── src
│ └── fastapir
│     ├── business
│     │ ├── app_business.py
│     │ ├── base_business.py
│     │ ├── base_validation.py
│     │ ├── employees_mgr.py
│     │ └── employees_validation.py
│     ├── config.py
│     ├── controllers
│     │ ├── employees_admin.py
│     │ └── __init__.py
│     ├── __init__.py
│     ├── models
│     │ └── employees.py
│     ├── static
│     │ └── styles.css
│     └── templates
│         ├── admin
│         │ ├── emp_edit.html
│         │ ├── emp_search.html
│         │ └── emp_search_result.html
│         └── base.html
└── tests
    ├── business
    │ └── test_employees_mgr.py
    ├── conftest.py
    ├── __init__.py
    ├── integration
    │ └── test_employees_itgt.py
    └── unit
        └── test_employees.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💥 Except for the framework-specific layer code, the remaining code in these two examples is very similar.&lt;/p&gt;

&lt;p&gt;Let's briefly discuss their similarities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/models&lt;/code&gt; and &lt;code&gt;/business&lt;/code&gt; code are identical. They could be shared across both examples, but I prefer to keep each example self-contained.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/tests/unit&lt;/code&gt; and &lt;code&gt;/tests/business&lt;/code&gt; code are identical.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
And there are differences in the following areas:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/controllers&lt;/code&gt;: This is the web layer, which is framework-specific, so understandably they are different.
&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;/tests/integration&lt;/code&gt;: The sole difference is framework-specific: how the HTTP response value is extracted:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Flask&lt;/code&gt;: &lt;code&gt;response.get_data(as_text=True)&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;FastAPI&lt;/code&gt;: &lt;code&gt;response.text&lt;/code&gt;
&lt;/li&gt;           
&lt;/ul&gt;




&lt;/li&gt;




&lt;li&gt;

&lt;code&gt;/tests/conftest.py&lt;/code&gt;: This file is framework-dependent. Both modules return the same fixtures, but the code has nothing in common.
&lt;/li&gt;




&lt;li&gt;

&lt;code&gt;/templates/base.html&lt;/code&gt;: There is one difference:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Flask&lt;/code&gt;: `&amp;lt;link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}"&amp;gt;`
&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;FastAPI&lt;/code&gt;: `&amp;lt;link rel="stylesheet" href="{{ url_for('static', path='/styles.css') }}"&amp;gt;`
&lt;/li&gt;           
&lt;/ul&gt;

&lt;p&gt;
That is, &lt;code&gt;Flask&lt;/code&gt; uses &lt;code&gt;filename&lt;/code&gt;, while 
&lt;code&gt;FastAPI&lt;/code&gt; uses &lt;code&gt;path&lt;/code&gt;.
&lt;/p&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;
The &lt;code&gt;/controllers&lt;/code&gt; layer is thin in the sense that the code is fairly short; it simply takes the client-submitted data and passes it to the business layer to handle the work. The business layer then forwards the validated data to the database layer, and so on. The differences between the two implementations are minor.
&lt;/p&gt;

&lt;p&gt;
It has been an interesting exercise developing this wrapper component. The fact that it seamlessly integrates with the &lt;a href="https://fastapi.tiangolo.com/" title="FastAPI"&gt;FastAPI&lt;/a&gt; framework is just a bonus for me; I didn't plan for it since I hadn't learned &lt;code&gt;FastAPI&lt;/code&gt; at the time. I hope you find this post useful. Thank you for reading, and stay safe as always.
&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;
Feature image source:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://quintagroup.com/cms/python/images/sqlalchemy-logo.png/view"&gt;https://quintagroup.com/cms/python/images/sqlalchemy-logo.png/view&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.logo.wine/logo/MySQL"&gt;https://www.logo.wine/logo/MySQL&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://icon-icons.com/download/170836/PNG/512/"&gt;https://icon-icons.com/download/170836/PNG/512/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://flask.palletsprojects.com/en/3.0.x/"&gt;https://flask.palletsprojects.com/en/3.0.x/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sqlalchemy</category>
      <category>flask</category>
      <category>fastapi</category>
      <category>wrapper</category>
    </item>
    <item>
      <title>Rust: Actix-web -- Async Functions as Middlewares</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Wed, 20 Mar 2024 12:26:10 +0000</pubDate>
      <link>https://forem.com/behainguyen/rust-actix-web-async-functions-as-middlewares-4327</link>
      <guid>https://forem.com/behainguyen/rust-actix-web-async-functions-as-middlewares-4327</guid>
      <description>&lt;p&gt;&lt;em&gt;In the &lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/" title="Rust: actix-web JSON Web Token authentication" rel="noopener noreferrer"&gt;tenth&lt;/a&gt; post of our &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web" rel="noopener noreferrer"&gt;actix-web&lt;/a&gt; learning application, we added an ad hoc middleware. In this post, with the assistance of the &lt;a href="https://docs.rs/actix-web-lab/latest/actix_web_lab/index.html" title="actix-web-lab" rel="noopener noreferrer"&gt;actix-web-lab&lt;/a&gt; crate, we will refactor this ad hoc middleware into a standalone &lt;code&gt;async&lt;/code&gt; function to enhance the overall code readability.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
🦀 &lt;a href="https://github.com/behai-nguyen/rust_web_01" title="Index of the Complete Series" rel="noopener noreferrer"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

&lt;p&gt;
🚀 &lt;strong&gt;Please note,&lt;/strong&gt; complete code for this post
can be downloaded from GitHub with:
&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.13.0 https://github.com/behai-nguyen/rust_web_01.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web" rel="noopener noreferrer"&gt;actix-web&lt;/a&gt; learning application mentioned above has been discussed in the twelve previous posts. The index of the complete series &lt;a href="https://github.com/behai-nguyen/rust_web_01" title="Index of the Complete Series" rel="noopener noreferrer"&gt;can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code we're developing in this post is a continuation of the code from the &lt;a href="https://behainguyen.wordpress.com/2024/03/18/rust-actix-web-daily-logging-fix-local-offset-apply-event-filtering/" title="Rust: Actix-web Daily Logging -- Fix Local Offset, Apply Event Filtering" rel="noopener noreferrer"&gt;twelfth&lt;/a&gt; post. 🚀 To get the code of this &lt;a href="https://behainguyen.wordpress.com/2024/03/18/rust-actix-web-daily-logging-fix-local-offset-apply-event-filtering/" title="Rust: Actix-web Daily Logging -- Fix Local Offset, Apply Event Filtering" rel="noopener noreferrer"&gt;twelfth&lt;/a&gt; post, please use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.12.0 https://github.com/behai-nguyen/rust_web_01.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;-- Note the tag &lt;code&gt;v0.12.0&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While this post continues from previous posts in this series, it can be read in conjunction with only the &lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/" title="Rust: actix-web JSON Web Token authentication" rel="noopener noreferrer"&gt;tenth&lt;/a&gt; post, focusing particularly on the section titled &lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/#updated-request-auth-process-lib" title="Code Updated in the src/lib.rs Module" rel="noopener noreferrer"&gt;Code Updated in the src/lib.rs Module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout"&gt;&lt;/a&gt;&lt;br&gt;
❶ For this post, no new modules are introduced. Instead, we will update existing modules and files. The layout chart below displays the updated files and modules, with those marked with &lt;span&gt;★&lt;/span&gt; indicating the ones that have been updated.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout-chart"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── Cargo.toml ★
├── ...
├── README.md ★
└── src
  ├── lib.rs ★
  └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="the-cargo-file"&gt;&lt;/a&gt;&lt;br&gt;
❷ An update to the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d22804332a55c683dbc272d66fa829c478681ea7/Cargo.toml#L24" title="The Cargo.toml file" rel="noopener noreferrer"&gt;Cargo.toml&lt;/a&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="py"&gt;actix-web-lab&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.20.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added the new crate &lt;a href="https://docs.rs/actix-web-lab/latest/actix_web_lab/index.html" title="actix-web-lab" rel="noopener noreferrer"&gt;actix-web-lab&lt;/a&gt;. This crate is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In-progress extractors and middleware for Actix Web.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This crate provides mechanisms for implementing middlewares as standalone &lt;code&gt;async&lt;/code&gt; functions, rather than using &lt;code&gt;actix-web&lt;/code&gt;'s &lt;a href="https://docs.rs/actix-web/latest/actix_web/struct.App.html#method.wrap_fn" title="wrap_fn function" rel="noopener noreferrer"&gt;wrap_fn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;According to the documentation, the &lt;a href="https://docs.rs/actix-web-lab/latest/actix_web_lab/index.html" title="actix-web-lab" rel="noopener noreferrer"&gt;actix-web-lab&lt;/a&gt; crate is essentially experimental. Functionalities implemented in this crate might eventually be integrated into the &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web" rel="noopener noreferrer"&gt;actix-web&lt;/a&gt; crate. In such a case, we would need to update our code.&lt;/p&gt;

&lt;p&gt;&lt;a id="refactor-middleware-out-of-wrap-fn"&gt;&lt;/a&gt;&lt;br&gt;
❸ Refactor an existing ad hoc middleware out of &lt;a href="https://docs.rs/actix-web/latest/actix_web/struct.App.html#method.wrap_fn" title="wrap_fn function" rel="noopener noreferrer"&gt;wrap_fn&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;As mentioned at the beginning, this post should be read in conjunction with the &lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/" title="Rust: actix-web JSON Web Token authentication" rel="noopener noreferrer"&gt;tenth&lt;/a&gt; post, where we introduced this &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/lib.rs#L156" title="src/lib.rs token forwarding ad hoc middleware" rel="noopener noreferrer"&gt;ad hoc middleware&lt;/a&gt;. The description of this simple middleware functionality is found in the section &lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/#updated-request-auth-process-lib" title="Code Updated in the src/lib.rs Module" rel="noopener noreferrer"&gt;Code Updated in the src/lib.rs Module&lt;/a&gt; of the tenth post.&lt;/p&gt;

&lt;p&gt;Below, we reprint the code of this ad hoc middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;            &lt;span class="c1"&gt;//&lt;/span&gt;
            &lt;span class="c1"&gt;// This ad hoc middleware looks for the updated access token String attachment in &lt;/span&gt;
            &lt;span class="c1"&gt;// the request extension, if there is one, extracts it and sends it to the client &lt;/span&gt;
            &lt;span class="c1"&gt;// via both the ``authorization`` header and cookie.&lt;/span&gt;
            &lt;span class="c1"&gt;//&lt;/span&gt;
            &lt;span class="nf"&gt;.wrap_fn&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;srv&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;updated_access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="c1"&gt;// Get set in src/auth_middleware.rs's &lt;/span&gt;
                &lt;span class="c1"&gt;// fn update_and_set_updated_token(request: &amp;amp;ServiceRequest, token_status: TokenStatus).&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="nf"&gt;.extensions_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;updated_access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="n"&gt;srv&lt;/span&gt;&lt;span class="nf"&gt;.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;updated_access_token&lt;/span&gt;&lt;span class="nf"&gt;.is_some&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updated_access_token&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                        &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="nf"&gt;.as_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.headers_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AUTHORIZATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                            &lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;HeaderValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TOKEN_STR_JWT_MSG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;);&lt;/span&gt;

                        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="nf"&gt;.as_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.response_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.add_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nf"&gt;build_authorization_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                    &lt;span class="p"&gt;};&lt;/span&gt;

                    &lt;span class="n"&gt;res&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not particularly lengthy, but its inclusion in the application instance construction process makes it difficult to read. While &lt;a href="https://doc.rust-lang.org/book/ch13-01-closures.html" title="closures" rel="noopener noreferrer"&gt;closures&lt;/a&gt; can call &lt;a href="https://doc.rust-lang.org/book/ch03-03-how-functions-work.html" title="functions" rel="noopener noreferrer"&gt;functions&lt;/a&gt;, refactoring this implementation into a standalone function isn't feasible. This is because the function would require access to the parameter &lt;code&gt;srv&lt;/code&gt;, which in this case refers to the &lt;a href="https://github.com/actix/actix-web/blob/0383f4bdd1210e726143ca1ebcf01169b67a4b6c/actix-web/src/app_service.rs#L294" title="AppRouting struct" rel="noopener noreferrer"&gt;AppRouting&lt;/a&gt; struct. Please refer to the screenshot below for clarification:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F03%2F103-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F03%2F103-01.png" alt="103-01.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;AppRouting&lt;/code&gt; struct is located in the private module &lt;a href="https://github.com/actix/actix-web/blob/0383f4bdd1210e726143ca1ebcf01169b67a4b6c/actix-web/src/app_service.rs" title="actix-web/src/app_service.rs" rel="noopener noreferrer"&gt;actix-web/src/app_service.rs&lt;/a&gt;, which means we don't have direct access to it. I attempted to refactor it into a standalone function but encountered difficulties. Someone else had also attempted it before me and faced similar issues.&lt;/p&gt;

&lt;p&gt;Please refer to the GitHub issue titled &lt;a href="https://github.com/actix/actix-web/issues/2681" title="wrap_fn &amp;amp;AppRouting should use Arc&amp;lt;AppRouting&amp;gt; #2681" rel="noopener noreferrer"&gt;wrap_fn &amp;amp;AppRouting should use Arc&amp;lt;AppRouting&amp;gt; #2681&lt;/a&gt; for more details. &lt;a href="https://github.com/actix/actix-web/issues/2681#issuecomment-1059769414" title="This reply" rel="noopener noreferrer"&gt;This reply&lt;/a&gt; suggests using the &lt;a href="https://docs.rs/actix-web-lab/latest/actix_web_lab/index.html" title="actix-web-lab" rel="noopener noreferrer"&gt;actix-web-lab&lt;/a&gt; crate. &lt;/p&gt;

&lt;p&gt;I believe I've come across this crate before, particularly the function &lt;a href="https://docs.rs/actix-web-lab/latest/actix_web_lab/middleware/fn.from_fn.html" title="Function actix_web_lab::middleware::from_fn" rel="noopener noreferrer"&gt;actix_web_lab::middleware::from_fn&lt;/a&gt;, but it didn't register with me at the time.&lt;/p&gt;

&lt;p&gt;Drawing from the official example &lt;a href="https://github.com/robjtede/actix-web-lab/blob/7f5ce616f063b0735fb423a441de7da872847c5c/actix-web-lab/examples/from_fn.rs" title="actix-web-lab/actix-web-lab/examples/from_fn.rs" rel="noopener noreferrer"&gt;actix-web-lab/actix-web-lab/examples/from_fn.rs&lt;/a&gt; and compiler suggestions, I've successfully refactored the ad hoc middleware mentioned above into the standalone async function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d22804332a55c683dbc272d66fa829c478681ea7/src/lib.rs#L143" title="src/lib.rs async fn update_return_jwt&amp;lt;B&amp;gt;(req: ServiceRequest, next: Next&amp;lt;B&amp;gt;) -&amp;gt; Result&amp;lt;ServiceResponse&amp;lt;B&amp;gt;, Error&amp;gt;" rel="noopener noreferrer"&gt;async fn update_return_jwt&amp;lt;B&amp;gt;(req: ServiceRequest, next: Next&amp;lt;B&amp;gt;) -&amp;gt; Result&amp;lt;ServiceResponse&amp;lt;B&amp;gt;, Error&amp;gt;&lt;/a&gt;. The screenshot below, taken from Visual Studio Code with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer" title="the Rust-Analyzer" rel="noopener noreferrer"&gt;Rust-Analyzer&lt;/a&gt; plugin, displays the full source code and variable types:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F03%2F103-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F03%2F103-02.png" alt="103-02.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compared to the original ad hoc middleware, the code is virtually unchanged. It's worth noting that this final version is the result of my sixth or seventh attempt; without the compiler suggestions, I would not have been able to complete it. We register it with the application instance using only a &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d22804332a55c683dbc272d66fa829c478681ea7/src/lib.rs#L202" title="src/lib.rs register async function update_return_jwt as a middleware" rel="noopener noreferrer"&gt;single line&lt;/a&gt;, as per the documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;            &lt;span class="nf"&gt;.wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;from_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_return_jwt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="other-minor-refactorings"&gt;&lt;/a&gt;&lt;br&gt;
❹ Other minor refactorings include optimising the application instance builder code for brevity. Specifically, I've moved the code to create the CORS instance to the standalone function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d22804332a55c683dbc272d66fa829c478681ea7/src/lib.rs#L47" title="src/lib.rs fn cors_config(config: &amp;amp;config::Config) -&amp;gt; Cors" rel="noopener noreferrer"&gt;fn cors_config(config: &amp;amp;config::Config) -&amp;gt; Cors&lt;/a&gt;, and the code to create the session store to the standalone async function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d22804332a55c683dbc272d66fa829c478681ea7/src/lib.rs#L62" title="src/lib.rs async fn config_session_store() -&amp;gt; (actix_web:🍪:Key, RedisSessionStore)" rel="noopener noreferrer"&gt;async fn config_session_store() -&amp;gt; (actix_web:🍪:Key, RedisSessionStore)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Currently, the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d22804332a55c683dbc272d66fa829c478681ea7/src/lib.rs" title="src/lib.rs" rel="noopener noreferrer"&gt;src/lib.rs&lt;/a&gt; module is less than 250 lines long, housing 7 helper functions that are completely unrelated. I find it still very manageable. The code responsible for creating the server instance and the application instance, encapsulated in the function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d22804332a55c683dbc272d66fa829c478681ea7/src/lib.rs#L179" title="src/lib.rs pub async fn run(listener: TcpListener) -&amp;gt; Result&amp;lt;Server, std::io::Error&amp;gt;" rel="noopener noreferrer"&gt;pub async fn run(listener: TcpListener) -&amp;gt; Result&amp;lt;Server, std::io::Error&amp;gt;&lt;/a&gt;, remains around 60 lines. Although I anticipate it will grow a bit more as we add more functionalities, I don't foresee it becoming overly lengthy.&lt;/p&gt;

&lt;p&gt;&lt;a id="concluding-remarks"&gt;&lt;/a&gt;&lt;br&gt;
❺ I am happy to have learned something new about &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web" rel="noopener noreferrer"&gt;actix-web&lt;/a&gt;. And I hope you find the information useful. Thank you for reading. And stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper" rel="noopener noreferrer"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/" rel="noopener noreferrer"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;https://www.rust-lang.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/" rel="noopener noreferrer"&gt;https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
🦀 &lt;a href="https://github.com/behai-nguyen/rust_web_01" title="Index of the Complete Series" rel="noopener noreferrer"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

</description>
      <category>rust</category>
      <category>actix</category>
      <category>async</category>
      <category>middleware</category>
    </item>
    <item>
      <title>Rust: Actix-web Daily Logging -- Fix Local Offset, Apply Event Filtering</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Mon, 18 Mar 2024 04:46:45 +0000</pubDate>
      <link>https://forem.com/behainguyen/rust-actix-web-daily-logging-fix-local-offset-apply-event-filtering-32ne</link>
      <guid>https://forem.com/behainguyen/rust-actix-web-daily-logging-fix-local-offset-apply-event-filtering-32ne</guid>
      <description>&lt;p&gt;&lt;em&gt;In the &lt;a href="https://behainguyen.wordpress.com/2024/03/13/rust-actix-web-and-daily-logging/#project-layout" title="Rust: Actix-web and Daily Logging"&gt;last post&lt;/a&gt; of our &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web"&gt;actix-web&lt;/a&gt; learning application, we identified two problems. First, there is an issue with calculating the UTC time offset on Ubuntu 22.10, as described in the section &lt;a href="https://behainguyen.wordpress.com/2024/03/13/rust-actix-web-and-daily-logging/#utcoffset-linux-problem" title="💥 Issue with calculating UTC time offset on Ubuntu 22.10"&gt;💥 Issue with calculating UTC time offset on Ubuntu 22.10&lt;/a&gt;. Secondly, loggings from other crates that match the logging configuration are also written onto log files, as mentioned in the &lt;a href="https://behainguyen.wordpress.com/2024/03/13/rust-actix-web-and-daily-logging/#concluding-remarks" title="Concluding Remarks"&gt;Concluding Remarks&lt;/a&gt; section. We should be able to configure what gets logged. We will address both of these issues in this post.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
🦀 &lt;a href="https://github.com/behai-nguyen/rust_web_01" title="Index of the Complete Series"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

&lt;p&gt;🚀 &lt;strong&gt;Please note,&lt;/strong&gt; complete code for this post can be downloaded from GitHub with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.12.0 https://github.com/behai-nguyen/rust_web_01.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web"&gt;actix-web&lt;/a&gt; learning application mentioned above has been discussed in the eleven previous posts. The index of the complete series &lt;a href="https://github.com/behai-nguyen/rust_web_01" title="Index of the Complete Series"&gt;can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code we're developing in this post is a continuation of the code from the &lt;a href="https://behainguyen.wordpress.com/2024/03/13/rust-actix-web-and-daily-logging/#project-layout" title="Rust: Actix-web and Daily Logging"&gt;eleventh&lt;/a&gt; post. 🚀 To get the code of this &lt;a href="https://behainguyen.wordpress.com/2024/03/13/rust-actix-web-and-daily-logging/#project-layout" title="Rust: Actix-web and Daily Logging"&gt;eleventh&lt;/a&gt; post, please use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.11.0 https://github.com/behai-nguyen/rust_web_01.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;-- Note the tag &lt;code&gt;v0.11.0&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While this post continues from previous posts in this series, it can be read in conjunction with only the &lt;a href="https://behainguyen.wordpress.com/2024/03/13/rust-actix-web-and-daily-logging/#project-layout" title="Rust: Actix-web and Daily Logging"&gt;eleventh&lt;/a&gt; post.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout"&gt;&lt;/a&gt;&lt;br&gt;
❶ For this post, no new modules are introduced. Instead, we will update some existing modules and files. The layout chart below shows the updated files and modules, with those marked with &lt;span&gt;★&lt;/span&gt; indicating the updated ones.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout-chart"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── .env ★
├── Cargo.toml ★
├── ...
├── README.md ★
├── src
│ ├── helper
│ │ ├── app_logger.rs ★
│ │ └── ...
│ ├── main.rs ★
│ └── ...
└── tests
    ├── common.rs ★
    └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🦀 In the context of this post, our focus is solely on the &lt;code&gt;RUST_LOG&lt;/code&gt; entry in the &lt;code&gt;.env&lt;/code&gt; file, which we will discuss in a later section.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-cargo-file"&gt;&lt;/a&gt;&lt;br&gt;
❷ Updates to the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/ded522aa79c0a5f82200dce585e1dad164918bd4/Cargo.toml" title="The Cargo.toml file"&gt;Cargo.toml&lt;/a&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
[dependencies]
...
time-tz = {version = "2.0", features = ["system"]}
tracing-subscriber = {version = "0.3", features = ["fmt", "std", "local-time", "time", "env-filter"]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added the new crate &lt;a href="https://docs.rs/time-tz/2.0.0/time_tz/" title="time-tz"&gt;time-tz&lt;/a&gt;, and for &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/index.html" title="tracing-subscriber"&gt;tracing-subscriber&lt;/a&gt;, we added the crate feature &lt;code&gt;env-filter&lt;/code&gt;. We will discuss these additions in later sections.&lt;/p&gt;

&lt;p&gt;&lt;a id="utc-time-offset"&gt;&lt;/a&gt;&lt;br&gt;
❸ Resolve the issue with calculating the UTC time offset to ensure reliable functionality on both Ubuntu 22.10 and Windows 10.&lt;/p&gt;

&lt;p&gt;⓵ As &lt;a href="https://behainguyen.wordpress.com/2024/03/13/rust-actix-web-and-daily-logging/#utcoffset-linux-problem" title="💥 Issue with calculating UTC time offset on Ubuntu 22.10"&gt;mentioned&lt;/a&gt; in the last post:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After extensive searching, I came across this GitHub issue, &lt;a href="https://github.com/time-rs/time/pull/297" title="Document #293 in local-offset feature description #297"&gt;Document #293 in local-offset feature description #297&lt;/a&gt;. &lt;em&gt;It appears that even after three years, this issue remains unresolved.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Document #293&lt;/code&gt; is dated December 19, 2020. Additionally, there are other relevant documents that I did not come across during my previous “extensive searching”:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
November 25, 2020 -- &lt;a href="https://github.com/time-rs/time/issues/296" title="Time 0.2.23 fails to determine local offset #296"&gt;Time 0.2.23 fails to determine local offset #296&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
November 2, 2021 -- &lt;a href="https://github.com/time-rs/time/issues/380" title="Better solution for getting local offset on unix #380"&gt;Better solution for getting local offset on unix #380&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
Dec 5, 2019 -- &lt;a href="https://github.com/time-rs/time/issues/193" title="tzdb support #193"&gt;tzdb support #193&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://github.com/time-rs/time/issues/193#issuecomment-1037227056" title="This reply"&gt;This reply&lt;/a&gt; posted on February 13, 2022 mentions the crate &lt;a href="https://crates.io/crates/time-tz" title="time-tz"&gt;time-tz&lt;/a&gt;, which resolves the previously mentioned issue.&lt;/p&gt;

&lt;p&gt;The parameter &lt;code&gt;utc_offset: time::UtcOffset&lt;/code&gt; was removed from the function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/ded522aa79c0a5f82200dce585e1dad164918bd4/src/helper/app_logger.rs#L38" title="src/helper/app_logger.rs pub fn init_app_logger() -&amp;gt; WorkerGuard"&gt;pub fn init_app_logger() -&amp;gt; WorkerGuard&lt;/a&gt;, and the offset calculation is now carried out internally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;OffsetTime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="nf"&gt;.offset&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nd"&gt;format_description!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[year]-[month]-[day] [hour]:[minute]:[second]"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⓶ Offset calculations were accordingly removed from both the function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/ded522aa79c0a5f82200dce585e1dad164918bd4/src/main.rs#L10" title="src/main.rs async fn main() -&amp;gt; Result&amp;lt;(), std::io::Error&amp;gt;"&gt;async fn main() -&amp;gt; Result&amp;lt;(), std::io::Error&amp;gt;&lt;/a&gt; in the module &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/ded522aa79c0a5f82200dce585e1dad164918bd4/src/main.rs" title="src/main.rs"&gt;src/main.rs&lt;/a&gt;, and the function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/ded522aa79c0a5f82200dce585e1dad164918bd4/tests/common.rs#L56" title="tests/common.rs function pub async fn spawn_app() -&amp;gt; TestApp"&gt;pub async fn spawn_app() -&amp;gt; TestApp&lt;/a&gt; in the module &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/ded522aa79c0a5f82200dce585e1dad164918bd4/tests/common.rs" title="tests/common.rs"&gt;tests/common.rs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="logging-event-filter"&gt;&lt;/a&gt;&lt;br&gt;
❸ Configuration to determine what get logged.&lt;/p&gt;

&lt;p&gt;We use &lt;code&gt;tracing_subscriber&lt;/code&gt;'s &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html" title="Struct tracing_subscriber::filter::EnvFilter"&gt;filter::EnvFilter&lt;/a&gt; struct to filter which events are logged. This functionality requires the crate feature &lt;code&gt;env-filter&lt;/code&gt;, as described above.&lt;/p&gt;

&lt;p&gt;Event filtering is configured via the environment variable &lt;code&gt;RUST_LOG&lt;/code&gt;. Its value can be much more sophisticated than simply &lt;code&gt;trace&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt;. The documentation in the section &lt;a href="https://docs.rs/env_logger/latest/env_logger/#enabling-logging" title="Enabling logging"&gt;Enabling logging&lt;/a&gt; of the &lt;code&gt;env_logger&lt;/code&gt; crate describes the syntax of &lt;code&gt;RUST_LOG&lt;/code&gt; with plenty of informative examples.&lt;/p&gt;

&lt;p&gt;&lt;a id="logging-event-filter-code-refactorings"&gt;&lt;/a&gt;&lt;br&gt;
⓵ Implementing event filtering for the function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/ded522aa79c0a5f82200dce585e1dad164918bd4/src/helper/app_logger.rs#L110" title="src/helper/app_logger.rs pub fn init_app_logger() -&amp;gt; WorkerGuard"&gt;pub fn init_app_logger() -&amp;gt; WorkerGuard&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;filter_layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;EnvFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_default_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.or_else&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;EnvFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"debug"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tracing_subscriber&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;...&lt;/span&gt;
                &lt;span class="nf"&gt;.with_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_layer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that the code to convert the value of &lt;code&gt;RUST_LOG&lt;/code&gt; to &lt;a href="https://docs.rs/tracing/latest/tracing/struct.Level.html" title="tracing::Level"&gt;tracing::Level&lt;/a&gt; has also been removed.&lt;/p&gt;

&lt;p&gt;For further documentation, please refer to &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/index.html#filtering-events-with-environment-variables" title="Filtering Events with Environment Variables"&gt;Filtering Events with Environment Variables&lt;/a&gt;. The code above is from the section &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/index.html#composing-layers" title="Composing Layers"&gt;Composing Layers&lt;/a&gt; of the mentioned documentation page. As for the two functions being called, please see:&lt;/p&gt;

&lt;p&gt;● &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#method.try_from_default_env" title="pub fn try_from_default_env() -&amp;gt; Result&amp;lt;Self, FromEnvError&amp;gt;"&gt;pub fn try_from_default_env() -&amp;gt; Result&amp;lt;Self, FromEnvError&amp;gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;● &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#method.try_new" title="pub fn try_new&amp;lt;S: AsRef&amp;lt;str&amp;gt;&amp;gt;(dirs: S) -&amp;gt; Result&amp;lt;Self, ParseError&amp;gt;"&gt;pub fn try_new&amp;lt;S: AsRef&amp;lt;str&amp;gt;&amp;gt;(dirs: S) -&amp;gt; Result&amp;lt;Self, ParseError&amp;gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;
And finally, the line:
&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;            &lt;span class="o"&gt;...&lt;/span&gt;
                &lt;span class="nf"&gt;.with_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_layer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is from the trait &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html" title="Trait tracing_subscriber::layer::Layer"&gt;tracing_subscriber::layer::Layer&lt;/a&gt;, which is a “a composable handler for &lt;code&gt;tracing&lt;/code&gt; events.”&lt;/p&gt;

&lt;p&gt;&lt;a id="logging-event-filter-rust-log-env-var"&gt;&lt;/a&gt;&lt;br&gt;
⓶ As for the value of &lt;code&gt;RUST_LOG&lt;/code&gt;, there are three cases that do not behave as I initially assumed:&lt;/p&gt;

&lt;p&gt;The first two cases are &lt;code&gt;RUST_LOG=xxxx&lt;/code&gt; and &lt;code&gt;RUST_LOG=&lt;/code&gt;, &lt;strong&gt;where nothing gets logged.&lt;/strong&gt; I had assumed that this error handling would default the logging event to &lt;code&gt;debug&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;        &lt;span class="nf"&gt;.or_else&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;EnvFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"debug"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I attempted several times to default them to &lt;code&gt;debug&lt;/code&gt;, but unfortunately, I was unsuccessful.&lt;/p&gt;

&lt;p&gt;The third case is &lt;code&gt;RUST_LOG&lt;/code&gt;, where only the &lt;code&gt;RUST_LOG&lt;/code&gt; variable name is present in the &lt;code&gt;.env&lt;/code&gt; file without any value assigned to it. Based on the above two instances, I expected that nothing would be logged. However, it defaults to &lt;code&gt;debug&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Please note that for the next example discussion, it's important to keep in mind that the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/ded522aa79c0a5f82200dce585e1dad164918bd4/Cargo.toml#L11" title="The Cargo.toml file"&gt;Cargo.toml&lt;/a&gt; file contains the following declaration, where &lt;code&gt;learn_actix_web&lt;/code&gt; is defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[bin]]&lt;/span&gt;
&lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"src/main.rs"&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"learn_actix_web"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples of some valid values:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;RUST_LOG=off,learn_actix_web=debug&lt;/code&gt; -- Only &lt;code&gt;debug&lt;/code&gt; logging events from the &lt;code&gt;learn_actix_web&lt;/code&gt; crate are logged. All logging events from other crates are ignored.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RUST_LOG=off,learn_actix_web=info&lt;/code&gt; -- Only &lt;code&gt;info&lt;/code&gt; logging events from the application are logged. If there are no &lt;code&gt;info&lt;/code&gt; events in the application, nothing gets logged and the log files remain empty.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RUST_LOG=off,learn_actix_web=debug,actix_server=info&lt;/code&gt; -- Only &lt;code&gt;debug&lt;/code&gt; events from the application and &lt;code&gt;info&lt;/code&gt; events from the &lt;code&gt;actix_server&lt;/code&gt; crate are logged.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RUST_LOG=off,learn_actix_web::middleware=debug&lt;/code&gt; -- Only &lt;code&gt;debug&lt;/code&gt; events from the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/src/middleware.rs" title="src/middleware.rs"&gt;src/middleware.rs&lt;/a&gt; module of the application are logged. This middleware is triggered when accessing the &lt;code&gt;GET&lt;/code&gt; route &lt;code&gt;http://0.0.0.0:5000/helloemployee/{partial last name}/{partial first name}&lt;/code&gt; from an &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated session"&gt;&lt;code&gt;authenticated session&lt;/code&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A further illustration for example 4 above: Log in and click on the last button as shown in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://behainguyen.files.wordpress.com/2024/03/102-01.png"&gt;https://behainguyen.files.wordpress.com/2024/03/102-01.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current log file should contain the following three new lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2024-03-18 00:51:15 DEBUG learn_actix_web::middleware: Hi from start. You requested: /helloemployee/%chi/%ak
2024-03-18 00:51:15 DEBUG learn_actix_web::middleware: Middleware. last name: %chi, first name: %ak.
2024-03-18 00:51:15 DEBUG learn_actix_web::middleware: Hi from response -- some employees found.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This finer control demonstrates the power, utility, and helpfulness of tracking an intermittent bug that is not reproducible on staging and development environments. By enabling debug and tracing logging for specific modules, we can effectively troubleshoot such issues.&lt;/p&gt;

&lt;p&gt;&lt;a id="unique-web-ssession-id"&gt;&lt;/a&gt;&lt;br&gt;
❹ Logging events should be grouped within the unique ID of the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated session"&gt;&lt;code&gt;authenticated session&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For each &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated session"&gt;&lt;code&gt;authenticated session&lt;/code&gt;&lt;/a&gt;, there is a third-party &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#notes-on-cookies-third-party" title="actix-sesion id"&gt;session ID&lt;/a&gt;. I have conducted some studies on this cookie, and its value seems to change after each request. For further discussion of this &lt;code&gt;ID&lt;/code&gt; under &lt;code&gt;HTTPS&lt;/code&gt;, please refer to this &lt;a href="https://behainguyen.wordpress.com/2024/02/13/rust-actix-web-cors-cookies-and-ajax-calls/#cookies-ajax-https" title="actix-sesion id updated"&gt;discussion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My initial plan is to group logging for each request under the value of this &lt;code&gt;ID&lt;/code&gt;. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;** 2024-03-18 00:51:15 DEBUG learn_actix_web::middleware: {value of id} entered.
2024-03-18 00:51:15 DEBUG learn_actix_web::middleware: Hi from start. You requested: /helloemployee/%chi/%ak
...
** 2024-03-18 00:51:15 DEBUG learn_actix_web::middleware: {value of id} exited.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have not yet determined how to achieve this; further study is required.&lt;/p&gt;

&lt;p&gt;&lt;a id="concluding-remarks"&gt;&lt;/a&gt;&lt;br&gt;
❺ We have concluded this post. I'm pleased to have resolved the offset issue and to have implemented logging in a more effective manner.&lt;/p&gt;

&lt;p&gt;I hope you find the information useful. Thank you for reading. And stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rust-lang.org/"&gt;https://www.rust-lang.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/"&gt;https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
🦀 &lt;a href="https://github.com/behai-nguyen/rust_web_01" title="Index of the Complete Series"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

</description>
      <category>rust</category>
      <category>logging</category>
      <category>event</category>
      <category>filter</category>
    </item>
    <item>
      <title>Rust: Actix-web and Daily Logging</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Wed, 13 Mar 2024 05:58:20 +0000</pubDate>
      <link>https://forem.com/behainguyen/rust-actix-web-and-daily-logging-4a62</link>
      <guid>https://forem.com/behainguyen/rust-actix-web-and-daily-logging-4a62</guid>
      <description>&lt;p&gt;&lt;em&gt;Currently, our &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web"&gt;actix-web&lt;/a&gt; learning application simply prints debug information to the console using the &lt;code&gt;println!&lt;/code&gt; macro. In this post, we will implement proper non-blocking daily logging to files. &lt;code&gt;Daily logging&lt;/code&gt; entails rotating to a new log file each day. &lt;code&gt;Non-blocking&lt;/code&gt; refers to having a dedicated thread for file writing operations. We will utilise the &lt;a href="https://docs.rs/tracing/latest/tracing/index.html" title="tracing"&gt;tracing&lt;/a&gt;, &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/index.html" title="tracing-appender"&gt;tracing-appender&lt;/a&gt;, and &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/index.html" title="tracing-subscriber"&gt;tracing-subscriber&lt;/a&gt; crates for our logging implementation.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
🦀 &lt;a href="https://github.com/behai-nguyen/rust_web_01" title="Index of the Complete Series"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

&lt;p&gt;🚀 &lt;strong&gt;Please note,&lt;/strong&gt; complete code for this post can be downloaded from GitHub with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.11.0 https://github.com/behai-nguyen/rust_web_01.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web"&gt;actix-web&lt;/a&gt; learning application mentioned above has been discussed in the following ten previous posts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/10/18/rust-web-application-mysql-server-sqlx-actix-web-and-tera/" title="Rust web application: MySQL server, sqlx, actix-web and tera"&gt;Rust web application: MySQL server, sqlx, actix-web and tera&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/11/26/rust-learning-actix-web-middleware-01/" title="Rust: learning actix-web middleware 01"&gt;Rust: learning actix-web middleware 01&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/12/31/rust-retrofit-integration-tests-to-an-existing-actix-web-application/" title="Rust: retrofit integration tests to an existing actix-web application."&gt;Rust: retrofit integration tests to an existing actix-web application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/03/rust-adding-actix-session-and-actix-identity-to-an-existing-actix-web-application/" title="Rust: adding actix-session and actix-identity to an existing actix-web application."&gt;Rust: adding actix-session and actix-identity to an existing actix-web application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/14/rust-actix-web-endpoints-which-accept-both-application-x-www-form-urlencoded-and-application-json-content-types/" title="Rust: actix-web endpoints which accept both application/x-www-form-urlencoded and application/json content types."&gt;Rust: actix-web endpoints which accept both &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; and &lt;code&gt;application/json&lt;/code&gt; content types&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/" title="Rust: simple actix-web email-password login and request authentication using middleware."&gt;Rust: simple actix-web email-password login and request authentication using middleware&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/02/10/rust-actix-web-get-ssl-https-for-localhost/" title="Rust: actix-web get SSL/HTTPS for localhost."&gt;Rust: actix-web get SSL/HTTPS for localhost&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/02/13/rust-actix-web-cors-cookies-and-ajax-calls/" title="Rust: actix-web CORS, Cookies and AJAX calls."&gt;Rust: actix-web CORS, Cookies and AJAX calls&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/02/16/rust-actix-web-global-extractor-error-handlers/" title="Rust: actix-web global extractor error handlers"&gt;Rust: actix-web global extractor error handlers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/" title="Rust: actix-web JSON Web Token authentication"&gt;Rust: actix-web JSON Web Token authentication&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code we're developing in this post is a continuation of the code from the &lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/" title="Rust: actix-web JSON Web Token authentication"&gt;tenth&lt;/a&gt; post above. 🚀 To get the code of this &lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/" title="Rust: actix-web JSON Web Token authentication"&gt;tenth&lt;/a&gt; post, please use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.10.0 https://github.com/behai-nguyen/rust_web_01.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;-- Note the tag &lt;code&gt;v0.10.0&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While this post continues from previous posts in this series, it can also be read independently. The logging module developed herein can be used in other projects without modification.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout"&gt;&lt;/a&gt;&lt;br&gt;
❶ For this post, we introduce a new module &lt;code&gt;src/helper/app_logger.rs&lt;/code&gt;, and some other modules and files are updated. The project layout remains the same as in the &lt;a href="https://behainguyen.wordpress.com/2024/02/26/rust-actix-web-json-web-token-authentication/#project-layout" title="Rust: actix-web JSON Web Token authentication Project Layout"&gt;last post&lt;/a&gt;. The layout chart below shows the affected files and modules: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-- Please note that&lt;/strong&gt; files marked with &lt;span&gt;★&lt;/span&gt; are updated, and &lt;code&gt;src/helper/app_logger.rs&lt;/code&gt; is marked with &lt;span&gt;☆&lt;/span&gt;, as it is the only new module.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout-chart"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── .env ★
├── Cargo.toml ★
├── ...
├── README.md ★
├── src
│ ├── auth_middleware.rs ★
│ ├── database.rs ★
│ ├── helper
│ │ ├── app_logger.rs ☆
│ │ └── ...
│ ├── helper.rs ★
│ ├── main.rs ★
│ ├── middleware.rs ★
│ └── ...
└── tests
    ├── common.rs ★
    └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="the-env-file"&gt;&lt;/a&gt;&lt;br&gt;
❷ An update to the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/.env#L12" title="The .env file"&gt;.env&lt;/a&gt; file: a new entry has been added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;RUST_LOG&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value of &lt;code&gt;RUST_LOG&lt;/code&gt; is translated into &lt;a href="https://docs.rs/tracing/latest/tracing/struct.Level.html" title="tracing::Level"&gt;tracing::Level&lt;/a&gt;. Valid values include &lt;code&gt;trace&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt;. Any other values are invalid and will default to &lt;a href="https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.DEBUG" title="Level::DEBUG"&gt;Level::DEBUG&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-cargo-file"&gt;&lt;/a&gt;&lt;br&gt;
❸ Updates to the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/Cargo.toml#L38" title="The Cargo.toml file"&gt;Cargo.toml&lt;/a&gt; file: as expected, the new crates are added to the &lt;code&gt;[dependencies]&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="py"&gt;tracing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1"&lt;/span&gt;
&lt;span class="py"&gt;tracing-appender&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2"&lt;/span&gt;
&lt;span class="nn"&gt;tracing-subscriber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"std"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"local-time"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="utcoffset-linux-problem"&gt;&lt;/a&gt;&lt;br&gt;
❹ 💥 Issue with calculating UTC time offset on Ubuntu 22.10.&lt;/p&gt;

&lt;p&gt;In the new code added for this post, we need to calculate the UTC time offset to obtain local time. The following code works on Windows 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UtcOffset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UtcOffset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;current_local_offset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, on Ubuntu 22.10, it doesn't always function as expected. Sometimes, it raises the error &lt;a href="https://docs.rs/time/latest/time/error/struct.IndeterminateOffset.html" title="IndeterminateOffset"&gt;IndeterminateOffset&lt;/a&gt;. The inconsistency in its behavior makes it challenging to identify a clear pattern of when it works and when it doesn't.&lt;/p&gt;

&lt;p&gt;After extensive searching, I came across this GitHub issue, &lt;a href="https://github.com/time-rs/time/pull/297" title="Document #293 in local-offset feature description #297"&gt;Document #293 in local-offset feature description #297&lt;/a&gt;. &lt;em&gt;It appears that even after three years, this issue remains unresolved.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This complication adds an extra layer of difficulty in ensuring both the code and integration tests function properly. In the subsequent sections of this post, when discussing the code, we'll refer back to this issue when relevant. Please keep this in mind.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module"&gt;&lt;/a&gt;&lt;br&gt;
❺ The &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/src/helper/app_logger.rs" title="src/helper/app_logger.rs"&gt;src/helper/app_logger.rs&lt;/a&gt; module has been designed to be easily copied into other projects, provided that the &lt;code&gt;Cargo.toml&lt;/code&gt; file includes the required crates discussed earlier.&lt;/p&gt;

&lt;p&gt;This module contains only a single public function, &lt;code&gt;pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard&lt;/code&gt;, which the application calls to set up the log. Please refer to the notes and documentation within this module while reading the code.&lt;/p&gt;

&lt;p&gt;Originally, the &lt;code&gt;utc_offset: time::UtcOffset&lt;/code&gt; parameter was not present. However, due to the issue mentioned in 💥 Issue with calculating UTC time offset on Ubuntu 22.10, the code was refactored to include this parameter, offering a bit more flexibility.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module-log-file"&gt;&lt;/a&gt;&lt;br&gt;
⓵ Setting up the daily log files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;log_appender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RollingFileAppender&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.rotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Rotation&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DAILY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Daily log file.&lt;/span&gt;
        &lt;span class="nf"&gt;.filename_suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"log"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// log file names will be suffixed with `.log`&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./log"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// try to build an appender that stores log files in `/var/log`&lt;/span&gt;
        &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initialising rolling file appender failed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To set up the daily log files, we begin by calling the &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/rolling/struct.RollingFileAppender.html#method.builder" title="pub fn builder() -&amp;gt; Builder"&gt;pub fn builder() -&amp;gt; Builder&lt;/a&gt; function.&lt;/p&gt;

&lt;p&gt;We specify &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/rolling/struct.Rotation.html#daily-rotation" title="DAILY rotation"&gt;DAILY&lt;/a&gt; rotation to generate daily log files. However, it's important to note that according to &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/rolling/fn.daily.html" title="Function tracing_appender::rolling::daily"&gt;the documentation&lt;/a&gt;, the file names are appended with the current date in UTC. Since I'm in the Australian Eastern Standard Time (AEST) zone, which is 10-11 hours ahead of UTC, there were instances where my log file names were created with dates from the previous day.&lt;/p&gt;

&lt;p&gt;To give log files the &lt;code&gt;.log&lt;/code&gt; extension, we use the method &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/rolling/struct.Builder.html#method.filename_suffix" title="pub fn filename_suffix(self, suffix: impl Into&amp;lt;String&amp;amp;gt) -&amp;gt; Self"&gt;pub fn filename_suffix(self, suffix: impl Into&amp;lt;String&amp;gt;) -&amp;gt; Self&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The format of the daily log file names follows the pattern &lt;code&gt;YYYY-MM-DD.log&lt;/code&gt;, for example, &lt;code&gt;2024-03-10.log&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We then invoke the method &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/rolling/struct.Builder.html#method.build" title="pub fn build(&amp;amp;self, directory: impl AsRef&amp;lt;Path&amp;amp;gt) -&amp;amp;gt Result&amp;lt;RollingFileAppender, InitError&amp;amp;gt"&gt;pub fn build( &amp;amp;self, directory: impl AsRef&amp;lt;Path&amp;gt;) -&amp;gt; Result&amp;lt;RollingFileAppender, InitError&amp;gt;&lt;/a&gt; to specify the location of the log files within the &lt;code&gt;log/&lt;/code&gt; sub-directory relative to where the application is executed. For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;▶️&amp;lt;code&amp;gt;Windows 10:&amp;lt;/code&amp;gt; F:\rust\actix_web&amp;gt;target\debug\learn_actix_web.exe
▶️&amp;lt;code&amp;gt;Ubuntu 22.10:&amp;lt;/code&amp;gt; behai@hp-pavilion-15:~/rust/actix_web$ /home/behai/rust/actix_web/target/debug/learn_actix_web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This results in the log files being stored at &lt;code&gt;F:\rust\actix_web\log&amp;lt;/code&amp;gt; and &lt;code&gt;/home/behai/rust/actix_web/target/debug/learn_actix_web/log/&lt;/code&gt; respectively.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module-non-blocking-writer"&gt;&lt;/a&gt;&lt;br&gt;
⓶ We create a non-blocking writer thread using the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;non_blocking_appender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;log_guard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tracing_appender&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;non_blocking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_appender&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the documentation section for the function &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/fn.non_blocking.html" title="Function tracing_appender::non_blocking"&gt;tracing_appender::non_blocking&lt;/a&gt;. For more detailed documentation, refer to the &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/index.html" title="Module tracing_appender::non_blocking"&gt;tracing_appender::non_blocking&lt;/a&gt; module. Please note the following: &lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module-log-guard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This function returns a tuple of &lt;code&gt;NonBlocking&lt;/code&gt; and &lt;code&gt;WorkerGuard&lt;/code&gt;. &lt;code&gt;NonBlocking&lt;/code&gt; implements &lt;a href="https://docs.rs/tracing-subscriber/0.3.18/x86_64-unknown-linux-gnu/tracing_subscriber/fmt/writer/trait.MakeWriter.html" title="MakeWriter"&gt;MakeWriter&lt;/a&gt; which integrates with &lt;code&gt;tracing_subscriber&lt;/code&gt;. &lt;code&gt;WorkerGuard&lt;/code&gt; is a drop guard that is responsible for flushing any remaining logs when the program terminates.&lt;/p&gt;

&lt;p&gt;Note that the &lt;code&gt;WorkerGuard&lt;/code&gt; returned by &lt;code&gt;non_blocking&lt;/code&gt; &lt;em&gt;must&lt;/em&gt; be assigned to a binding that is not &lt;code&gt;&lt;em&gt;&lt;/em&gt;&lt;/code&gt;, as &lt;code&gt;&lt;/code&gt; will result in the &lt;code&gt;WorkerGuard&lt;/code&gt; being dropped immediately. Unintentional drops of &lt;code&gt;WorkerGuard&lt;/code&gt; remove the guarantee that logs will be flushed during a program’s termination, in a panic or otherwise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What this means is that we must keep &lt;code&gt;log_guard&lt;/code&gt; alive for the application to continue logging. &lt;code&gt;log_guard&lt;/code&gt; is an instance of the &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.WorkerGuard.html" title="WorkerGuard"&gt;WorkerGuard&lt;/a&gt; struct and is also the returned value of the public function &lt;code&gt;pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard&lt;/code&gt;. We will revisit this returned value in a later section.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module-local-datetime"&gt;&lt;/a&gt;&lt;br&gt;
⓷ Next, we specify the date and time format for each log line. Each line begins with a local date and time. For instance, &lt;code&gt;2024-03-12-08:19:13&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Each log line starts with a local date and time token.&lt;/span&gt;
    &lt;span class="c1"&gt;// &lt;/span&gt;
    &lt;span class="c1"&gt;// On Ubuntu 22.10, calling UtcOffset::current_local_offset().unwrap() after non_blocking()&lt;/span&gt;
    &lt;span class="c1"&gt;// causes IndeterminateOffset error!!&lt;/span&gt;
    &lt;span class="c1"&gt;// &lt;/span&gt;
    &lt;span class="c1"&gt;// See also https://github.com/time-rs/time/pull/297.&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;OffsetTime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;//UtcOffset::current_local_offset().unwrap(),&lt;/span&gt;
        &lt;span class="n"&gt;utc_offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;format_description!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[year]-[month]-[day]-[hour]:[minute]:[second]"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've discussed local dates in some detail in &lt;a href="https://behainguyen.wordpress.com/2023/09/03/rust-baby-step-some-preliminary-look-at-date/" title="Rust: baby step -- some preliminary look at date."&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;🚀 Please note that this is a local date and time. In my time zone, Australian Eastern Standard Time (AEST), which is 10-11 hours ahead of UTC, the log file name for a log line that starts with &lt;code&gt;2024-03-12-08:19:13&lt;/code&gt; would actually be &lt;code&gt;log/2024-03-11.log&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module-tracing-level"&gt;&lt;/a&gt;&lt;br&gt;
⓸ Next, we attempt to define the &lt;a href="https://docs.rs/tracing/latest/tracing/struct.Level.html" title="tracing::Level"&gt;tracing::Level&lt;/a&gt; based on the environment variable &lt;code&gt;RUST_LOG&lt;/code&gt; discussed previously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Extracts tracing::Level from .env RUST_LOG, if there is any problem, &lt;/span&gt;
    &lt;span class="c1"&gt;// defaults to Level::DEBUG.&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var_os&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RUST_LOG"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="nf"&gt;.to_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💥 &lt;strong&gt;&lt;em&gt;I initially assumed that having &lt;code&gt;RUST_LOG&lt;/code&gt; defined in the environment file &lt;code&gt;.env&lt;/code&gt; would suffice. However, it turns out that we need to explicitly set it in the code.&lt;/em&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module-create-subscriber"&gt;&lt;/a&gt;&lt;br&gt;
⓹ We then proceed to &lt;em&gt;“create a subscriber”&lt;/em&gt;, I hope I'm using the correct terminology:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tracing_subscriber&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;Layer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;.with_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.with_ansi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.with_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;non_blocking_appender&lt;/span&gt;&lt;span class="nf"&gt;.with_max_level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="nf"&gt;.with_max_level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fn.registry.html" title="Function tracing_subscriber::registry()"&gt;tracing_subscriber::registry()&lt;/a&gt; returns a &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/registry/struct.Registry.html" title="Struct tracing_subscriber::registry::Registry"&gt;tracing_subscriber::registry::Registry&lt;/a&gt; struct. This struct implements the trait &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.SubscriberExt.html" title="Trait tracing_subscriber::layer::SubscriberExt"&gt;tracing_subscriber::layer::SubscriberExt&lt;/a&gt;. The method &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.SubscriberExt.html#method.with" title="fn with&amp;lt;L&amp;gt;(self, layer: L) -&amp;gt; Layered&amp;lt;L, Self&amp;gt;"&gt;fn with&amp;lt;L&amp;gt;(self, layer: L) -&amp;gt; Layered&amp;lt;L, Self&amp;gt;&lt;/a&gt; from this trait returns a &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/struct.Layered.html" title="Struct tracing_subscriber::layer::Layered"&gt;tracing_subscriber::layer::Layered&lt;/a&gt; struct, which is a: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;a href="https://docs.rs/tracing-core/0.1.32/tracing_core/subscriber/trait.Subscriber.html" title="Trait tracing_core::subscriber::Subscriber"&gt;Subscriber&lt;/a&gt; composed of a &lt;code&gt;Subscriber&lt;/code&gt; wrapped by one or more &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html" title="Trait tracing_subscriber::layer::Layer"&gt;Layer&lt;/a&gt;s.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We create the new &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html" title="Trait tracing_subscriber::layer::Layer"&gt;Layer&lt;/a&gt; using &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/struct.Layer.html" title="Struct tracing_subscriber::fmt::Layer"&gt;tracing_subscriber::fmt::Layer&lt;/a&gt; implementation.&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;non_blocking_appender&lt;/code&gt; is an instance of &lt;a href="https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.NonBlocking.html" title="Struct tracing_appender::non_blocking::NonBlocking"&gt;tracing_appender::non_blocking::NonBlocking&lt;/a&gt; struct. This struct implements the trait &lt;a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/writer/trait.MakeWriterExt.html" title="Trait tracing_subscriber::fmt::writer::MakeWriterExt"&gt;tracing_subscriber::fmt::writer::MakeWriterExt&lt;/a&gt;, where the method &lt;a href="https://docs.rs/tracing-subscriber/0.3.18/tracing_subscriber/fmt/writer/trait.MakeWriterExt.html#method.with_max_level" title="fn with_max_level(self, level: Level) -&amp;gt; WithMaxLevel&amp;lt;Self&amp;gt;"&gt;fn with_max_level(self, level: Level) -&amp;gt; WithMaxLevel&amp;lt;Self&amp;gt;&lt;/a&gt; is defined.&lt;/p&gt;

&lt;p&gt;🚀 &lt;code&gt;.and(std::io::stdout.with_max_level(level))&lt;/code&gt; means that anything logged to the log file will also be printed to the console.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module-register-subscriber"&gt;&lt;/a&gt;&lt;br&gt;
⓺ Next, the new &lt;a href="https://docs.rs/tracing-core/0.1.32/tracing_core/subscriber/trait.Subscriber.html" title="Trait tracing_core::subscriber::Subscriber"&gt;Subscriber&lt;/a&gt; is set as the global default for the duration of the entire program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// tracing::subscriber::set_global_default(subscriber) can only be called once. &lt;/span&gt;
    &lt;span class="c1"&gt;// Subsequent calls raise SetGlobalDefaultError, ignore these errors.&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// There are integeration test methods which call this init_app_logger(...) repeatedly!!&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;set_global_default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Logger set_global_default, ignored: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documentation for the function &lt;a href="https://docs.rs/tracing/latest/tracing/subscriber/fn.set_global_default.html" title="Function tracing::subscriber::set_global_default"&gt;tracing::subscriber::set_global_default&lt;/a&gt; states: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can only be set once; subsequent attempts to set the global default will fail. Returns whether the initialization was successful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since some integration test methods call the &lt;code&gt;pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard&lt;/code&gt; more than once, we catch potential errors and ignore them. &lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-logger-module-return"&gt;&lt;/a&gt;&lt;br&gt;
⓻ Finally, &lt;code&gt;pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard&lt;/code&gt; returns &lt;code&gt;log_guard&lt;/code&gt;, as discussed above.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-main-module"&gt;&lt;/a&gt;&lt;br&gt;
❻ Updates to the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/src/main.rs" title="src/main.rs"&gt;src/main.rs&lt;/a&gt; module.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-main-module-worker-guard"&gt;&lt;/a&gt;&lt;br&gt;
⓵ Coming back to &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/src/helper/app_logger.rs#L40" title="src/helper/app_logger.rs pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard"&gt;pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard&lt;/a&gt;, specifically regarding the returned value discussed previously, I read and understood the quoted documentation, and I believe the code was correct. However, it did not write to log files as expected. I &lt;a href="https://users.rust-lang.org/t/actix-web-tracing-subscriber-does-not-write-to-log-file-from-other-modules/107986/5" title="Actix-web tracing-subscriber does not write to log file from other modules"&gt;sought help&lt;/a&gt;. As per my help request post, I initially called &lt;code&gt;init_app_logger&lt;/code&gt; in the &lt;code&gt;src/lib.rs&lt;/code&gt; module's &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/lib.rs#L126" title="src/lib.rs pub async fn run(listener: TcpListener) -&amp;gt; Result&amp;lt;Server, std::io::Error&amp;gt;"&gt;pub async fn run(listener: TcpListener) -&amp;gt; Result&amp;lt;Server, std::io::Error&amp;gt;&lt;/a&gt;. Consequently, as soon as &lt;code&gt;run&lt;/code&gt; went of scope, the returned &lt;code&gt;WorkerGuard&lt;/code&gt; was dropped, and the writer thread terminated.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-main-module-worker-guard-init-app-logger"&gt;&lt;/a&gt;&lt;br&gt;
Simply moved it to &lt;code&gt;src/main.rs&lt;/code&gt;'s &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/src/main.rs#L21" title="src/main.rs async fn main() -&amp;gt; Result&amp;lt;(), std::io::Error&amp;gt;"&gt;async fn main() -&amp;gt; Result&amp;lt;(), std::io::Error&amp;gt;&lt;/a&gt;, fixed this problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Call this to load RUST_LOG.&lt;/span&gt;
    &lt;span class="nf"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.ok&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 

    &lt;span class="c1"&gt;// Calling UtcOffset::current_local_offset().unwrap() here works in Ubuntu 22.10, i.e.,&lt;/span&gt;
    &lt;span class="c1"&gt;// it does not raise the IndeterminateOffset error.&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// TO_DO. But this does not guarantee that it will always work! &lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_guards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;init_app_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;UtcOffset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;current_local_offset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Please note the call&lt;/strong&gt; &lt;code&gt;UtcOffset::current_local_offset().unwrap()&lt;/code&gt;. This is due to the problem discussed in the section 💥 Issue with calculating UTC time offset on Ubuntu 22.10.&lt;/p&gt;

&lt;p&gt;&lt;a id="the-app-main-module-dotenv-ok"&gt;&lt;/a&gt;&lt;br&gt;
⓶ The function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/src/helper/app_logger.rs#L40" title="src/helper/app_logger.rs pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard"&gt;pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard&lt;/a&gt; requires the environment variable &lt;code&gt;RUST_LOG&lt;/code&gt; as discussed previously. That's why &lt;code&gt;dotenv().ok()&lt;/code&gt; is called in &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/src/main.rs#L14" title="src/main.rs async fn main() -&amp;gt; Result&amp;lt;(), std::io::Error&amp;gt;"&gt;async fn main() -&amp;gt; Result&amp;lt;(), std::io::Error&amp;gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recall that &lt;code&gt;dotenv().ok()&lt;/code&gt; is also called in the &lt;code&gt;src/lib.rs&lt;/code&gt; module's &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/lib.rs#L127" title="src/lib.rs pub async fn run(listener: TcpListener) -&amp;gt; Result&amp;lt;Server, std::io::Error&amp;gt;"&gt;pub async fn run(listener: TcpListener) -&amp;gt; Result&amp;lt;Server, std::io::Error&amp;gt;&lt;/a&gt; to load other environment variables. &lt;em&gt;This setup might seem clunky, but I haven't found a better solution yet!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="update-integration-tests"&gt;&lt;/a&gt;&lt;br&gt;
❼ Updating integration tests. We want integration tests to be able to log as well. These updates are made solely in the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/tests/common.rs" title="tests/common.rs module"&gt;tests/common.rs&lt;/a&gt; module.&lt;/p&gt;

&lt;p&gt;The function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/tests/common.rs#L57" title="tests/common.rs function pub async fn spawn_app() -&amp;gt; TestApp"&gt;pub async fn spawn_app() -&amp;gt; TestApp&lt;/a&gt; in &lt;code&gt;tests/common.rs&lt;/code&gt; calls the &lt;code&gt;src/lib.rs&lt;/code&gt; module's function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/lib.rs#L127" title="src/lib.rs pub async fn run(listener: TcpListener) -&amp;gt; Result&amp;lt;Server, std::io::Error&amp;gt;"&gt;pub async fn run(listener: TcpListener) -&amp;gt; Result&amp;lt;Server, std::io::Error&amp;gt;&lt;/a&gt; to create application server instances. &lt;/p&gt;

&lt;p&gt;This means that &lt;code&gt;spawn_app()&lt;/code&gt; must be refactored to call &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/src/helper/app_logger.rs#L40" title="src/helper/app_logger.rs pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard"&gt;pub fn init_app_logger(utc_offset: time::UtcOffset) -&amp;gt; WorkerGuard&lt;/a&gt; and somehow keep the writer thread alive after &lt;code&gt;spawn_app()&lt;/code&gt; goes out of scope. We manage this by:&lt;/p&gt;

&lt;p&gt;⓵ Update &lt;code&gt;TestApp&lt;/code&gt; struct by adding &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/tests/common.rs#L45" title="TestApp pub guard: WorkerGuard"&gt;pub guard: WorkerGuard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;⓶ Update the function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d0005d87b4df15c775daa5167da3fc07c6935f59/tests/common.rs#L57" title="tests/common.rs function pub async fn spawn_app() -&amp;gt; TestApp"&gt;pub async fn spawn_app() -&amp;gt; TestApp&lt;/a&gt; with additional calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// To load RUST_LOG from .env file.&lt;/span&gt;
    &lt;span class="nf"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.ok&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 

    &lt;span class="cm"&gt;/*
    On Ubuntu 22.10, calling UtcOffset's offset methods causes IndeterminateOffset error!!

    See also https://github.com/time-rs/time/pull/297

    ...
    */&lt;/span&gt;

    &lt;span class="c1"&gt;// TO_DO: 11 is the current number of hours the Australian Eastern Standard Time (AEST)&lt;/span&gt;
    &lt;span class="c1"&gt;// is ahead of UTC. This value need to be worked out dynamically -- if it is at all &lt;/span&gt;
    &lt;span class="c1"&gt;// possible on Linux!!&lt;/span&gt;
    &lt;span class="c1"&gt;// &lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;init_app_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;UtcOffset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_hms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="update-integration-tests-utcoffset-problem"&gt;&lt;/a&gt;&lt;br&gt;
Note the call &lt;code&gt;UtcOffset::from_hms(11, 0, 0).unwrap()&lt;/code&gt;. This is due to the problem discussed in section 💥 Issue with calculating UTC time offset on Ubuntu 22.10:&lt;/p&gt;

&lt;p&gt;-- 👎 Unlike &lt;a id="the-app-main-module-worker-guard-init-app-logger"&gt;src/main.rs&lt;/a&gt;, where &lt;code&gt;UtcOffset::current_local_offset().unwrap()&lt;/code&gt; works, calling it here consistently results in the &lt;a href="https://docs.rs/time/latest/time/error/struct.IndeterminateOffset.html" title="IndeterminateOffset"&gt;IndeterminateOffset&lt;/a&gt; error! &lt;code&gt;UtcOffset::from_hms(11, 0, 0).unwrap()&lt;/code&gt; works, but again, this is not a guarantee it will keep working.&lt;/p&gt;

&lt;p&gt;👎 &lt;strong&gt;The value 11 is hardcoded.&lt;/strong&gt; Presently, the Australian Eastern Standard Time (AEST) zone is 11 hours ahead of UTC. To get the AEST date and time, we need to offset UTC by 11 hours. However, 11 is not a constant value; due to daylight savings, in Southern Hemisphere winters, it changes to 10 hours (I think). This means that this code will no longer be correct.&lt;/p&gt;

&lt;p&gt;&lt;a id="concluding-remarks"&gt;&lt;/a&gt;&lt;br&gt;
❽ We've reached the conclusion of this post. I'd like to mention that the ecosystem surrounding tracing and logging is incredibly vast. While this post only scratches the surface, it provides a complete working example nonetheless. We can build upon this foundation as needed.&lt;/p&gt;

&lt;p&gt;The UTC offset issue on Ubuntu 22.10, as described, must be addressed definitively. However, that task is for another day.&lt;/p&gt;

&lt;p&gt;I'm not entirely satisfied with the numerous debug loggings from other crates. These can be filtered and removed, but that's a topic for another post, perhaps.&lt;/p&gt;

&lt;p&gt;I hope you find the information useful. Thank you for reading. And stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rust-lang.org/"&gt;https://www.rust-lang.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/"&gt;https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
🦀 &lt;a href="https://github.com/behai-nguyen/rust_web_01" title="Index of the Complete Series"&gt;Index of the Complete Series&lt;/a&gt;.
&lt;/h3&gt;

</description>
      <category>rust</category>
      <category>actix</category>
      <category>logging</category>
      <category>tracing</category>
    </item>
    <item>
      <title>Rust: actix-web JSON Web Token authentication.</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Tue, 27 Feb 2024 02:06:01 +0000</pubDate>
      <link>https://forem.com/behainguyen/rust-actix-web-json-web-token-authentication-2f1b</link>
      <guid>https://forem.com/behainguyen/rust-actix-web-json-web-token-authentication-2f1b</guid>
      <description>&lt;p&gt;&lt;em&gt;In the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/" title="Rust: simple actix-web email-password login and request authentication using middleware."&gt;sixth&lt;/a&gt; post of our &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web"&gt;actix-web&lt;/a&gt; learning application, we implemented a basic email-password login process with a placeholder for a &lt;code&gt;token&lt;/code&gt;. In this post, we will implement a comprehensive JSON Web Token (JWT)-based authentication system. We will utilise the &lt;a href="https://docs.rs/jsonwebtoken/latest/jsonwebtoken/index.html" title="jsonwebtoken"&gt;jsonwebtoken&lt;/a&gt; crate, which we have &lt;a href="https://behainguyen.wordpress.com/2023/11/20/rust-json-web-token-some-investigative-studies-on-crate-jsonwebtoken/" title="Rust: JSON Web Token -- some investigative studies on crate jsonwebtoken"&gt;previously studied&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Please note,&lt;/strong&gt; complete code for this post can be downloaded from GitHub with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.10.0 https://github.com/behai-nguyen/rust_web_01.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web"&gt;actix-web&lt;/a&gt; learning application mentioned above has been discussed in the following nine previous posts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/10/18/rust-web-application-mysql-server-sqlx-actix-web-and-tera/" title="Rust web application: MySQL server, sqlx, actix-web and tera"&gt;Rust web application: MySQL server, sqlx, actix-web and tera&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/11/26/rust-learning-actix-web-middleware-01/" title="Rust: learning actix-web middleware 01"&gt;Rust: learning actix-web middleware 01&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/12/31/rust-retrofit-integration-tests-to-an-existing-actix-web-application/" title="Rust: retrofit integration tests to an existing actix-web application."&gt;Rust: retrofit integration tests to an existing actix-web application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/03/rust-adding-actix-session-and-actix-identity-to-an-existing-actix-web-application/" title="Rust: adding actix-session and actix-identity to an existing actix-web application."&gt;Rust: adding actix-session and actix-identity to an existing actix-web application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/14/rust-actix-web-endpoints-which-accept-both-application-x-www-form-urlencoded-and-application-json-content-types/" title="Rust: actix-web endpoints which accept both application/x-www-form-urlencoded and application/json content types."&gt;Rust: actix-web endpoints which accept both &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; and &lt;code&gt;application/json&lt;/code&gt; content types&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/" title="Rust: simple actix-web email-password login and request authentication using middleware."&gt;Rust: simple actix-web email-password login and request authentication using middleware&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/02/10/rust-actix-web-get-ssl-https-for-localhost/" title="Rust: actix-web get SSL/HTTPS for localhost."&gt;Rust: actix-web get SSL/HTTPS for localhost&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/02/13/rust-actix-web-cors-cookies-and-ajax-calls/" title="Rust: actix-web CORS, Cookies and AJAX calls."&gt;Rust: actix-web CORS, Cookies and AJAX calls&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/02/16/rust-actix-web-global-extractor-error-handlers/" title="Rust: actix-web global extractor error handlers"&gt;Rust: actix-web global extractor error handlers&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code we're developing in this post is a continuation of the code from the &lt;a href="https://behainguyen.wordpress.com/2024/02/16/rust-actix-web-global-extractor-error-handlers/" title="Rust: actix-web global extractor error handlers"&gt;ninth&lt;/a&gt; post above. 🚀 To get the code of this &lt;a href="https://behainguyen.wordpress.com/2024/02/16/rust-actix-web-global-extractor-error-handlers/" title="Rust: actix-web global extractor error handlers"&gt;ninth&lt;/a&gt; post, please use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.9.0 https://github.com/behai-nguyen/rust_web_01.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;-- Note the tag &lt;code&gt;v0.9.0&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;Table of contents&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Previous Studies on JSON Web Token (JWT)
&lt;/li&gt;
&lt;li&gt;
Proposed JWT Implementations: Problems and Solutions
&lt;ul&gt;
&lt;li&gt;
Proposed JWT Implementations
&lt;/li&gt;
&lt;li&gt;
Problems with the Proposed Implementations
&lt;ul&gt;
&lt;li&gt;
Problems when Used as an API-Server or Service                 
&lt;/li&gt;
&lt;li&gt;
Problems when Used as an Application Server                    
&lt;/li&gt;
&lt;/ul&gt;               




&lt;/li&gt;

&lt;li&gt;

Proposed Solutions
&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

The “Bearer” Token Scheme
&lt;/li&gt;

&lt;li&gt;

Project Layout
&lt;/li&gt;

&lt;li&gt;

The Token Utility jwt_utils.rs and Test test_jsonwebtoken.rs Modules

&lt;ul&gt;
&lt;li&gt;
The Token Utility src/helper/jwt_utils.rs Module
&lt;/li&gt;
&lt;li&gt;
The Test tests/test_jsonwebtoken.rs Module
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;

&lt;li&gt;

The Updated Login Process
&lt;/li&gt;

&lt;li&gt;

The Updated Request Authentication Process

&lt;ul&gt;
&lt;li&gt;
Code Updated in the src/auth_middleware.rs Module
&lt;/li&gt;
&lt;li&gt;
Code Updated in the src/lib.rs Module
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;

&lt;li&gt;

JWT and Logout
&lt;/li&gt;

&lt;li&gt;

Updating Integration Tests
&lt;/li&gt;

&lt;li&gt;

Concluding Remarks
&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  &lt;a id="previous-studies-jwt"&gt;Previous Studies on JSON Web Token (JWT)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier, we conducted studies on the &lt;a href="https://docs.rs/jsonwebtoken/latest/jsonwebtoken/index.html" title="jsonwebtoken"&gt;jsonwebtoken&lt;/a&gt; crate, as detailed in the post titled &lt;a href="https://behainguyen.wordpress.com/2023/11/20/rust-json-web-token-some-investigative-studies-on-crate-jsonwebtoken/" title="Rust: JSON Web Token -- some investigative studies on crate jsonwebtoken"&gt;Rust: JSON Web Token -- some investigative studies on crate jsonwebtoken&lt;/a&gt;. The JWT implementation in this post is based on the specifications discussed in the &lt;a href="https://behainguyen.wordpress.com/2023/11/20/rust-json-web-token-some-investigative-studies-on-crate-jsonwebtoken/#the-second-example" title="Rust: JSON Web Token -- some investigative studies on crate jsonwebtoken"&gt;second example&lt;/a&gt; of the aforementioned post, particularly focusing on this specification:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 &lt;strong&gt;It should be obvious that: &lt;em&gt;this implementation implies &lt;code&gt;SECONDS_VALID_FOR&lt;/code&gt; is the duration the token stays valid since last active&lt;/em&gt;&lt;/strong&gt;. It does not mean that after this duration, the token becomes invalid or expired. So long as the client keeps sending requests while the token is valid, it will never expire!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will provide further details on this specification later in the post. Additionally, before &lt;a href="https://behainguyen.wordpress.com/2023/11/20/rust-json-web-token-some-investigative-studies-on-crate-jsonwebtoken/" title="Rust: JSON Web Token -- some investigative studies on crate jsonwebtoken"&gt;studying the jsonwebtoken crate&lt;/a&gt;, we conducted research on the &lt;a href="https://docs.rs/jwt-simple/latest/jwt_simple/" title="jwt-simple"&gt;jwt-simple&lt;/a&gt; crate, as discussed in the post titled &lt;a href="https://behainguyen.wordpress.com/2023/11/17/rust-json-web-token-some-investigative-studies-on-crate-jwt-simple/" title="Rust: JSON Web Token -- some investigative studies on crate jwt-simple"&gt;Rust: JSON Web Token -- some investigative studies on crate jwt-simple&lt;/a&gt;. It would be beneficial to review this post as well, as it covers background information on JWT.&lt;/p&gt;

&lt;h3&gt;
  &lt;a id="jwt-implementations"&gt;Proposed JWT Implementations: Problems and Solutions&lt;/a&gt;
&lt;/h3&gt;

&lt;h4&gt;
  &lt;a id="proposed-jwt-impl"&gt;Proposed JWT Implementations&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Let's revisit the specifications outlined in the previous section:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 &lt;strong&gt;It should be obvious that: &lt;em&gt;this implementation implies &lt;code&gt;SECONDS_VALID_FOR&lt;/code&gt; is the duration the token stays valid since last active&lt;/em&gt;&lt;/strong&gt;. It does not mean that after this duration, the token becomes invalid or expired. So long as the client keeps sending requests while the token is valid, it will never expire!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This concept involves extending the expiry time of a valid token every time a request is made. This functionality was demonstrated in the original discussion, specifically in the &lt;a href="https://behainguyen.wordpress.com/2023/11/20/rust-json-web-token-some-investigative-studies-on-crate-jsonwebtoken/#the-second-example" title="Rust: JSON Web Token -- some investigative studies on crate jsonwebtoken"&gt;second example&lt;/a&gt; section mentioned earlier.&lt;/p&gt;

&lt;p&gt;🦀 &lt;strong&gt;&lt;em&gt;Since the expiry time is updated, we generate a new &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt;. &lt;/em&gt;&lt;/strong&gt; Here's what we do with the new token:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Replace the current &lt;a href="https://docs.rs/actix-identity/0.7.0/actix_identity/struct.Identity.html" title="actix-identity Identity"&gt;actix-identity::Identity&lt;/a&gt; login with the new &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always&lt;/strong&gt; send the new &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; to clients via both the response header and the response cookie &lt;code&gt;authorization&lt;/code&gt;, as in the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#the-login-process-api-login" title="login process"&gt;login process&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We generate a new &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; based on logic, but it doesn't necessarily mean the previous ones have expired."&lt;/p&gt;

&lt;h4&gt;
  &lt;a id="proposed-jwt-impl-problems"&gt;Problems with the Proposed Implementations&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The proposed implementations outlined above present some practical challenges, which we will discuss next. &lt;/p&gt;

&lt;p&gt;&lt;span&gt;However, for the sake of learning in this project, we will proceed with the proposed implementations despite the identified issues.&lt;/span&gt;&lt;/p&gt;

&lt;h5&gt;
  &lt;a id="proposed-jwt-impl-problems-api-server"&gt;Problems when Used as an API-Server or Service&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;In an &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-api-server" title="API-like server or a service"&gt;&lt;code&gt;API-like server&lt;/code&gt; or a &lt;code&gt;service&lt;/code&gt;&lt;/a&gt;, users are required to include a valid &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; in the request &lt;code&gt;authorization&lt;/code&gt; header. Therefore, if a new token is generated, users should have access to this latest token.&lt;/p&gt;

&lt;p&gt;&lt;a id="proposed-jwt-impl-problems-api-server-problems"&gt;&lt;/a&gt;&lt;br&gt;
What happens if users simply ignore the new tokens and continue using a previous one that has not yet expired? In such a scenario, &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-request-auth" title="request authentication"&gt;&lt;code&gt;request authentication&lt;/code&gt;&lt;/a&gt; would still be successful, and the requests would potentially succeed until the old token expires. However, a more serious concern arises if we implement blacklisting. In that case, we would need to blacklist all previous tokens. This would necessitate writing the current access token to a blacklist table for every request, which is impractical.&lt;/p&gt;

&lt;h5&gt;
  &lt;a id="proposed-jwt-impl-problems-app-server"&gt;Problems when Used as an Application Server&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;When used as an &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-app-server" title="application server"&gt;&lt;code&gt;application server&lt;/code&gt;&lt;/a&gt;, we simply replace the current &lt;a href="https://docs.rs/actix-identity/0.7.0/actix_identity/struct.Identity.html" title="actix-identity Identity"&gt;actix-identity::Identity&lt;/a&gt; login with the new &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt;. If we implement blacklisting, we only need to blacklist the last token&lt;/p&gt;

&lt;p&gt;🚀 This process makes sense, as we cannot expire a session while a user is still actively using it.&lt;/p&gt;

&lt;p&gt;&lt;a id="proposed-jwt-impl-problems-app-server-problems"&gt;&lt;/a&gt; However, we still encounter similar problems as described in the previous section for &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-api-server" title="API-like server or a service"&gt;&lt;code&gt;API-like servers&lt;/code&gt; or &lt;code&gt;services&lt;/code&gt;&lt;/a&gt;. Since clients always have access to the &lt;code&gt;authorization&lt;/code&gt; response header and cookie, they can use this token with different client tools to send requests, effectively treating the application as an &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-api-server" title="API-like server or a service"&gt;&lt;code&gt;API-like server&lt;/code&gt; or a &lt;code&gt;service&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  &lt;a id="proposed-jwt-impl-solutions"&gt;Proposed Solutions&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The above problems would disappear, and the actual implementations would be simpler if we adjust the logic slightly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Only&lt;/strong&gt; send the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; to clients &lt;strong&gt;once if the content type of the login request is &lt;code&gt;application/json&lt;/code&gt;&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;
Then users of an &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-api-server" title="API-like server or a service"&gt;&lt;code&gt;API-like server&lt;/code&gt; or a &lt;code&gt;service&lt;/code&gt;&lt;/a&gt; will only have one &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; until it expires. They will need to log in again to obtain a new token.
&lt;/li&gt;
&lt;li&gt;
Still replace the current &lt;a href="https://docs.rs/actix-identity/0.7.0/actix_identity/struct.Identity.html" title="actix-identity Identity"&gt;actix-identity::Identity&lt;/a&gt; login with the new &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt;. The &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-app-server" title="application server"&gt;&lt;code&gt;application server&lt;/code&gt;&lt;/a&gt; continues to function as usual. However, since users no longer have access to the token, we only need to manage the one stored in the &lt;a href="https://docs.rs/actix-identity/0.7.0/actix_identity/struct.Identity.html" title="actix-identity Identity"&gt;actix-identity::Identity&lt;/a&gt; login.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;span&gt;&lt;br&gt;
But as mentioned at the start of this section, we will ignore the problems and, therefore, the solutions for this revision of the code.&lt;br&gt;
&lt;/span&gt;&lt;/p&gt;

&lt;h3&gt;
  &lt;a id="bearer-token"&gt;The “Bearer” Token Scheme&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We adhere to the “Bearer” token scheme as specified in &lt;a href="https://datatracker.ietf.org/doc/html/rfc6750" title="The OAuth 2.0 Authorization Framework: Bearer Token Usage"&gt;RFC 6750&lt;/a&gt;, section &lt;a href="https://datatracker.ietf.org/doc/html/rfc6750#page-5" title="2.1. Authorization Request Header Field"&gt;2.1. Authorization Request Header Field&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    For example:
        GET /resource HTTP/1.1
        Host: server.example.com
        Authorization: Bearer mF_9.B5f-4.1JqM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is, the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; used during &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-request-auth" title="request authentication"&gt;&lt;code&gt;request authentication&lt;/code&gt;&lt;/a&gt; is in the format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bearer. + the proper JSON Web Token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bearer.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImNoaXJzdGlhbi5rb2JsaWNrLjEwMDA0QGdtYWlsLmNvbSIsImlhdCI6MTcwODU1OTcwNywiZXhwIjoxNzA4NTYxNTA3LCJsYXN0X2FjdGl2ZSI6MTcwODU1OTcwN30.CN-whQ0rWW8IuLPVTF7qprk4-GgtK1JSJqp3C8X-ytE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❶ The &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; included in the request &lt;code&gt;authorization&lt;/code&gt; header must adhere to the "Bearer" token format.&lt;/p&gt;

&lt;p&gt;❷ Similarly, the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; set for the &lt;a href="https://docs.rs/actix-identity/0.7.0/actix_identity/struct.Identity.html" title="actix-identity Identity"&gt;actix-identity::Identity&lt;/a&gt; login is also a "Bearer" token.&lt;/p&gt;

&lt;p&gt;🦀 However, the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; sent to clients via the response header and the response cookie &lt;code&gt;authorization&lt;/code&gt; is always a pure JSON Web Token.&lt;/p&gt;

&lt;h3&gt;
  &lt;a id="project-layout"&gt;Project Layout&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Below is the complete project layout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-- Please note,&lt;/strong&gt; those marked with &lt;span&gt;★&lt;/span&gt; are updated, and those marked with &lt;span&gt;☆&lt;/span&gt; are new.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout-chart"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── .env ★
├── Cargo.toml ★
├── cert
│ ├── cert-pass.pem
│ ├── key-pass-decrypted.pem
│ └── key-pass.pem
├── migrations
│ ├── mysql
│ │ └── migrations
│ │     ├── 20231128234321_emp_email_pwd.down.sql
│ │     └── 20231128234321_emp_email_pwd.up.sql
│ └── postgres
│     └── migrations
│         ├── 20231130023147_emp_email_pwd.down.sql
│         └── 20231130023147_emp_email_pwd.up.sql
├── README.md ★
├── src
│ ├── auth_handlers.rs ★
│ ├── auth_middleware.rs ★
│ ├── bh_libs
│ │ ├── api_status.rs
│ │ └── australian_date.rs
│ ├── bh_libs.rs
│ ├── config.rs ★
│ ├── database.rs
│ ├── handlers.rs
│ ├── helper
│ │ ├── app_utils.rs ★
│ │ ├── constants.rs ★
│ │ ├── endpoint.rs ★
│ │ ├── jwt_utils.rs ☆
│ │ └── messages.rs ★
│ ├── helper.rs ★
│ ├── lib.rs ★
│ ├── main.rs
│ ├── middleware.rs
│ └── models.rs ★
├── templates
│ ├── auth
│ │ ├── home.html
│ │ └── login.html
│ └── employees.html
└── tests
    ├── common.rs ★
    ├── test_auth_handlers.rs ★
    ├── test_handlers.rs ★
    └── test_jsonwebtoken.rs ☆
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  &lt;a id="token-utility-and-test"&gt;The Token Utility jwt_utils.rs and Test test_jsonwebtoken.rs Modules&lt;/a&gt;
&lt;/h3&gt;

&lt;h4&gt;
  &lt;a id="token-utility-and-test-module"&gt;The Token Utility src/helper/jwt_utils.rs Module&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;In the module &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs" title="src/helper/app_utils.rs"&gt;src/helper/jwt_utils.rs&lt;/a&gt;, we implement all the JWT management code, which includes the core essential code that somewhat repeats the code already mentioned in the &lt;a href="https://behainguyen.wordpress.com/2023/11/20/rust-json-web-token-some-investigative-studies-on-crate-jsonwebtoken/#the-second-example" title="Rust: JSON Web Token -- some investigative studies on crate jsonwebtoken"&gt;second example&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L25" title="src/helper/app_utils.rs JWTPayload"&gt; &lt;code&gt;struct JWTPayload&lt;/code&gt;&lt;/a&gt; -- represents the JWT payload, where the &lt;code&gt;email&lt;/code&gt; field uniquely identifies the logged-in user.
&lt;/li&gt;

&lt;li&gt;
&lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L64" title="src/helper/app_utils.rs impl JWTPayload"&gt;&lt;code&gt;JWTPayload implementation&lt;/code&gt;&lt;/a&gt; -- implements some of the required functions and methods:
&lt;ul&gt;
&lt;li&gt;
A function to create a new instance.
&lt;/li&gt;
&lt;li&gt;
Methods to update the expiry field (&lt;code&gt;exp&lt;/code&gt;) and the &lt;code&gt;last_active&lt;/code&gt; field using seconds, minutes, and hours.
&lt;/li&gt;
&lt;li&gt;
Four getter methods which return the values of the &lt;code&gt;iat&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;exp&lt;/code&gt;, and &lt;code&gt;last_active&lt;/code&gt; fields.
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Additionally, there are two main functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L187" title="src/helper/app_utils.rs pub fn make_token"&gt;&lt;code&gt;pub fn make_token&lt;/code&gt;&lt;/a&gt; -- creates a new JWT from an &lt;code&gt;email&lt;/code&gt;. The parameter &lt;code&gt;secs_valid_for&lt;/code&gt; indicates how many seconds the token is valid for, and the parameter &lt;code&gt;secret_key&lt;/code&gt; is used by the &lt;a href="https://docs.rs/jsonwebtoken/latest/jsonwebtoken/index.html" title="jsonwebtoken"&gt;jsonwebtoken&lt;/a&gt; crate to encode the token. It creates an instance of &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L25" title="src/helper/app_utils.rs JWTPayload"&gt;&lt;code&gt;struct JWTPayload&lt;/code&gt;&lt;/a&gt;, and then creates a token using this instance.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L262" title="src/helper/app_utils.rs pub fn decode_token"&gt;&lt;code&gt;pub fn decode_token&lt;/code&gt;&lt;/a&gt; -- decodes a given token. If the token is valid and successfully decoded, it returns the token's &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L25" title="src/helper/app_utils.rs JWTPayload"&gt;&lt;code&gt;struct JWTPayload&lt;/code&gt;&lt;/a&gt;. Otherwise, it returns an &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/e5fa751f1454bf3ffe3ad72a7c70c6169402bfcb/src/bh_libs/api_status.rs#L18" title="src/bh_libs/api_status.rs ApiStatus"&gt;&lt;code&gt;ApiStatus&lt;/code&gt;&lt;/a&gt; which describes the error.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Other functions are “convenient” functions or wrapper functions:
&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L217" title="src/helper/app_utils.rs pub fn make_token_from_payload"&gt;&lt;code&gt;pub fn make_token_from_payload&lt;/code&gt;&lt;/a&gt; -- creates a JWT from an instance of struct &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L25" title="src/helper/app_utils.rs JWTPayload"&gt;&lt;code&gt;struct JWTPayload&lt;/code&gt;&lt;/a&gt;. It is a "convenient" function. We decode the current token, update the extracted payload, then call this function to create an updated token.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/helper/jwt_utils.rs#L242" title="src/helper/app_utils.rs pub fn make_bearer_token"&gt;&lt;code&gt;pub fn make_bearer_token&lt;/code&gt;&lt;/a&gt; -- a wrapper function that creates a “Bearer” token from a given token.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L309" title="src/helper/app_utils.rs pub fn decode_bearer_token"&gt;&lt;code&gt;pub fn decode_bearer_token&lt;/code&gt;&lt;/a&gt; -- a wrapper function that decodes a “Bearer” token.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please note also the unit test section in this module. There are sufficient tests to cover all functions and methods.&lt;/p&gt;

&lt;p&gt;The documentation in the source code should be sufficient to aid in the reading of the code.&lt;/p&gt;

&lt;h4&gt;
  &lt;a id="token-utility-and-test-test"&gt;The Test tests/test_jsonwebtoken.rs Module&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;We implement some integration tests for JWT management code. These tests are self-explanatory.&lt;/p&gt;

&lt;h3&gt;
  &lt;a id="updated-login-process"&gt;The Updated Login Process&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In the current &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#the-login-process-api-login" title="login process"&gt;login process&lt;/a&gt;, at &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#the-login-process-api-login-step-four" title="login process step 4"&gt;step 4&lt;/a&gt;, we note:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// TO_DO: Work in progress -- future implementations will formalise access token.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;selected_login&lt;/span&gt;&lt;span class="py"&gt;.email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// https://docs.rs/actix-identity/latest/actix_identity/&lt;/span&gt;
    &lt;span class="c1"&gt;// Attach a verified user identity to the active session&lt;/span&gt;
    &lt;span class="nn"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="nf"&gt;.extensions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part of the login process handler &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/auth_handlers.rs#L388" title="The login process handler"&gt;&lt;code&gt;pub async fn login(request: HttpRequest, app_state: web::Data&amp;lt;super::AppState&amp;gt;, body: Bytes) -&amp;gt; Either&amp;lt;impl Responder, HttpResponse&amp;gt;&lt;/code&gt;&lt;/a&gt; is updated to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;selected_login&lt;/span&gt;&lt;span class="py"&gt;.email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;app_state&lt;/span&gt;&lt;span class="py"&gt;.cfg.jwt_secret_key&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;app_state&lt;/span&gt;&lt;span class="py"&gt;.cfg.jwt_mins_valid_for&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// https://docs.rs/actix-identity/latest/actix_identity/&lt;/span&gt;
    &lt;span class="c1"&gt;// Attach a verified user identity to the active session&lt;/span&gt;
    &lt;span class="nn"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="nf"&gt;.extensions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;make_bearer_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note the call to &lt;code&gt;make_bearer_token&lt;/code&gt;, which adheres to The “Bearer” Token Scheme.&lt;/p&gt;

&lt;p&gt;This update would take care of the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-app-server" title="application server"&gt;&lt;code&gt;application server&lt;/code&gt;&lt;/a&gt; case. In the case of an &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-api-server" title="API-like server or a service"&gt;&lt;code&gt;API-like server&lt;/code&gt; or a &lt;code&gt;service&lt;/code&gt;&lt;/a&gt;, users are required to include a valid &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; in the request &lt;code&gt;authorization&lt;/code&gt; header, as mentioned, so we don't need to do anything.&lt;/p&gt;

&lt;p&gt;The next task is to update the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-request-auth" title="request authentication"&gt;&lt;code&gt;request authentication&lt;/code&gt;&lt;/a&gt; process. This update occurs in the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs" title="src/auth_middleware.rs"&gt;src/auth_middleware.rs&lt;/a&gt; and the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/lib.rs" title="src/lib.rs"&gt;src/lib.rs&lt;/a&gt; modules.&lt;/p&gt;

&lt;h3&gt;
  &lt;a id="updated-request-auth-process"&gt;The Updated Request Authentication Process&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The updated request &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-request-auth" title="request authentication"&gt;&lt;code&gt;request authentication&lt;/code&gt;&lt;/a&gt; involves changes to both the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs" title="src/auth_middleware.rs"&gt;src/auth_middleware.rs&lt;/a&gt; and &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/lib.rs" title="src/lib.rs"&gt;src/lib.rs&lt;/a&gt; modules.&lt;/p&gt;

&lt;p&gt;This section, &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#request-authentication-works" title="How the Request Authentication Process Works"&gt;How the Request Authentication Process Works&lt;/a&gt;, describes the current process.&lt;/p&gt;

&lt;h4&gt;
  &lt;a id="updated-request-auth-process-mw"&gt;Code Updated in the src/auth_middleware.rs Module&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Please recall that the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs" title="src/auth_middleware.rs"&gt;src/auth_middleware.rs&lt;/a&gt; module serves as the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-request-auth-middleware" title="Request authentication middleware"&gt;&lt;code&gt;request authentication middleware&lt;/code&gt;&lt;/a&gt;. We will make some substantial updates within this module.&lt;/p&gt;

&lt;p&gt;Although the code has sufficient documentation, we will discuss the updates in the following sections.&lt;/p&gt;

&lt;p&gt;&lt;a id="updated-request-auth-process-mw-module-doc"&gt;&lt;/a&gt;&lt;br&gt;
⓵ The module documentation has been updated to describe how the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-request-auth" title="request authentication"&gt;&lt;code&gt;request authentication&lt;/code&gt;&lt;/a&gt; process works with JWT. Please refer to the documentation section &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/auth_middleware.rs#L21" title="How This Middleware Works"&gt;How This Middleware Works&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;&lt;a id="updated-request-auth-process-mw-tokenstatus"&gt;&lt;/a&gt;&lt;br&gt;
⓶ New &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L84" title="src/auth_middleware.rs struct TokenStatus"&gt;&lt;code&gt;struct TokenStatus&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;TokenStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;is_logged_in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JWTPayload&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiStatus&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L84" title="src/auth_middleware.rs struct TokenStatus"&gt;&lt;code&gt;struct TokenStatus&lt;/code&gt;&lt;/a&gt; represents the status of the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; for the current request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
When there is no token, &lt;code&gt;is_logged_in&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt; to indicate that the request comes from an &lt;strong&gt;un&lt;/strong&gt;&lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated session"&gt;&lt;code&gt;authenticated session&lt;/code&gt;&lt;/a&gt;. The other two fields are set to &lt;code&gt;None&lt;/code&gt;, indicating that there is no error.
&lt;/li&gt;
&lt;li&gt;
When there is a token, we call the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L262" title="pub fn decode_token(token: &amp;amp;str, secret_key: &amp;amp;[u8]) -&amp;gt; Result&amp;lt;JWTPayload, ApiStatus&amp;gt;"&gt;&lt;code&gt;pub fn decode_token(token: &amp;amp;str, secret_key: &amp;amp;[u8]) -&amp;gt; Result&amp;lt;JWTPayload, ApiStatus&amp;gt;&lt;/code&gt;&lt;/a&gt; function:
&lt;ul&gt;
&lt;li&gt;
If token decoding fails or the token has already expired, &lt;code&gt;is_logged_in&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt;, and &lt;code&gt;api_status&lt;/code&gt; is set to the returned &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/e5fa751f1454bf3ffe3ad72a7c70c6169402bfcb/src/bh_libs/api_status.rs#L18" title="src/bh_libs/api_status.rs ApiStatus"&gt;&lt;code&gt;ApiStatus&lt;/code&gt;&lt;/a&gt;. This indicates an error.
&lt;/li&gt;
&lt;li&gt;
If token decoding succeeds, &lt;code&gt;is_logged_in&lt;/code&gt; is set to to &lt;code&gt;true&lt;/code&gt;, and &lt;code&gt;payload&lt;/code&gt; is set to the returned &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L25" title="src/helper/app_utils.rs JWTPayload"&gt;&lt;code&gt;JWTPayload&lt;/code&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a id="updated-request-auth-process-mw-token-verify-fn"&gt;&lt;/a&gt;&lt;br&gt;
⓷ The function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L167" title="fn verify_valid_access_token(request: &amp;amp;ServiceRequest) -&amp;gt; TokenStatus"&gt;&lt;code&gt;fn verify_valid_access_token(request: &amp;amp;ServiceRequest) -&amp;gt; TokenStatus&lt;/code&gt;&lt;/a&gt; has been completely rewritten, although its purpose remains the same. It checks if the token is present and, if so, decodes it.&lt;/p&gt;

&lt;p&gt;The return value of this function is &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L84" title="src/auth_middleware.rs struct TokenStatus"&gt;&lt;code&gt;struct TokenStatus&lt;/code&gt;&lt;/a&gt;, whose fields are set based on the rules discussed previously.&lt;/p&gt;

&lt;p&gt;&lt;a id="updated-request-auth-process-mw-token-updated-set"&gt;&lt;/a&gt;&lt;br&gt;
⓸ The new helper function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L206" title="fn update_and_set_updated_token(request: &amp;amp;ServiceRequest, token_status: TokenStatus)"&gt;&lt;code&gt;fn update_and_set_updated_token(request: &amp;amp;ServiceRequest, token_status: TokenStatus)&lt;/code&gt;&lt;/a&gt; is called when there is a token and the token is successfully decoded.&lt;/p&gt;

&lt;p&gt;It uses the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L25" title="src/helper/app_utils.rs JWTPayload"&gt;&lt;code&gt;JWTPayload&lt;/code&gt;&lt;/a&gt; instance in the &lt;code&gt;token_status&lt;/code&gt; parameter to create the updated &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt;. Then, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Replaces the current &lt;a href="https://docs.rs/actix-identity/0.7.0/actix_identity/struct.Identity.html" title="actix-identity Identity"&gt;actix-identity::Identity&lt;/a&gt; login with the new updated token, as discussed earlier.
&lt;/li&gt;
&lt;a id="updated-request-auth-process-mw-token-updated-set-next-adhoc-mw"&gt;&lt;/a&gt;
&lt;li&gt;
Attaches the updated token to &lt;a href="https://docs.rs/actix-web/latest/actix_web/dev/struct.ServiceRequest.html" title="dev::ServiceRequest"&gt;dev::ServiceRequest&lt;/a&gt;'s &lt;a href="https://docs.rs/actix-web/latest/actix_web/dev/struct.Extensions.html" title="dev::Extensions"&gt;dev::Extensions&lt;/a&gt; by calling &lt;a href="https://docs.rs/actix-web/latest/actix_web/dev/struct.ServiceRequest.html#method.extensions_mut" title="fn extensions_mut(&amp;amp;self) -&amp;gt; RefMut&amp;lt;'_, Extensions&amp;gt;"&gt;fn extensions_mut(&amp;amp;self) -&amp;gt; RefMut&amp;lt;'_, Extensions&amp;gt;&lt;/a&gt;.

The next adhoc middleware, discussed in the next section, consumes this extension.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="updated-request-auth-process-mw-unauth-closure"&gt;&lt;/a&gt;&lt;br&gt;
⓹ The new closure, &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L329" title="let unauthorised_token = |req: ServiceRequest, api_status: ApiStatus| -&amp;gt; Self::Future"&gt; &lt;code&gt;let unauthorised_token = |req: ServiceRequest, api_status: ApiStatus| -&amp;gt; Self::Future&lt;/code&gt;&lt;/a&gt;, calls the &lt;a href="https://docs.rs/actix-web/latest/actix_web/struct.HttpResponse.html#method.Unauthorized" title="the Unauthorized() method"&gt;Unauthorized()&lt;/a&gt; method on &lt;a href="https://docs.rs/actix-web/latest/actix_web/struct.HttpResponse.html" title="HttpResponse"&gt;HttpResponse&lt;/a&gt; to return a JSON serialisation of &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/e5fa751f1454bf3ffe3ad72a7c70c6169402bfcb/src/bh_libs/api_status.rs#L18" title="src/bh_libs/api_status.rs ApiStatus"&gt;&lt;code&gt;ApiStatus&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note the calls to remove the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#notes-on-cookies-our-own" title="server-side per-request cookies"&gt;server-side per-request&lt;/a&gt; cookies &lt;code&gt;redirect-message&lt;/code&gt; and &lt;code&gt;original-content-type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="updated-request-auth-process-mw-call-method"&gt;&lt;/a&gt;&lt;br&gt;
⓺ Update the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L329" title="fn call(&amp;amp;self, request: ServiceRequest) -&amp;gt; Self::Future"&gt;&lt;code&gt;fn call(&amp;amp;self, request: ServiceRequest) -&amp;gt; Self::Future&lt;/code&gt;&lt;/a&gt; function. All groundwork has been completed. The updates to this method are fairly straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Update the call to &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L167" title="fn verify_valid_access_token(request: &amp;amp;ServiceRequest) -&amp;gt; TokenStatus"&gt;&lt;code&gt;fn verify_valid_access_token(request: &amp;amp;ServiceRequest) -&amp;gt; TokenStatus&lt;/code&gt;&lt;/a&gt;; the return value is now &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L84" title="src/auth_middleware.rs struct TokenStatus"&gt;&lt;code&gt;struct TokenStatus&lt;/code&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
If the token is in error, call the closure &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L329" title="let unauthorised_token = |req: ServiceRequest, api_status: ApiStatus| -&amp;gt; Self::Future"&gt;&lt;code&gt;unauthorised_token()&lt;/code&gt;&lt;/a&gt; to return the error response. The request is then completed.
&lt;/li&gt;
&lt;li&gt;
If the request is from an &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-authenticated-session" title="authenticated session"&gt;&lt;code&gt;authenticated session&lt;/code&gt;&lt;/a&gt;, meaning we have a token, and the token has been decoded successfully, we make an additional call to the new helper function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/auth_middleware.rs#L206" title="fn update_and_set_updated_token(request: &amp;amp;ServiceRequest, token_status: TokenStatus)"&gt;&lt;code&gt;fn update_and_set_updated_token(request: &amp;amp;ServiceRequest, token_status: TokenStatus)&lt;/code&gt;&lt;/a&gt;, which has been described in the previous section.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The core logic of this method remains unchanged.&lt;/p&gt;

&lt;h4&gt;
  &lt;a id="updated-request-auth-process-lib"&gt;Code Updated in the src/lib.rs Module&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;As mentioned previously, if a valid token is present, an updated token is generated from the current token's payload every time a request occurs. This updated &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; is then sent to the client via both the response header and the response cookie &lt;code&gt;authorization&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This section describes how the updated token is attached to the request extension so that the next adhoc middleware can pick it up and send it to the clients.&lt;/p&gt;

&lt;p&gt;This is the updated &lt;code&gt;src/lib.rs&lt;/code&gt; &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/lib.rs#L156" title="src/lib.rs token forwarding adhoc middleware"&gt;next adhoc middleware&lt;/a&gt;. Its functionality is straightforward. It queries the current &lt;a href="https://docs.rs/actix-web/latest/actix_web/dev/struct.ServiceRequest.html" title="dev::ServiceRequest"&gt;dev::ServiceRequest&lt;/a&gt;'s &lt;a href="https://docs.rs/actix-web/latest/actix_web/dev/struct.Extensions.html" title="dev::Extensions"&gt;dev::Extensions&lt;/a&gt; for a &lt;a href="https://doc.rust-lang.org/std/string/struct.String.html" title="Struct std::string::String"&gt;String&lt;/a&gt;, which represents the updated token. If found, it sets the &lt;a href="https://docs.rs/actix-web/latest/actix_web/dev/struct.ServiceResponse.html" title="ServiceResponse"&gt;ServiceResponse&lt;/a&gt; &lt;code&gt;authorization&lt;/code&gt; header and cookie with this updated token.&lt;/p&gt;

&lt;p&gt;Afterward, it forwards the response. Since it is currently the last middleware in the call stack, the response will be sent directly to the client, completing the request.&lt;/p&gt;

&lt;h3&gt;
  &lt;a id="jwt-and-logout"&gt;JWT and Logout&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Due to the issues outlined in this section and this section, we were unable to effectively implement the logout functionality in the application. This will remain unresolved until we implement the proposed solutions and integrate blacklisting.&lt;/p&gt;

&lt;p&gt;-- For the time being, we will retain the current logout process unchanged.&lt;/p&gt;

&lt;p&gt;Once blacklisting is implemented, the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-request-auth" title="request authentication"&gt;&lt;code&gt;request authentication&lt;/code&gt;&lt;/a&gt; process will need to validate the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/#definition-access-token" title="access token"&gt;&lt;code&gt;access token&lt;/code&gt;&lt;/a&gt; against the blacklist table. If the token is found in the blacklist, it will be considered invalid.&lt;/p&gt;

&lt;h3&gt;
  &lt;a id="updated-integration-tests"&gt;Updating Integration Tests&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;There is a new integration test module as already discussed in section The Test tests/test_jsonwebtoken.rs Module. There is no new integration test added to existing modules.&lt;/p&gt;

&lt;p&gt;Some common test code has been updated as a result of implementing JSON Web Token.&lt;/p&gt;

&lt;p&gt;⓵ There are several updates in module &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs" title="tests/common.rs"&gt;tests/common.rs&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs#L43" title="pub fn mock_access_token(&amp;amp;self, secs_valid_for: u64) -&amp;gt; String"&gt; &lt;code&gt;pub fn mock_access_token(&amp;amp;self, secs_valid_for: u64) -&amp;gt; String&lt;/code&gt;&lt;/a&gt; now returns a correctly formatted “Bearer” token. Please note the new parameter &lt;code&gt;secs_valid_for&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
New function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs#L29" title="pub fn jwt_secret_key() -&amp;gt; String"&gt;&lt;code&gt;pub fn jwt_secret_key() -&amp;gt; String&lt;/code&gt;&lt;/a&gt; 
&lt;/li&gt;
&lt;li&gt;
New function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs#L180" title="pub fn assert_token_email(token: &amp;amp;str, email: &amp;amp;str)"&gt;&lt;code&gt;pub fn assert_token_email(token: &amp;amp;str, email: &amp;amp;str)&lt;/code&gt;&lt;/a&gt;. It decodes the parameter &lt;code&gt;token&lt;/code&gt;, which is expected to always succeed, then tests that the token &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/src/helper/jwt_utils.rs#L25" title="src/helper/app_utils.rs JWTPayload"&gt;&lt;code&gt;JWTPayload&lt;/code&gt;&lt;/a&gt;'s &lt;code&gt;email&lt;/code&gt; value equal to parameter &lt;code&gt;email&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
Rewrote &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs#L188" title="pub fn assert_access_token_in_header(response: &amp;amp;reqwest::Response, email: &amp;amp;str)"&gt;&lt;code&gt;pub fn assert_access_token_in_header(response: &amp;amp;reqwest::Response, email: &amp;amp;str)&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs#L198" title="pub fn assert_access_token_in_cookie(response: &amp;amp;reqwest::Response, email: &amp;amp;str)"&gt;&lt;code&gt;pub fn assert_access_token_in_cookie(response: &amp;amp;reqwest::Response, email: &amp;amp;str)&lt;/code&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
Updated &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs#L188" title="pub async fn assert_json_successful_login(response: reqwest::Response, email: &amp;amp;str)"&gt;&lt;code&gt;pub async fn assert_json_successful_login(response: reqwest::Response, email: &amp;amp;str)&lt;/code&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⓶ Some minor changes in both the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/test_handlers.rs" title="tests/test_handlers.rs"&gt;tests/test_handlers.rs&lt;/a&gt; and the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/test_auth_handlers.rs" title="tests/test_auth_handlers.rs"&gt;tests/test_auth_handlers.rs&lt;/a&gt; modules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Call the function &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs#L43" title="pub fn mock_access_token(&amp;amp;self, secs_valid_for: u64) -&amp;gt; String"&gt;&lt;code&gt;pub fn mock_access_token(&amp;amp;self, secs_valid_for: u64) -&amp;gt; String&lt;/code&gt;&lt;/a&gt; with the new parameter &lt;code&gt;secs_valid_for&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
Other updates as a result of the updates in the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/4b7c3acf8af1f18f99553e7a728f05d9493fb885/tests/common.rs" title="tests/common.rs"&gt;tests/common.rs&lt;/a&gt; module.
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  &lt;a id="concluding-remarks"&gt;Concluding Remarks&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;It has been an interesting process for me as I delved into the world of &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web"&gt;actix-web&lt;/a&gt; adhoc middleware. While the code may seem simple at first glance, I encountered some problems along the way and &lt;a href="https://users.rust-lang.org/t/actix-web-an-application-wide-adhoc-middleware-to-set-a-response-header-for-all-routes/107207" title="sought assistance"&gt;sought assistance&lt;/a&gt; to overcome them.&lt;/p&gt;

&lt;p&gt;I anticipated the problems, as described in this section and this section, before diving into the actual coding process. Despite the hurdles, I proceeded with the implementation because I wanted to learn how to set a custom header for all routes before their final response is sent to clients – that's the essence of adhoc middleware.&lt;/p&gt;

&lt;p&gt;In a future post, I plan to implement the proposed solutions and explore the concept of blacklisting.&lt;/p&gt;

&lt;p&gt;I hope you find this post informative and helpful. Thank you for reading. And stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rust-lang.org/"&gt;https://www.rust-lang.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/"&gt;https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>actix</category>
      <category>json</category>
      <category>jwt</category>
    </item>
    <item>
      <title>Rust: actix-web get SSL/HTTPS for localhost.</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Sat, 10 Feb 2024 05:58:39 +0000</pubDate>
      <link>https://forem.com/behainguyen/rust-actix-web-get-sslhttps-for-localhost-2199</link>
      <guid>https://forem.com/behainguyen/rust-actix-web-get-sslhttps-for-localhost-2199</guid>
      <description>&lt;p&gt;&lt;em&gt;We are going to enable our &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web" rel="noopener noreferrer"&gt;actix-web&lt;/a&gt; learning application to run under &lt;code&gt;HTTPS&lt;/code&gt;. As a result, we need to do some minor refactoring to existing integration tests. We also move and rename an existing module for better code organisation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Please note,&lt;/strong&gt; complete code for this post can be downloaded from GitHub with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git clone -b v0.7.0 https://github.com/behai-nguyen/rust_web_01.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web" rel="noopener noreferrer"&gt;actix-web&lt;/a&gt; learning application mentioned above has been discussed in the following six previous posts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/10/18/rust-web-application-mysql-server-sqlx-actix-web-and-tera/" title="Rust web application: MySQL server, sqlx, actix-web and tera" rel="noopener noreferrer"&gt;Rust web application: MySQL server, sqlx, actix-web and tera&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/11/26/rust-learning-actix-web-middleware-01/" title="Rust: learning actix-web middleware 01" rel="noopener noreferrer"&gt;Rust: learning actix-web middleware 01&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/12/31/rust-retrofit-integration-tests-to-an-existing-actix-web-application/" title="Rust: retrofit integration tests to an existing actix-web application." rel="noopener noreferrer"&gt;Rust: retrofit integration tests to an existing actix-web application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/03/rust-adding-actix-session-and-actix-identity-to-an-existing-actix-web-application/" title="Rust: adding actix-session and actix-identity to an existing actix-web application." rel="noopener noreferrer"&gt;Rust: adding actix-session and actix-identity to an existing actix-web application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/14/rust-actix-web-endpoints-which-accept-both-application-x-www-form-urlencoded-and-application-json-content-types/" title="Rust: actix-web endpoints which accept both application/x-www-form-urlencoded and application/json content types." rel="noopener noreferrer"&gt;Rust: actix-web endpoints which accept both &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; and &lt;code&gt;application/json&lt;/code&gt; content types&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/" title="Rust: simple actix-web email-password login and request authentication using middleware." rel="noopener noreferrer"&gt;Rust: simple actix-web email-password login and request authentication using middleware&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code we're developing in this post is a continuation of the code from the &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/" title="Rust: simple actix-web email-password login and request authentication using middleware." rel="noopener noreferrer"&gt;sixth&lt;/a&gt; post above. 🚀 To get the code of this &lt;a href="https://behainguyen.wordpress.com/2024/01/28/rust-simple-actix-web-email-password-login-and-request-authentication-using-middleware/" title="Rust: simple actix-web email-password login and request authentication using middleware." rel="noopener noreferrer"&gt;sixth&lt;/a&gt; post, please use the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git clone -b v0.6.0 https://github.com/behai-nguyen/rust_web_01.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;-- Note the tag &lt;code&gt;v0.6.0&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;Table of contents&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Define “to run under &lt;code&gt;HTTPS&lt;/code&gt;”&lt;/li&gt;
&lt;li&gt;Project Layout&lt;/li&gt;
&lt;li&gt;
The OpenSSL Toolkit
&lt;ul&gt;
&lt;li&gt;Installation&lt;/li&gt;
&lt;li&gt;Windows 10, the Rust Analyzer plug-in and the OpenSSL Toolkit environment variable &lt;code&gt;OPENSSL_DIR&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;li&gt;Generate a Self-Signed Encrypted Private Key and a Certificate&lt;/li&gt;


&lt;li&gt;Code Refactoring to Enable &lt;code&gt;HTTPS&lt;/code&gt;
&lt;/li&gt;


&lt;li&gt;

Refactor Integration Tests

&lt;ul&gt;
&lt;li&gt;The tests/common.rs Module&lt;/li&gt;
&lt;li&gt;The tests/test_handlers.rs and tests/test_auth_handlers.rs Modules&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;li&gt;Moving src/utils.rs to src/bh_libs/australian_date.rs&lt;/li&gt;


&lt;li&gt;Concluding Remarks&lt;/li&gt;


&lt;/ul&gt;

&lt;p&gt;&lt;a id="define-running-under-https"&gt;&lt;/a&gt;&lt;br&gt;
❶ To run under &lt;code&gt;HTTPS&lt;/code&gt;. That is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

https://localhost:5000/ui/login
https://192.168.0.16:5000/ui/login


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a id="project-layout"&gt;&lt;/a&gt;&lt;br&gt;
❷ Project Layout.&lt;/p&gt;

&lt;p&gt;This post introduces a self-signed encrypted private key file and a certificate file. The updated directory layout for the project is listed below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-- Please note,&lt;/strong&gt; those marked with &lt;span&gt;★&lt;/span&gt; are updated, and those marked with &lt;span&gt;☆&lt;/span&gt; are new.&lt;/p&gt;

&lt;p&gt;&lt;a id="project-layout-chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.
├── Cargo.toml ★
├── cert
│ ├── cert-pass.pem ☆ -- Self-signed encrypted private key
│ └── key-pass.pem ☆ -- Certificate
├── .env
├── migrations
│ ├── mysql
│ │ └── migrations
│ │     ├── 20231128234321_emp_email_pwd.down.sql
│ │     └── 20231128234321_emp_email_pwd.up.sql
│ └── postgres
│     └── migrations
│         ├── 20231130023147_emp_email_pwd.down.sql
│         └── 20231130023147_emp_email_pwd.up.sql
├── README.md ★
├── src
│ ├── auth_handlers.rs
│ ├── auth_middleware.rs
│ ├── bh_libs
│ │ ├── api_status.rs
│ │ └── australian_date.rs ★
│ ├── bh_libs.rs ★
│ ├── config.rs
│ ├── database.rs
│ ├── handlers.rs
│ ├── helper
│ │ ├── app_utils.rs ★
│ │ ├── constants.rs
│ │ ├── endpoint.rs
│ │ └── messages.rs
│ ├── helper.rs
│ ├── lib.rs ★
│ ├── main.rs
│ ├── middleware.rs
│ └── models.rs ★
├── templates
│ ├── auth
│ │ ├── home.html
│ │ └── login.html
│ └── employees.html
└── tests
    ├── common.rs ★
    ├── test_auth_handlers.rs ★
    └── test_handlers.rs ★


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a id="openssl-toolkit"&gt;&lt;/a&gt;&lt;br&gt;
❸ In this post, we are using the &lt;a href="https://www.openssl.org/" title="Open SSL Cryptography and SSL/TLS Toolkit" rel="noopener noreferrer"&gt;OpenSSL Cryptography and SSL/TLS Toolkit&lt;/a&gt; to generate the self-signed encrypted private key and the certificate files.&lt;/p&gt;

&lt;p&gt;&lt;a id="openssl-toolkit-installation"&gt;&lt;/a&gt;&lt;br&gt;
⓵ We have previously discussed its installation on both Windows 10 Pro and Ubuntu 22.10 in &lt;a href="https://behainguyen.wordpress.com/2023/10/10/rust-sqlx-cli-database-migration-with-mysql-and-postgresql/#sqlx-cli-openssl" title="Rust SQLx CLI: database migration with MySQL and PostgreSQL | SQLx CLI Installation Requires OpenSSL" rel="noopener noreferrer"&gt;this section&lt;/a&gt; of another post.&lt;/p&gt;

&lt;p&gt;&lt;a id="win10-openssl-rust-analyzer-env-var"&gt;&lt;/a&gt;&lt;br&gt;
⓶ 💥 On Windows 10 Pro, I have observed that, once we include the &lt;a href="https://docs.rs/openssl/latest/openssl/" title="Crate openssl" rel="noopener noreferrer"&gt;openssl&lt;/a&gt; crate, we should set the environment variable &lt;code&gt;OPENSSL_DIR&lt;/code&gt; at the system level, otherwise the &lt;a href="https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer" title="Rust Analyzer" rel="noopener noreferrer"&gt;Rust Analyzer&lt;/a&gt; Visual Studio Code plug-in would run into trouble.&lt;/p&gt;

&lt;p&gt;The environment variable &lt;code&gt;OPENSSL_DIR&lt;/code&gt; indicates where OpenSSL has been installed. For example, &lt;code&gt;C:\Program Files\OpenSSL-Win64&lt;/code&gt;. Following are the steps to access Windows 10 Pro environment variable setting dialog.&lt;/p&gt;

&lt;p&gt;Right click on &lt;strong&gt;This PC&lt;/strong&gt; ➜ &lt;strong&gt;Properties&lt;/strong&gt; ➜ &lt;strong&gt;Advance system settings&lt;/strong&gt; (right hand side) ➜ &lt;strong&gt;Advanced&lt;/strong&gt; tab ➜ &lt;strong&gt;Environment Variables...&lt;/strong&gt; button ➜ under &lt;strong&gt;System variables&lt;/strong&gt; ➜ &lt;strong&gt;New...&lt;/strong&gt; ➜ enter variable name and value in the dialog ➜ &lt;strong&gt;OK&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;The screenshot below is a brief visual representation of the above steps, including the environment variable &lt;code&gt;OPENSSL_DIR&lt;/code&gt; in place:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-01.png" alt="097-01.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We might need to restart Visual Studio Code to get the new setting recognised.&lt;/p&gt;

&lt;p&gt;&lt;a id="generate-key-and-certificate"&gt;&lt;/a&gt;&lt;br&gt;
❹ Generate the self-signed encrypted private key and the certificate files using the &lt;a href="https://www.openssl.org/" title="Open SSL Cryptography and SSL/TLS Toolkit" rel="noopener noreferrer"&gt;OpenSSL Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The OpenSSL command to generate the files will prompt a series of questions. One important question is the &lt;code&gt;Common Name&lt;/code&gt; which is the server name or &lt;code&gt;FQDN&lt;/code&gt; where the certificate is going to be used. If we are not yet familiar with this process, this &lt;a href="https://www.hostinger.com/tutorials/fqdn" title="FQDN (Fully Qualified Domain Name): What It Is, Examples, and More" rel="noopener noreferrer"&gt;FQDN (Fully Qualified Domain Name): What It Is, Examples, and More&lt;/a&gt; article would be an essential reading, in my humble opnion.&lt;/p&gt;

&lt;p&gt;I did seek help working on this issue, please see &lt;a href="https://users.rust-lang.org/t/actix-web-openssl-ssl-https-for-localhost-is-it-possible-please/106444" title="Actix-web OpenSSL SSL/HTTPS for Localhost, is it possible please?" rel="noopener noreferrer"&gt;Actix-web OpenSSL SSL/HTTPS for Localhost, is it possible please?&lt;/a&gt; And I was pointed to &lt;a href="https://github.com/actix/examples/tree/master/https-tls/openssl" title="examples/https-tls/openssl/" rel="noopener noreferrer"&gt;examples/https-tls/openssl/&lt;/a&gt; -- and this is my primary source of reference for getting this learning application to run under &lt;code&gt;HTTPS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The command I choose to use is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ openssl req -x509 -newkey rsa:4096 -keyout key-pass.pem -out cert-pass.pem -sha256 -days 365


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Be prepared, we will be asked the following questions:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Enter PEM pass pharse: 

Country Name (2 letter code) [AU]: 
State or Province Name (full name) [Some-State]: 
Locality Name (eg, city) []: Melbourne
Organization Name (eg, company) [Internet Widgits Pty Ltd]: 
Organizational Unit Name (eg, section) []: 
Common Name (e.g. server FQDN or YOUR name) []: 
Email Address []: 

Please enter the following 'extra' attributes
to be sent with your certificate request

A challenge password []: 
An optional company name []: 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Both &lt;code&gt;key-pass.pem&lt;/code&gt; and &lt;code&gt;cert-pass.pem&lt;/code&gt; are in the &lt;code&gt;cert/&lt;/code&gt; sub-directory as seen in the Project Layout section.&lt;/p&gt;

&lt;p&gt;💥 Please note I also use these two files on Windows 10 Pro to run the application. It works, I am not sure why yet. I need to keep an eye out for this.&lt;/p&gt;

&lt;p&gt;&lt;a id="refactor-code-to-enable-https"&gt;&lt;/a&gt;&lt;br&gt;
❺ Code refactoring to enable &lt;code&gt;HTTPS&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We are also taking the code from &lt;a href="https://github.com/actix/examples/tree/master/https-tls/openssl" title="examples/https-tls/openssl/" rel="noopener noreferrer"&gt;examples/https-tls/openssl/&lt;/a&gt;. In &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/src/lib.rs" title="src/lib.rs" rel="noopener noreferrer"&gt;src/lib.rs&lt;/a&gt;, we add two private functions &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/src/lib.rs#L35" title='fn load_encrypted_private_key() -&amp;gt; PKey&amp;lt;Private&amp;gt;" target="_blank"&amp;gt;&amp;lt;code&amp;gt;fn load_encrypted_private_key() -&amp;gt; PKey&amp;lt;Private&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; and &amp;lt;a href=' rel="noopener noreferrer"&gt;HttpServer&lt;/a&gt; object, we call &lt;a href="https://docs.rs/actix-web/latest/actix_web/struct.HttpServer.html#method.listen_openssl" title="Method listen_openssl(...)" rel="noopener noreferrer"&gt;method listen_openssl(...)&lt;/a&gt; instead of &lt;a href="https://docs.rs/actix-web/latest/actix_web/struct.HttpServer.html#method.listen" title="Method listen(...)" rel="noopener noreferrer"&gt;method listen(...)&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="nf"&gt;.listen_openssl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;ssl_builder&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I have tested with the latest version of the following browsers: &lt;strong&gt;Firefox&lt;/strong&gt;, &lt;strong&gt;Chrome&lt;/strong&gt;, &lt;strong&gt;Edge&lt;/strong&gt;, &lt;strong&gt;Opera&lt;/strong&gt;, &lt;strong&gt;Brave&lt;/strong&gt; and &lt;strong&gt;Vivaldi&lt;/strong&gt;, for both:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

https://localhost:5000/ui/login
https://192.168.0.16:5000/ui/login


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We might get a warning of &lt;span&gt; &lt;code&gt;potential security risk...&lt;/code&gt;&lt;/span&gt; For example, see the &lt;strong&gt;Firefox&lt;/strong&gt; warning in the below screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-02.png" alt="097-02.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I just ignore the warning and choose to go ahead. Even though &lt;code&gt;https://&lt;/code&gt; works, but all mentioned browsers state that the connection is &lt;span&gt;not secure&lt;/span&gt;. Please see &lt;strong&gt;Firefox&lt;/strong&gt;, &lt;strong&gt;Chrome&lt;/strong&gt; and &lt;strong&gt;Opera&lt;/strong&gt; sample screenshots below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-03-firefox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-03-firefox.png" alt="097-03-firefox.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-03-chrome.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-03-chrome.png" alt="097-03-chrome.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-03-opera.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F02%2F097-03-opera.png" alt="097-03-opera.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="refactor-integration-tests"&gt;&lt;/a&gt;&lt;br&gt;
❻ We have to make changes to both integration tests common code and actual test code.&lt;/p&gt;

&lt;p&gt;&lt;a id="refactor-integration-tests-common"&gt;&lt;/a&gt;&lt;br&gt;
⓵ It's quite obvious that we should access the routes via &lt;code&gt;HTTPS&lt;/code&gt;. The first change would be &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/tests/common.rs#L27" title='pub async fn spawn_app() -&amp;gt; TestApp" target="_blank"&amp;gt;&amp;lt;code&amp;gt;pub async fn spawn_app() -&amp;gt; TestApp&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; in module &amp;lt;a href=' rel="noopener noreferrer"&gt;tests/common.rs&lt;/a&gt;. We should set the scheme of &lt;code&gt;app_url&lt;/code&gt; to &lt;code&gt;https://&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;TestApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;app_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://127.0.0.1:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;   


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I did run integration tests after making this change. They failed. Base on the error messages, it seems that &lt;a href="https://docs.rs/reqwest/latest/reqwest/struct.Client.html" title="reqwest::Client" rel="noopener noreferrer"&gt;reqwest::Client&lt;/a&gt; should “have” the certificate as well (?). &lt;/p&gt;

&lt;p&gt;Looking through the &lt;a href="https://docs.rs/reqwest/latest/reqwest/index.html" title="Crate reqwest" rel="noopener noreferrer"&gt;reqwest&lt;/a&gt; crate documentation, &lt;a href="https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.add_root_certificate" title="pub fn add_root_certificate(self, cert: Certificate) -&amp;gt; ClientBuilder" rel="noopener noreferrer"&gt;&lt;code&gt;pub fn add_root_certificate(self, cert: Certificate) -&amp;gt; ClientBuilder&lt;/code&gt;&lt;/a&gt; seems like a good candidate... &lt;/p&gt;

&lt;p&gt;Base on the example given in &lt;a href="https://docs.rs/reqwest/latest/reqwest/tls/struct.Certificate.html" title="reqwest::tls::Certificate" rel="noopener noreferrer"&gt;reqwest::tls::Certificate&lt;/a&gt;, I come up with &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/tests/common.rs#L42" title="pub fn load_certificate() -&amp;gt; Certificate" rel="noopener noreferrer"&gt;&lt;code&gt;pub fn load_certificate() -&amp;gt; Certificate&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/tests/common.rs#L72" title="pub fn reqwest_client() -&amp;gt; reqwest::Client" rel="noopener noreferrer"&gt;&lt;code&gt;pub fn reqwest_client() -&amp;gt; reqwest::Client&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have tried to document all my observations during developing these two helper functions. They are short and simple, I think the inline documentation explains the code quite sufficiently.&lt;/p&gt;

&lt;p&gt;-- Initially, &lt;code&gt;reqwest_client()&lt;/code&gt; does not include &lt;code&gt;.danger_accept_invalid_certs(true)&lt;/code&gt;, resulting in a certificate error. &lt;a href="https://stackoverflow.com/questions/76724036/how-to-resolve-a-rust-reqwest-error-invalid-certificate#comment135281495_76724036" title="Invalid certificate potential correct answer" rel="noopener noreferrer"&gt;This solution&lt;/a&gt;, provided in the following Stack Overflow thread titled &lt;a href="https://stackoverflow.com/questions/76724036/how-to-resolve-a-rust-reqwest-error-invalid-certificate" title="How to resolve a Rust Reqwest Error: Invalid Certificate" rel="noopener noreferrer"&gt;How to resolve a Rust Reqwest Error: Invalid Certificate&lt;/a&gt; suggests adding &lt;code&gt;.danger_accept_invalid_certs(true)&lt;/code&gt;, which appears to resolve the issue.&lt;/p&gt;

&lt;p&gt;&lt;a id="refactor-integration-tests-possible-certificate-issue"&gt;&lt;/a&gt;&lt;br&gt;
💥 Base on all evidences presented so far, including the &lt;code&gt;connection not secure&lt;/code&gt; warnings reported by browsers and the need to call &lt;code&gt;.danger_accept_invalid_certs(true)&lt;/code&gt; when creating a &lt;a href="https://docs.rs/reqwest/latest/reqwest/struct.Client.html" title="reqwest::Client" rel="noopener noreferrer"&gt;reqwest::Client&lt;/a&gt; instance, it seems to suggest that &lt;strong&gt;&lt;em&gt; there may still be an issue with this implementation. Or is it common for a self-signed certificate, which is not issued by a trusted certificate authority, to encounter such problems? &lt;/em&gt;&lt;/strong&gt; However, having the application run under &lt;code&gt;https://&lt;/code&gt; addresses issues I have had with cookies. For now, I will leave it as is. We will discuss cookie in another new post.&lt;/p&gt;

&lt;p&gt;&lt;a id="refactor-integration-tests-modules"&gt;&lt;/a&gt;&lt;br&gt;
⓶ In both integration test modules, &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/tests/test_handlers.rs" title="tests/test_handlers.rs" rel="noopener noreferrer"&gt;tests/test_handlers.rs&lt;/a&gt; and &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/tests/test_auth_handlers.rs" title="tests/test_auth_handlers.rs TO_DO" rel="noopener noreferrer"&gt;tests/test_auth_handlers.rs&lt;/a&gt;, we use the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/tests/common.rs#L72" title="pub fn reqwest_client() -&amp;gt; reqwest::Client" rel="noopener noreferrer"&gt;&lt;code&gt;pub fn reqwest_client() -&amp;gt; reqwest::Client&lt;/code&gt;&lt;/a&gt; function to create instances of &lt;a href="https://docs.rs/reqwest/latest/reqwest/struct.Client.html" title="reqwest::Client" rel="noopener noreferrer"&gt;reqwest::Client&lt;/a&gt; for testing purposes, instead of creating instances directly in each test method.&lt;/p&gt;

&lt;p&gt;&lt;a id="general-code-refactoring"&gt;&lt;/a&gt;&lt;br&gt;
❼ The final task of this post involves moving &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/d6d60d7d2e2bac2a0f1a626df50c0c24575dfd53/src/utils.rs" title="src/utils.rs" rel="noopener noreferrer"&gt;src/utils.rs&lt;/a&gt; to &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/86d2a4d902c5b8f585cea9407720dcc93fcf6a51/src/bh_libs/australian_date.rs" title="src/bh_libs/australian_date.rs" rel="noopener noreferrer"&gt;src/bh_libs/australian_date.rs&lt;/a&gt;, as it is a generic module, even though it depends on other third-party crates. It is possible that this module will be moved elsewhere again.&lt;/p&gt;

&lt;p&gt;The module &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/86d2a4d902c5b8f585cea9407720dcc93fcf6a51/src/bh_libs/australian_date.rs" title="src/bh_libs/australian_date.rs" rel="noopener noreferrer"&gt;src/bh_libs/australian_date.rs&lt;/a&gt; is generic enough to used as-is in other projects.&lt;/p&gt;

&lt;p&gt;As a result, the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/1738173addc7c4db0832ed7678358e0bae2a2a2d/src/models.rs#L%20TO_DO%20Employee%20struct" title="src/models.rs Employee struct" rel="noopener noreferrer"&gt;src/models.rs&lt;/a&gt; module is updated.&lt;/p&gt;

&lt;p&gt;&lt;a id="concluding-remarks"&gt;&lt;/a&gt;&lt;br&gt;
❽ We've reached the end of this post. I'd like to mention that I also followed the tutorial &lt;a href="https://hackernoon.com/how-to-get-sslhttps-for-localhost-i11s3342" title="How to Get SSL/HTTPS for Localhost" rel="noopener noreferrer"&gt;How to Get SSL/HTTPS for Localhost&lt;/a&gt;. I completed it successfully on Ubuntu 22.10, but browsers still warn about the connection not being secure. Perhaps this is to be expected with a self-signed certificate?&lt;/p&gt;

&lt;p&gt;Overall, it's been an interesting exercise. I hope you find the information in this post useful. Thank you for reading. And stay safe, as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper" rel="noopener noreferrer"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/" rel="noopener noreferrer"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;https://www.rust-lang.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/" rel="noopener noreferrer"&gt;https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>actix</category>
      <category>ssl</category>
      <category>https</category>
    </item>
    <item>
      <title>Rust: actix-web endpoints which accept both application/x-www-form-urlencoded and application/json content types.</title>
      <dc:creator>Be Hai Nguyen</dc:creator>
      <pubDate>Sun, 14 Jan 2024 02:01:31 +0000</pubDate>
      <link>https://forem.com/behainguyen/rust-actix-web-endpoints-which-accept-both-applicationx-www-form-urlencoded-and-applicationjson-content-types-1bdg</link>
      <guid>https://forem.com/behainguyen/rust-actix-web-endpoints-which-accept-both-applicationx-www-form-urlencoded-and-applicationjson-content-types-1bdg</guid>
      <description>&lt;p&gt;&lt;em&gt;We're implementing a login process for our &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web" rel="noopener noreferrer"&gt;actix-web&lt;/a&gt; learning application. We undertake some general updates to get ready to support login. We then implement a new &lt;code&gt;/api/login&lt;/code&gt; route, which supports both &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; and &lt;code&gt;application/json&lt;/code&gt; content types. In this post, we only implement deserialising the submitted request data, then echo some response. We also add a login page via route &lt;code&gt;/ui/login&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Please note,&lt;/strong&gt; complete code for this post can be downloaded from GitHub with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git clone -b v0.5.0 https://github.com/behai-nguyen/rust_web_01.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;a href="https://docs.rs/actix-web/latest/actix_web/" title="actix-web" rel="noopener noreferrer"&gt;actix-web&lt;/a&gt; learning application mentioned above has been discussed in the following four (4) previous posts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/10/18/rust-web-application-mysql-server-sqlx-actix-web-and-tera/" title="Rust web application: MySQL server, sqlx, actix-web and tera" rel="noopener noreferrer"&gt;Rust web application: MySQL server, sqlx, actix-web and tera&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/11/26/rust-learning-actix-web-middleware-01/" title="Rust: learning actix-web middleware 01" rel="noopener noreferrer"&gt;Rust: learning actix-web middleware 01&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2023/12/31/rust-retrofit-integration-tests-to-an-existing-actix-web-application/" title="Rust: retrofit integration tests to an existing actix-web application." rel="noopener noreferrer"&gt;Rust: retrofit integration tests to an existing actix-web application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://behainguyen.wordpress.com/2024/01/03/rust-adding-actix-session-and-actix-identity-to-an-existing-actix-web-application/" title="Rust: adding actix-session and actix-identity to an existing actix-web application." rel="noopener noreferrer"&gt;Rust: adding actix-session and actix-identity to an existing actix-web application&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code we're developing in this post is a continuation of the code from the &lt;a href="https://behainguyen.wordpress.com/2024/01/03/rust-adding-actix-session-and-actix-identity-to-an-existing-actix-web-application/" title="Rust: adding actix-session and actix-identity to an existing actix-web application." rel="noopener noreferrer"&gt;fourth&lt;/a&gt; post above. 🚀 To get the code of this &lt;a href="https://behainguyen.wordpress.com/2024/01/03/rust-adding-actix-session-and-actix-identity-to-an-existing-actix-web-application/" title="Rust: adding actix-session and actix-identity to an existing actix-web application." rel="noopener noreferrer"&gt;fourth&lt;/a&gt; post, please use the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git clone -b v0.4.0 https://github.com/behai-nguyen/rust_web_01.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;-- Note the tag &lt;code&gt;v0.4.0&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As already mentioned in the introduction above, in this post, our main focus of the login process is deserialising both &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; and &lt;code&gt;application/json&lt;/code&gt; into a &lt;code&gt;struct&lt;/code&gt; ready to support login. I struggle with this issue a little, I document it as part of my Rust learning journey.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="new-directory-layout"&gt;&lt;/a&gt;&lt;br&gt;
This post introduces a few new modules, some MySQL migration scripts, and a new login HTML page. The updated directory layout for the project is in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-01.png" alt="095-01.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Table of contents&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;❶ Update Rust to use latest actix-cors&lt;/li&gt;
&lt;li&gt;❷ Add new fields &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; to the &lt;code&gt;employees&lt;/code&gt; table&lt;/li&gt;
&lt;li&gt;❸ Update &lt;code&gt;src/models.rs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❹ New module &lt;code&gt;src/auth_handlers.rs&lt;/code&gt; which implements routes &lt;code&gt;/ui/login&lt;/code&gt; and &lt;code&gt;/api/login&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❺ Update &lt;code&gt;src/lib.rs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❻ The new &lt;code&gt;templates/auth/login.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
❼ Testing
&lt;ul&gt;
&lt;li&gt;⓵ &lt;code&gt;cargo test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;⓶ Manual tests&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;/ul&gt;

&lt;p&gt;&lt;a id="step-one-update-rust"&gt;&lt;/a&gt;&lt;br&gt;
❶ Update Rust to the latest version. At the time of this post, the latest version is &lt;code&gt;1.75.0&lt;/code&gt;. The command to update:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

▶️&amp;lt;code&amp;gt;Windows 10:&amp;lt;/code&amp;gt; rustup update
▶️&amp;lt;code&amp;gt;Ubuntu 22.10:&amp;lt;/code&amp;gt; $ rustup update


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We've taken &lt;a href="https://aws.amazon.com/what-is/cross-origin-resource-sharing/" title="CORS" rel="noopener noreferrer"&gt;CORS&lt;/a&gt; into account when we started out this project in this &lt;a href="https://behainguyen.wordpress.com/2023/10/18/rust-web-application-mysql-server-sqlx-actix-web-and-tera/" title="Rust web application: MySQL server, sqlx, actix-web and tera" rel="noopener noreferrer"&gt;first&lt;/a&gt; post. &lt;/p&gt;

&lt;p&gt;I'm not quite certain what'd happened, but all of a sudden, it just rejects requests with message &lt;code&gt;&lt;span&gt;Origin is not allowed to make this request&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;-- Browsers have been updated, perhaps?&lt;/p&gt;

&lt;p&gt;Failing to troubleshoot the problem, and seeing that &lt;a href="https://docs.rs/actix-cors/latest/actix_cors/index.html" title="actix-cors" rel="noopener noreferrer"&gt;actix-cors&lt;/a&gt; is at version &lt;code&gt;0.7.0&lt;/code&gt;. I update it. &lt;/p&gt;

&lt;p&gt;-- It does not work with Rust version &lt;code&gt;1.74.0&lt;/code&gt;. This new version of &lt;a href="https://docs.rs/actix-cors/latest/actix_cors/index.html" title="actix-cors" rel="noopener noreferrer"&gt;actix-cors&lt;/a&gt; seems to fix the above request rejection issue.&lt;/p&gt;

&lt;p&gt;&lt;a id="step-two-update-employees-table"&gt;&lt;/a&gt;&lt;br&gt;
❷ Update the &lt;code&gt;employees&lt;/code&gt; table, adding new fields &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using the migration tool &lt;a href="https://github.com/launchbadge/sqlx/tree/main/sqlx-cli" title="SQLx CLI" rel="noopener noreferrer"&gt;SQLx CLI&lt;/a&gt;, which we've covered in &lt;a href="https://behainguyen.wordpress.com/2023/10/10/rust-sqlx-cli-database-migration-with-mysql-and-postgresql/" title="Rust SQLx CLI: database migration with MySQL and PostgreSQL." rel="noopener noreferrer"&gt;Rust SQLx CLI: database migration with MySQL and PostgreSQL&lt;/a&gt;, to update the &lt;code&gt;employees&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;While inside the new directory &lt;code&gt;migrations/mysql/&lt;/code&gt;, see project directory layout above, create empty migration files &lt;code&gt;99999999999999_emp_email_pwd.up.sql&lt;/code&gt; and &lt;code&gt;99999999999999_emp_email_pwd.down.sql&lt;/code&gt; using the command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

▶️&amp;lt;code&amp;gt;Windows 10:&amp;lt;/code&amp;gt; sqlx migrate add -r emp_email_pwd
▶️&amp;lt;code&amp;gt;Ubuntu 22.10:&amp;lt;/code&amp;gt; $ sqlx migrate add -r emp_email_pwd


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Populate the two script files with what we would like to do. Please see their contents &lt;a href="https://github.com/behai-nguyen/rust_web_01/tree/main/migrations/mysql/migrations" title="on GitHub" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;. To apply, run the below command, it'll take a little while to complete:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

▶️&amp;lt;code&amp;gt;Windows 10:&amp;lt;/code&amp;gt; sqlx migrate add -r emp_email_pwd
▶️&amp;lt;code&amp;gt;Ubuntu 22.10:&amp;lt;/code&amp;gt; $ sqlx migrate add -r emp_email_pwd


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a id="step-three-update-models"&gt;&lt;/a&gt;&lt;br&gt;
❸ Update &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/models.rs" title="updated src/models.rs" rel="noopener noreferrer"&gt;&lt;code&gt;src/models.rs&lt;/code&gt;&lt;/a&gt; to manage new fields &lt;code&gt;employees.email&lt;/code&gt; and &lt;code&gt;employees.password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we run &lt;code&gt;cargo test&lt;/code&gt; now, all integration tests should fail. All integration tests eventually call to &lt;code&gt;get_employees(...)&lt;/code&gt;, which does a &lt;code&gt;select * from employees...&lt;/code&gt;. Since the two new fields've been added to a specific order, field indexes in &lt;code&gt;get_employees(...)&lt;/code&gt; are out of order.&lt;/p&gt;

&lt;p&gt;Module &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/models.rs" title="updated src/models.rs" rel="noopener noreferrer"&gt;&lt;code&gt;src/models.rs&lt;/code&gt;&lt;/a&gt; gets the following updates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;pub email: String&lt;/code&gt; field added to &lt;code&gt;struct Employee&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pub async fn get_employees(...)&lt;/code&gt; updated to read &lt;code&gt;Employee.email&lt;/code&gt; field. Other fields' indexes also get updated.&lt;/li&gt;
&lt;li&gt;New &lt;code&gt;pub struct EmployeeLogin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;New &lt;code&gt;pub async fn select_employee(...)&lt;/code&gt;, which optionally selects an employee base on exact email match.&lt;/li&gt;
&lt;li&gt;New &lt;code&gt;pub struct LoginSuccess&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;"email": "siamak.bernardeschi.67115@gmail.com"&lt;/code&gt; to existing tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please see the &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/models.rs" title="updated src/models.rs" rel="noopener noreferrer"&gt;updated &lt;code&gt;src/models.rs&lt;/code&gt;&lt;/a&gt; on GitHub. The documentation should be sufficient to help reading the code.&lt;/p&gt;

&lt;p&gt;&lt;a id="step-four-login-routes"&gt;&lt;/a&gt;&lt;br&gt;
❹ New module &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/auth_handlers.rs" title="src/auth_handlers.rs" rel="noopener noreferrer"&gt;&lt;code&gt;src/auth_handlers.rs&lt;/code&gt;&lt;/a&gt;, where new login routes &lt;code&gt;/ui/login&lt;/code&gt; and &lt;code&gt;/api/login&lt;/code&gt; are implemented.&lt;/p&gt;

&lt;p&gt;● &lt;code&gt;&lt;a href="http://0.0.0.0:5000/ui/login" rel="noopener noreferrer"&gt;http://0.0.0.0:5000/ui/login&lt;/a&gt;&lt;/code&gt; is a &lt;code&gt;GET&lt;/code&gt; route, which just returns the &lt;code&gt;login.html&lt;/code&gt; page as HTML.&lt;/p&gt;

&lt;p&gt;● &lt;code&gt;&lt;a href="http://0.0.0.0:5000/api/login" rel="noopener noreferrer"&gt;http://0.0.0.0:5000/api/login&lt;/a&gt;&lt;/code&gt; is a &lt;code&gt;POST&lt;/code&gt; route. This is effectively the application login handler.&lt;/p&gt;

&lt;p&gt;💥 This &lt;code&gt;&lt;a href="http://0.0.0.0:5000/api/login" rel="noopener noreferrer"&gt;http://0.0.0.0:5000/api/login&lt;/a&gt;&lt;/code&gt; route is the main focus of this post:&lt;/p&gt;

&lt;p&gt;-- Its handler method accepts both &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; and &lt;code&gt;application/json&lt;/code&gt; content types, and deserialises the byte stream to &lt;code&gt;struct EmployeeLogin&lt;/code&gt; mentioned above.&lt;/p&gt;

&lt;p&gt;💥 &lt;strong&gt;&lt;em&gt;Please also note that, as already mentioned, in this post, the login process does not do login, if successfully deserialised the submitted data, it'd just echo a confirmation response in the format of the request content type. If failed to deserialise, it'd send back a JSON response which has an error code and a text message. &lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Examples of valid submitted data for each content type:&lt;/p&gt;

&lt;p&gt;✔️ Content type: &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;; data: &lt;code&gt;email=&lt;a href="mailto:chirstian.koblick.10004@gmail.com"&gt;chirstian.koblick.10004@gmail.com&lt;/a&gt;&amp;amp;password=password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;✔️ Content type: &lt;code&gt;application/json&lt;/code&gt;; data: &lt;code&gt;{"email": "&lt;a href="mailto:chirstian.koblick.10004@gmail.com"&gt;chirstian.koblick.10004@gmail.com&lt;/a&gt;", "password": "password"}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Content of &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/auth_handlers.rs" title="src/auth_handlers.rs" rel="noopener noreferrer"&gt;src/auth_handlers.rs&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="nd"&gt;#[post(&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bytes&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// Attempts to extract -- deserialising -- request body into EmployeeLogin.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;api_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extract_employee_login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="nf"&gt;.content_type&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="c1"&gt;// Failed to deserialise request body. Returns the error as is.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;api_status&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.content_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;api_status&lt;/span&gt;&lt;span class="nf"&gt;.err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Succeeded to deserialise request body.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;emp_login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EmployeeLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_status&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note the second parameter &lt;code&gt;body&lt;/code&gt;, which is &lt;a href="https://docs.rs/actix-web/4.4.1/actix_web/web/struct.Bytes.html" title="actix_web::web::Bytes" rel="noopener noreferrer"&gt;actix_web::web::Bytes&lt;/a&gt;, this is the byte stream presentation of the request body. &lt;/p&gt;

&lt;p&gt;As an extractor, &lt;a href="https://docs.rs/actix-web/4.4.1/actix_web/web/struct.Bytes.html" title="actix_web::web::Bytes" rel="noopener noreferrer"&gt;actix_web::web::Bytes&lt;/a&gt; has been mentioned in section &lt;a href="https://actix.rs/docs/extractors#other" title="Other" rel="noopener noreferrer"&gt;Type-safe information extraction | Other&lt;/a&gt;. We're providing our own implementation to do the deserialisation, method &lt;code&gt;extract_employee_login(...)&lt;/code&gt; in new module &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/helper/endpoint.rs" title="src/helper/endpoint.rs" rel="noopener noreferrer"&gt;&lt;code&gt;src/helper/endpoint.rs&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Content of &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/helper/endpoint.rs" title="src/helper/endpoint.rs" rel="noopener noreferrer"&gt;src/helper/endpoint.rs&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;extract_employee_login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmployeeLogin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ApiStatus&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;extractors&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Extractor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;APPLICATION_WWW_FORM_URLENCODED&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
        &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmployeeLogin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ApiStatus&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmployeeLogin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ApiStatus&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;err_code_500&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.set_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;extractors&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Extractor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmployeeLogin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ApiStatus&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// From https://stackoverflow.com/a/67340858&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ApiStatus&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;err_code_500&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.set_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; content type, we call method &lt;a href="https://docs.rs/serde_html_form/0.2.3/serde_html_form/fn.from_bytes.html" title="serde_html_form::from_bytes(...)" rel="noopener noreferrer"&gt;serde_html_form::from_bytes(...)&lt;/a&gt; from (new) crate &lt;a href="https://docs.rs/serde_html_form/0.2.3/serde_html_form/" title="serde_html_form" rel="noopener noreferrer"&gt;serde_html_form&lt;/a&gt; to deserialise the byte stream to &lt;code&gt;EmployeeLogin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/Cargo.toml" title="Cargo.toml" rel="noopener noreferrer"&gt;&lt;code&gt;Cargo.toml&lt;/code&gt;&lt;/a&gt; has been updated to include crate &lt;a href="https://docs.rs/serde_html_form/0.2.3/serde_html_form/" title="serde_html_form" rel="noopener noreferrer"&gt;serde_html_form&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And for &lt;code&gt;application/json&lt;/code&gt; content type, we call to &lt;a href="https://docs.rs/serde_json/latest/serde_json/fn.from_slice.html" title="serde_json::from_slice(...)" rel="noopener noreferrer"&gt;serde_json::from_slice(...)&lt;/a&gt; from the already included &lt;a href="https://docs.rs/serde_json/latest/serde_json/" title="serde_json" rel="noopener noreferrer"&gt;serde_json&lt;/a&gt; crate to do the work.&lt;/p&gt;

&lt;p&gt;These're the essential details of the code. The rest is fairly straightforward, and there's also sufficient documentation to aid the reading of the code.&lt;/p&gt;

&lt;p&gt;💥 Please also note that there're also some more new modules, such as &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/bh_libs/api_status.rs" title="src/bh_libs/api_status.rs" rel="noopener noreferrer"&gt;&lt;code&gt;src/bh_libs/api_status.rs&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/helper/messages.rs" title="src/helper/messages.rs" rel="noopener noreferrer"&gt;&lt;code&gt;src/helper/messages.rs&lt;/code&gt;&lt;/a&gt;, they're very small, self-explanatory and have sufficient documentation where appropriate.&lt;/p&gt;

&lt;p&gt;&lt;a id="step-five-update-lib"&gt;&lt;/a&gt;&lt;br&gt;
❺ Register new login routes &lt;code&gt;/ui/login&lt;/code&gt; and &lt;code&gt;/api/login&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Updated &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/src/lib.rs" title="src/lib.rs" rel="noopener noreferrer"&gt;src/lib.rs&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
            &lt;span class="nf"&gt;.service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nn"&gt;web&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/ui"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;employees_html1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;employees_html2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;auth_handlers&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;login_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="c1"&gt;// .service(auth_handlers::home_page),&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nn"&gt;web&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;auth_handlers&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nn"&gt;web&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/helloemployee/{last_name}/{first_name}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SayHi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;web&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hi_first_employee_found&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a id="step-six-new-login-page"&gt;&lt;/a&gt;&lt;br&gt;
❻ The last addition, the new &lt;a href="https://github.com/behai-nguyen/rust_web_01/blob/main/templates/auth/login.html" title="templates/auth/login.html" rel="noopener noreferrer"&gt;&lt;code&gt;templates/auth/login.html&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please note, this login page has only HTML. There is no CSS at all. It looks like a dog's breakfast, but it does work. There is no client-side validations either. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Login&lt;/code&gt; button &lt;code&gt;POST&lt;/code&gt;s login requests to &lt;code&gt;&lt;a href="http://0.0.0.0:5000/api/login" rel="noopener noreferrer"&gt;http://0.0.0.0:5000/api/login&lt;/a&gt;&lt;/code&gt;, the content type then is &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;application/json&lt;/code&gt; content type, we can use &lt;a href="https://testfully.io/" title="Testfully" rel="noopener noreferrer"&gt;Testfully&lt;/a&gt;. (We could also write our own AJAX requests to test.)&lt;/p&gt;

&lt;p&gt;&lt;a id="step-seven-some-testing"&gt;&lt;/a&gt;&lt;br&gt;
❼ As this is not yet the final version of the login process, we're not writing any integration tests for it yet. We'll do so in due course...&lt;/p&gt;

&lt;p&gt;&lt;a id="step-seven-some-testing-notes-on-cargo-test"&gt;&lt;/a&gt;&lt;br&gt;
⓵ For the time being, we've written some new code and their associated unit tests. We have also written some documentation examples. The full test with the command &lt;code&gt;cargo test&lt;/code&gt; should have all tests pass.&lt;/p&gt;

&lt;p&gt;&lt;a id="step-seven-some-testing-manual-tests"&gt;&lt;/a&gt;&lt;br&gt;
⓶ Manual tests of the new routes. &lt;/p&gt;

&lt;p&gt;In the following two successful tests, I run the application server on an Ubuntu 22.10 machine, and run both the login page and &lt;a href="https://testfully.io/" title="Testfully" rel="noopener noreferrer"&gt;Testfully&lt;/a&gt; on Windows 10.&lt;/p&gt;

&lt;p&gt;Test &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; submission via login page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-02.png" alt="095-02.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-03.png" alt="095-03.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test &lt;code&gt;application/json&lt;/code&gt; submission using &lt;a href="https://testfully.io/" title="Testfully" rel="noopener noreferrer"&gt;Testfully&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-04.png" alt="095-04.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-05.png" alt="095-05.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this failure test, I run the application server and &lt;a href="https://testfully.io/" title="Testfully" rel="noopener noreferrer"&gt;Testfully&lt;/a&gt; on Windows 10. The submitted &lt;code&gt;application/json&lt;/code&gt; data does not have an &lt;code&gt;email&lt;/code&gt; field:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-06.png" alt="095-06.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbehainguyen.files.wordpress.com%2F2024%2F01%2F095-07.png" alt="095-07.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's been an interesting exercise for me. My understanding of Rust's improved a little. I hope you find the information in this post useful. Thank you for reading and stay safe as always.&lt;/p&gt;

&lt;p&gt;✿✿✿&lt;/p&gt;

&lt;p&gt;Feature image source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper" rel="noopener noreferrer"&gt;https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://in.pinterest.com/pin/337277459600111737/" rel="noopener noreferrer"&gt;https://in.pinterest.com/pin/337277459600111737/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;https://www.rust-lang.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/" rel="noopener noreferrer"&gt;https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>actix</category>
      <category>urlencoded</category>
      <category>json</category>
    </item>
  </channel>
</rss>
