'How to use a generic session with Actix and Paperclip?
I've created a server using Actix Web which has authentication support. Now I want to add an OpenAPI Specification using paperclip.
Normally you only have to add #[derive(Apiv2Security)]
to the struct which represents the session of an authorized user. In my case this does not seem to do the trick because of some indirection: The app_data contains a generic Context type which defines what storage should be used (for example a database or an in memory store). The Context has an associated type which specifies the type of session it can load. The code looks something like this:
use actix_http::Payload;
use actix_web::{App, FromRequest, HttpRequest, HttpServer};
use paperclip::actix::{api_v2_operation, web, Apiv2Security, OpenApiExt};
use serde::{Deserialize, Serialize};
use std::convert::Infallible;
use std::future::{ready, Ready};
trait Context {
type Session: Session;
fn session_by_id(&self, session_id: usize) -> Self::Session;
}
#[derive(Clone)]
struct InMemoryContext;
impl Context for InMemoryContext {
type Session = SimpleSession;
fn session_by_id(&self, session_id: usize) -> Self::Session {
SimpleSession {
id: session_id
}
}
}
pub trait Session {
fn id(&self) -> usize;
}
#[derive(Clone, Deserialize, Serialize, Apiv2Security)]
#[openapi(
apiKey,
in = "header",
name = "Authorization",
description = "Use format 'Bearer TOKEN'"
)]
pub struct SimpleSession {
id: usize,
}
impl Session for SimpleSession {
fn id(&self) -> usize {
self.id
}
}
impl FromRequest for SimpleSession {
type Error = Infallible;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
let ctx = req
.app_data::<web::Data<InMemoryContext>>()
.expect("InMemoryContext must be available")
.get_ref()
.clone();
ready(Ok(ctx.session_by_id(42)))
}
}
#[api_v2_operation]
async fn hello_world_handler<C: Context>(session: C::Session) -> web::Json<usize> {
web::Json(session.id())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.app_data(web::Data::new(InMemoryContext {}))
.wrap_api()
.route(
"/hello",
web::get().to(hello_world_handler::<InMemoryContext>),
)
.with_json_spec_at("/api/spec/v2")
.build()
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
The compiler throws errors because it cannot find the methods generated by the derive macro:
error[E0599]: no function or associated item named `update_parameter` found for associated type `<C as Context>::Session` in the current scope
--> src/main.rs:61:1
|
61 | #[api_v2_operation]
| ^^^^^^^^^^^^^^^^^^^ function or associated item not found in `<C as Context>::Session`
|
= note: this error originates in the attribute macro `api_v2_operation` (in Nightly builds, run with -Z macro-backtrace for more info)
Do you know how to get this working?
Solution 1:[1]
I found out that a trait was missing on Context::Session
. The solution is to add paperclip::actix::OperationModifier
:
trait Context {
type Session: Session + OperationModifier;
fn session_by_id(&self, session_id: usize) -> Self::Session;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Lutz Kremer |