我现在尝试使用axum和async-graphql构建一个graphql服务器。
最大的问题是graphql和axum之间的数据共享不好,我之所以要这么做,是因为我想把axum收到的用于认证的http头传递给graphql的世界。
谁能解决这个问题或提出另一种方法?
Cargo.toml
[package]
name = "app"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = { version = "0.6.1", features = ["headers"] }
tokio = { version = "1.19.2", features = ["rt-multi-thread", "macros"] }
serde = { version = "1.0.136", features = ["derive"] }
async-graphql = { version = "5.0.4", features = ["chrono"] }
sqlx = { version = "0.6.2", features = [ "runtime-actix-native-tls", "postgres", "chrono" ] }
dotenv = "0.15.0"
tower-http = { version = "0.3.5", features = ["cors", "trace"] }
tokio-stream = "0.1.11"
chrono = "0.4.23"
jsonwebtoken = "8.2.0"
thiserror = "1.0.38"
async-trait = "0.1.60"
main.rs
mod db;
mod repositories;
mod resolvers;
use async_graphql::{
http::{playground_source, GraphQLPlaygroundConfig},
Request, Response, Schema,
};
use axum::{
extract::{Extension, State},
http::{
header::{ACCEPT, AUTHORIZATION},
HeaderValue, Method, Request as AxumRequest,
},
middleware::Next,
response::{Html, IntoResponse, Response as AxumResponse},
routing::get,
Json, Router,
};
use dotenv::dotenv;
use resolvers::{QueryRoot, Subscription};
use std::net::SocketAddr;
use tower_http::cors::CorsLayer;
use crate::{db::DB, resolvers::Mutation};
pub type MainSchema = Schema<QueryRoot, Mutation, Subscription>;
async fn graphql_handler(schema: Extension<MainSchema>, req: Json<Request>) -> Json<Response> {
schema.execute(req.0).await.into()
}
async fn graphql_playground() -> impl IntoResponse {
Html(playground_source(GraphQLPlaygroundConfig::new("/")))
}
#[derive(Clone, Debug)]
pub struct AppState {
db: DB,
token: Option<String>,
}
impl AppState {
fn set_token(mut self, token: String) {
self.token = Some(token)
}
}
async fn propagate_header<B>(
State(state): State<&AppState>,
req: AxumRequest<B>,
next: Next<B>,
) -> AxumResponse {
let token = req.headers().get("Authorization");
if token.is_some() {
// TODO: Put token in state.
};
next.run(req).await
}
#[tokio::main]
async fn main() {
dotenv().ok();
let server = async {
let db = DB::new().await;
let state = AppState { db, token: None };
let schema = Schema::build(QueryRoot, Mutation, Subscription)
// .limit_depth(5)
.data(&state)
.finish();
let cors_layer = CorsLayer::new()
.allow_origin("*".parse::<HeaderValue>().unwrap())
.allow_methods([Method::GET, Method::POST, Method::OPTIONS])
.allow_headers(vec![AUTHORIZATION, ACCEPT]);
let app = Router::new()
.route("/", get(graphql_playground).post(graphql_handler))
.layer(cors_layer)
.layer(axum::middleware::from_fn_with_state(
&state,
propagate_header,
))
.layer(Extension(schema));
let addr = SocketAddr::from(([0, 0, 0, 0], 8009));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
};
tokio::join!(server);
}
误差
error[E0597]: `state` does not live long enough
--> src/main.rs:71:19
|
69 | let schema = Schema::build(QueryRoot, Mutation, Subscription)
| ______________________-
70 | | // .limit_depth(5)
71 | | .data(&state)
| |___________________^^^^^^- argument requires that `state` is borrowed for `'static`
| |
| borrowed value does not live long enough
...
93 | };
| - `state` dropped here while still borrowed
1条答案
按热度按时间vyswwuz21#
在这里您可能需要使用
Arc
,请参见here。我能看到的下一个问题是,基于对
token
字段的查看,你的状态似乎是可变的,这不能单独使用arc来实现,你可能还需要添加一个Mutex,或者其他一些锁(例如RwLock
等)。