add actix web
This commit is contained in:
parent
0da03b6b03
commit
962973d869
117
content/actix-web-import-issue.md
Normal file
117
content/actix-web-import-issue.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
title: Solving `app_data` or `ReqData` missing in requests
|
||||
date: 2022-03-19
|
||||
---
|
||||
|
||||
> This post is day 5 of me taking part in the
|
||||
> [#100DaysToOffload](https://100daystooffload.com/) challenge.
|
||||
|
||||
I'm using `actix-web` to set up a web server, and I've been hitting a small
|
||||
problem that I think other people may come across too.
|
||||
|
||||
To explain the problem, let me talk a bit about my setup. I have a custom
|
||||
middleware that checks if a user is authorized to access a route. It looks like
|
||||
this:
|
||||
|
||||
```rust
|
||||
impl<S: 'static, B> Service<ServiceRequest> for CheckLoginMiddleware<S>
|
||||
where
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S::Future: 'static,
|
||||
{
|
||||
type Response = ServiceResponse<EitherBody<B>>;
|
||||
type Error = Error;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
dev::forward_ready!(service);
|
||||
|
||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
let state = self.state.clone();
|
||||
let (request, payload) = req.into_parts();
|
||||
let service = self.service.clone();
|
||||
|
||||
let user_token = get_token_from_header(&request);
|
||||
let path_token = if self.allow_path_tokens {
|
||||
get_token_from_query(&request)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Box::pin(async move {
|
||||
match verify_auth(state, user_token, path_token, request.path()).await {
|
||||
Ok(authorized) => {
|
||||
tracing::debug!("Request authorized, inserting authorization token");
|
||||
// This is the "important bit" where we insert the authorization token into the request data
|
||||
request.extensions_mut().insert(authorized);
|
||||
let service_request =
|
||||
service.call(ServiceRequest::from_parts(request, payload));
|
||||
service_request
|
||||
.await
|
||||
.map(ServiceResponse::map_into_left_body)
|
||||
}
|
||||
Err(err) => {
|
||||
let response = HttpResponse::Unauthorized().json(err).map_into_right_body();
|
||||
|
||||
Ok(ServiceResponse::new(request, response))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `verify_auth` function is omitted, but the gist of it is that it returns an `Result<Authorized, Error>`.
|
||||
If the user is authorized, the authorization token `verify_auth` returned is then attached to the request.
|
||||
|
||||
Then here's how I use it in a path:
|
||||
|
||||
```rust
|
||||
#[delete("/{store}/{path:.*}")]
|
||||
async fn delete_storage(
|
||||
params: web::Path<(String, String)>,
|
||||
// This parameter is automatically filled with the token
|
||||
authorized: Option<ReqData<Authorized>>,
|
||||
) -> Result<HttpResponse, StorageError> {
|
||||
let (store, path) = params.as_ref();
|
||||
|
||||
let mut store_path = get_authorized_path(&authorized, store)?;
|
||||
store_path.push(path);
|
||||
if fs::metadata(&store_path).await?.is_file() {
|
||||
tracing::debug!("Deleting file {:?}", store_path);
|
||||
fs::remove_file(&store_path).await?;
|
||||
} else {
|
||||
tracing::debug!("Deleting folder {:?}", store_path);
|
||||
fs::remove_dir(&store_path).await?;
|
||||
}
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
```
|
||||
|
||||
This setup worked for this path, but would absolutely not work for another path.
|
||||
I inserted logs to track everything, and just found that the middleware would
|
||||
insert the token, but the path would just get `None`. How‽ I tried to slowly
|
||||
strip everything away from the non-functional path until it was identical to
|
||||
this one, but it still would not work.
|
||||
|
||||
Well it turns out the solution was very simple, see this:
|
||||
|
||||
```rust
|
||||
use my_package::storage::put_storage;
|
||||
use crate::storage::delete_storage;
|
||||
```
|
||||
|
||||
Ah! They are imported differently. I had set up my program as both a library and
|
||||
a program for various reasons. However, it turns out importing the same thing
|
||||
from `crate` is different from importing it from the library. Because of the
|
||||
difference in import, Actix doesn't recognize that the types match, so the route
|
||||
can't access the attached token.
|
||||
|
||||
The solution is normalizing the imports. I went with going through the library
|
||||
for everything, because that's what `rust-analyzer`s automatic import seems to
|
||||
prefer.
|
||||
|
||||
```rust
|
||||
use my_package::storage::{put_storage, delete_storage};
|
||||
```
|
||||
|
||||
Solved!
|
|
@ -26,7 +26,7 @@ $ mark-list my-video.mp4 # Choose a file
|
|||
Marked 1 file.
|
||||
$ mark-list *.webm # Choose many files
|
||||
Marked 3 files.
|
||||
$ cd Downloads
|
||||
$ cd Downloadsr
|
||||
$ mark-list last.mpg # You can go to other directories and keep marking
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in a new issue