multiple fullstack nits/cleanups before release (#4852)
* feat: `:arg` syntax for serverfn * add better decode/encode verify message * allow catch-all query args * add rebind * implement websocket frames for `Streaming` * fix lazy init * wip... serverfnstate * wip... * wip: serverfns require Send/Sync, let's fix that * wip... * small cleanup * fix hang by actually spawning the tokio task * clippy * fix parameter catchall binding * fix lychee * doc tests
This commit is contained in:
1 parent
43373f0be7
commit
5559e463c8
27 files changed
+1395
-1097
No files matched your search
@@ -18,7 +18,7 @@ fn main() {
|
||||
});
|
||||
}
|
||||
|
||||
#[get("/api/{name}/?age")]
|
||||
#[get("/api/:name/?age")]
|
||||
async fn get_message(name: String, age: i32) -> Result<String> {
|
||||
Ok(format!("Hello {}, you are {} years old!", name, age))
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//! An example showcasing query parameters in Dioxus Fullstack server functions.
|
||||
//!
|
||||
//! The query parameter syntax mostly follows axum, but with a few extra conveniences.
|
||||
//! - can rename parameters in the function signature with `?age=age_in_years` where `age_in_years` is Rust variable name
|
||||
//! - can absorb all query params with `?{object}` directly into a struct implementing `Deserialize`
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::launch(|| {
|
||||
let mut message = use_action(get_message);
|
||||
let mut message_rebind = use_action(get_message_rebind);
|
||||
let mut message_all = use_action(get_message_all);
|
||||
|
||||
rsx! {
|
||||
h1 { "Server says: "}
|
||||
div {
|
||||
button { onclick: move |_| message.call(22), "Single" }
|
||||
pre { "{message:?}"}
|
||||
}
|
||||
div {
|
||||
button { onclick: move |_| message_rebind.call(25), "Rebind" }
|
||||
pre { "{message_rebind:?}"}
|
||||
}
|
||||
div {
|
||||
button { onclick: move |_| message_all.call(Params { age: 30, name: "world".into() }), "Bind all" }
|
||||
pre { "{message_all:?}"}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[get("/api/message/?age")]
|
||||
async fn get_message(age: i32) -> Result<String> {
|
||||
Ok(format!("You are {} years old!", age))
|
||||
}
|
||||
|
||||
#[get("/api/rebind/?age=age_in_years")]
|
||||
async fn get_message_rebind(age_in_years: i32) -> Result<String> {
|
||||
Ok(format!("You are {} years old!", age_in_years))
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
||||
struct Params {
|
||||
age: i32,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[get("/api/all/?{query}")]
|
||||
async fn get_message_all(query: Params) -> Result<String> {
|
||||
Ok(format!(
|
||||
"Hello {}, you are {} years old!",
|
||||
query.name, query.age
|
||||
))
|
||||
}
|
||||
@@ -92,8 +92,8 @@
|
||||
//! take a `State<T>` extractor cannot be automatically added to the router since the dioxus router
|
||||
//! type does not know how to construct the `T` type.
|
||||
//!
|
||||
//! These server functions will be registered once the `ServerFnState<T>` layer is added to the app with
|
||||
//! `router = router.layer(ServerFnState::new(your_state))`.
|
||||
//! These server functions will be registered once the `ServerState<T>` layer is added to the app with
|
||||
//! `router = router.layer(ServerState::new(your_state))`.
|
||||
//!
|
||||
//! ## Middleware
|
||||
//!
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
//! This example shows how to use global state to maintain state between server functions.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use std::rc::Rc;
|
||||
|
||||
use axum_core::extract::{FromRef, FromRequest};
|
||||
use dioxus::{
|
||||
fullstack::{FullstackContext, extract::State},
|
||||
prelude::*,
|
||||
};
|
||||
use reqwest::header::HeaderMap;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use {
|
||||
@@ -77,7 +84,68 @@ type BroadcastExtension = axum::Extension<tokio::sync::broadcast::Sender<String>
|
||||
|
||||
#[post("/api/broadcast", ext: BroadcastExtension)]
|
||||
async fn broadcast_message() -> Result<()> {
|
||||
let rt = Rc::new("asdasd".to_string());
|
||||
ext.send("New broadcast message".to_string())?;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
println!("rt: {}", rt);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
Option 4:
|
||||
|
||||
You can use Axum's `State` extractor to provide custom application state to your server functions.
|
||||
|
||||
All ServerFunctions pull in `FullstackContext`, so you need to implement `FromRef<FullstackContext>` for your
|
||||
custom state type. To add your state to your app, you can use `.register_server_functions()` on a router
|
||||
for a given state type, which will automatically add your state into the `FullstackContext` used by your server functions.
|
||||
|
||||
There are two details to note here:
|
||||
|
||||
- You need to implement `FromRef<FullstackContext>` for your custom state type.
|
||||
- Custom extractors need to implement `FromRequest<S>` where `S` is the state type that implements `FromRef<FullstackContext>`.
|
||||
*/
|
||||
#[derive(Clone)]
|
||||
struct MyAppState {
|
||||
abc: i32,
|
||||
}
|
||||
|
||||
impl FromRef<FullstackContext> for MyAppState {
|
||||
fn from_ref(state: &FullstackContext) -> Self {
|
||||
state.extension::<MyAppState>().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomExtractor {
|
||||
abc: i32,
|
||||
headermap: HeaderMap,
|
||||
}
|
||||
|
||||
impl<S> FromRequest<S> for CustomExtractor
|
||||
where
|
||||
MyAppState: FromRef<S>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = ();
|
||||
|
||||
async fn from_request(
|
||||
_req: axum::extract::Request,
|
||||
state: &S,
|
||||
) -> std::result::Result<Self, Self::Rejection> {
|
||||
let state = MyAppState::from_ref(state);
|
||||
Ok(CustomExtractor {
|
||||
abc: state.abc,
|
||||
headermap: HeaderMap::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/api/stateful", state: State<MyAppState>, ex: CustomExtractor)]
|
||||
async fn app_state() -> Result<()> {
|
||||
println!("abc: {}", state.abc);
|
||||
println!("state abc: {:?}", ex.abc);
|
||||
println!("headermap: {:?}", ex.headermap);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -95,6 +163,10 @@ fn main() {
|
||||
let router = dioxus::server::router(app)
|
||||
.layer(Extension(tokio::sync::broadcast::channel::<String>(16).0));
|
||||
|
||||
// To use our custom app state with `State<MyAppState>`, we need to register it
|
||||
// as an extension since our `FromRef<FullstackContext>` implementation relies on it.
|
||||
let router = router.layer(Extension(MyAppState { abc: 42 }));
|
||||
|
||||
Ok(router)
|
||||
});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user