Logo

dev-resources.site

for different kinds of informations.

Rust: Actix-web -- Async Functions as Middlewares

Published at
3/20/2024
Categories
rust
actix
async
middleware
Author
behainguyen
Categories
4 categories in total
rust
open
actix
open
async
open
middleware
open
Author
11 person written this
behainguyen
open
Rust: Actix-web -- Async Functions as Middlewares

In the tenth post of our actix-web learning application, we added an ad hoc middleware. In this post, with the assistance of the actix-web-lab crate, we will refactor this ad hoc middleware into a standalone async function to enhance the overall code readability.

🦀 Index of the Complete Series.

🚀 Please note, complete code for this post can be downloaded from GitHub with:

git clone -b v0.13.0 https://github.com/behai-nguyen/rust_web_01.git
Enter fullscreen mode Exit fullscreen mode

The actix-web learning application mentioned above has been discussed in the twelve previous posts. The index of the complete series can be found here.

The code we're developing in this post is a continuation of the code from the twelfth post. 🚀 To get the code of this twelfth post, please use the following command:

git clone -b v0.12.0 https://github.com/behai-nguyen/rust_web_01.git
Enter fullscreen mode Exit fullscreen mode

-- Note the tag v0.12.0.

While this post continues from previous posts in this series, it can be read in conjunction with only the tenth post, focusing particularly on the section titled Code Updated in the src/lib.rs Module.


❶ 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 indicating the ones that have been updated.


.
├── Cargo.toml ★
├── ...
├── README.md ★
└── src
  ├── lib.rs ★
  └── ...
Enter fullscreen mode Exit fullscreen mode


❷ An update to the Cargo.toml file:

...
[dependencies]
...
actix-web-lab = "0.20.2"
Enter fullscreen mode Exit fullscreen mode

We added the new crate actix-web-lab. This crate is:

In-progress extractors and middleware for Actix Web.

This crate provides mechanisms for implementing middlewares as standalone async functions, rather than using actix-web's wrap_fn.

According to the documentation, the actix-web-lab crate is essentially experimental. Functionalities implemented in this crate might eventually be integrated into the actix-web crate. In such a case, we would need to update our code.


❸ Refactor an existing ad hoc middleware out of wrap_fn.

As mentioned at the beginning, this post should be read in conjunction with the tenth post, where we introduced this ad hoc middleware. The description of this simple middleware functionality is found in the section Code Updated in the src/lib.rs Module of the tenth post.

Below, we reprint the code of this ad hoc middleware:

            //
            // This ad hoc middleware looks for the updated access token String attachment in 
            // the request extension, if there is one, extracts it and sends it to the client 
            // via both the ``authorization`` header and cookie.
            //
            .wrap_fn(|req, srv| {
                let mut updated_access_token: Option<String> = None;

                // Get set in src/auth_middleware.rs's 
                // fn update_and_set_updated_token(request: &ServiceRequest, token_status: TokenStatus).
                if let Some(token) = req.extensions_mut().get::<String>() {
                    updated_access_token = Some(token.to_string());
                }

                srv.call(req).map(move |mut res| {

                    if updated_access_token.is_some() {
                        let token = updated_access_token.unwrap();
                        res.as_mut().unwrap().headers_mut().append(
                            header::AUTHORIZATION, 
                            header::HeaderValue::from_str(token.as_str()).expect(TOKEN_STR_JWT_MSG)
                        );

                        let _ = res.as_mut().unwrap().response_mut().add_cookie(
                            &build_authorization_cookie(&token));
                    };

                    res
                })
            })
Enter fullscreen mode Exit fullscreen mode

It's not particularly lengthy, but its inclusion in the application instance construction process makes it difficult to read. While closures can call functions, refactoring this implementation into a standalone function isn't feasible. This is because the function would require access to the parameter srv, which in this case refers to the AppRouting struct. Please refer to the screenshot below for clarification:

103-01.png

The AppRouting struct is located in the private module actix-web/src/app_service.rs, 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.

Please refer to the GitHub issue titled wrap_fn &AppRouting should use Arc<AppRouting> #2681 for more details. This reply suggests using the actix-web-lab crate.

I believe I've come across this crate before, particularly the function actix_web_lab::middleware::from_fn, but it didn't register with me at the time.

Drawing from the official example actix-web-lab/actix-web-lab/examples/from_fn.rs and compiler suggestions, I've successfully refactored the ad hoc middleware mentioned above into the standalone async function async fn update_return_jwt<B>(req: ServiceRequest, next: Next<B>) -> Result<ServiceResponse<B>, Error>. The screenshot below, taken from Visual Studio Code with the Rust-Analyzer plugin, displays the full source code and variable types:

103-02.png

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 single line, as per the documentation:

            .wrap(from_fn(update_return_jwt))
Enter fullscreen mode Exit fullscreen mode


❹ 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 fn cors_config(config: &config::Config) -> Cors, and the code to create the session store to the standalone async function async fn config_session_store() -> (actix_web:🍪:Key, RedisSessionStore).

Currently, the src/lib.rs 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 pub async fn run(listener: TcpListener) -> Result<Server, std::io::Error>, 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.


❺ I am happy to have learned something new about actix-web. And I hope you find the information useful. Thank you for reading. And stay safe, as always.

✿✿✿

Feature image source:

🦀 Index of the Complete Series.

async Article's
30 articles in total
Favicon
This Small Python Script Improved Understanding of Low-Level Programming
Favicon
Async,Await Promise
Favicon
Async Vs Sync, which is most preferrable?
Favicon
Async/Await: Task.WhenAll + Exceptions = Dor de Cabeça!
Favicon
Everything You Need to Know About JavaScript Promises and How They Work
Favicon
Asynchronous Python
Favicon
Building pipelines with IAsyncEnumerable in .NET
Favicon
Unleash the Power of FastAPI: Async vs Blocking I/O
Favicon
Total Madness #2: Async Locks
Favicon
Don't use 'BuildContext's across async gaps.
Favicon
Integration Digest: May 2024
Favicon
Total Madness #1: Async/Await
Favicon
Forcing Angular SSR to Wait in 2024
Favicon
Using Async in Ruby on Rails for CSV export
Favicon
Mastering Async Await in JavaScript for Asynchronous Programming
Favicon
PHP HyperF + MariaDB -> Async / Parallel
Favicon
Async/await and SwiftUI
Favicon
🕒 Task vs Promise: Chaining
Favicon
🕒 Task vs Promise: Encadenación
Favicon
New custom blocks for Analytics Builder (async comms, downsampling and complex measurements)
Favicon
Concurrent-ruby (async) S3 files download
Favicon
Ruby class pattern to work with API requests with built-in async approach
Favicon
How to use ActionCable with async requests in a Ruby on Rails web app
Favicon
Introducing EventSail: A Python Library for Event-driven Programming
Favicon
Enhancing Asynchronous Data Fetching in Umbraco v14 with Lit Async Directives
Favicon
API simples que gera arquivos de forma assíncrona, com Java e Spring? Aqui tem!
Favicon
Async Axiom logging
Favicon
Rust: Actix-web -- Async Functions as Middlewares
Favicon
Streamlining Asynchronous Tasks in Django with Django Tasks Scheduler
Favicon
JavaScript async call analysis

Featured ones: