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:
Jonathan Kelley authored and GitHub committed 2025-10-30 18:52:47 -07:00
1 parent 43373f0be7
commit 5559e463c8
27 files changed
+1395 -1097

No files matched your search

+14
View File
@@ -162,6 +162,20 @@ impl From<RequestError> for ServerFnError {
}
}
impl From<ServerFnError> for HttpError {
fn from(value: ServerFnError) -> Self {
let status = StatusCode::from_u16(match &value {
ServerFnError::ServerError { code, .. } => *code,
_ => 500,
})
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
HttpError {
status,
message: Some(value.to_string()),
}
}
}
impl From<HttpError> for ServerFnError {
fn from(value: HttpError) -> Self {
ServerFnError::ServerError {
-3
View File
@@ -25,6 +25,3 @@ pub use error::*;
pub mod httperror;
pub use httperror::*;
#[derive(Clone, Default)]
pub struct DioxusServerState {}
+18 -7
View File
@@ -9,10 +9,6 @@ use std::collections::HashSet;
use std::fmt::Debug;
use std::sync::Arc;
tokio::task_local! {
static FULLSTACK_CONTEXT: FullstackContext;
}
/// The context provided by dioxus fullstack for server-side rendering.
///
/// This context will only be set on the server during the initial streaming response
@@ -21,10 +17,18 @@ tokio::task_local! {
pub struct FullstackContext {
// We expose the lock for request headers directly so it needs to be in a separate lock
request_headers: Arc<RwLock<http::request::Parts>>,
// The rest of the fields are only held internally, so we can group them together
lock: Arc<RwLock<FullstackContextInner>>,
}
// `FullstackContext` is always set when either
// 1. rendering the app via SSR
// 2. handling a server function request
tokio::task_local! {
static FULLSTACK_CONTEXT: FullstackContext;
}
pub struct FullstackContextInner {
current_status: StreamingStatus,
current_status_subscribers: HashSet<ReactiveContext>,
@@ -78,6 +82,7 @@ impl FullstackContext {
pub fn commit_initial_chunk(&mut self) {
let mut lock = self.lock.write();
lock.current_status = StreamingStatus::InitialChunkCommitted;
// The key type is mutable, but the hash is stable through mutations because we hash by pointer
#[allow(clippy::mutable_key_type)]
let subscribers = std::mem::take(&mut lock.current_status_subscribers);
@@ -110,17 +115,23 @@ impl FullstackContext {
FULLSTACK_CONTEXT.scope(self, fut).await
}
/// Extract an extension from the current request.
pub fn extension<T: Clone + Send + Sync + 'static>(&self) -> Option<T> {
let lock = self.request_headers.read();
lock.extensions.get::<T>().cloned()
}
/// Extract an axum extractor from the current request.
///
/// The body of the request is always empty when using this method, as the body can only be consumed once in the server
/// function extractors.
pub async fn extract<T: FromRequest<(), M>, M>() -> Result<T, ServerFnError> {
pub async fn extract<T: FromRequest<Self, M>, M>() -> Result<T, ServerFnError> {
let this = Self::current()
.ok_or_else(|| ServerFnError::new("No FullstackContext found".to_string()))?;
let parts = this.request_headers.read().clone();
let request = axum_core::extract::Request::from_parts(parts, Default::default());
match T::from_request(request, &()).await {
match T::from_request(request, &this).await {
Ok(res) => Ok(res),
Err(err) => {
let resp = err.into_response();
@@ -255,7 +266,7 @@ pub fn commit_initial_chunk() {
/// Extract an axum extractor from the current request.
#[deprecated(note = "Use FullstackContext::extract instead", since = "0.7.0")]
pub fn extract<T: FromRequest<(), M>, M>(
pub fn extract<T: FromRequest<FullstackContext, M>, M>(
) -> impl std::future::Future<Output = Result<T, ServerFnError>> {
FullstackContext::extract::<T, M>()
}