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.
Rust: Actix-web β Async Functions as Middlewares |
π 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
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
β 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 β
βββ ...
β· An update to the Cargo.toml file:
...
[dependencies]
...
actix-web-lab = "0.20.2"
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
})
})
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:
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:
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))
βΉ 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::cookie::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:
- https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper
- https://in.pinterest.com/pin/337277459600111737/
- https://www.rust-lang.org/
- https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/