More fullstack fixes (#4737)

* allow `--server` only builds!

* rollback example

* capture `/:route` routes

* fix: properly implement file uploads in forms

* implement desktop form uploads

* Performant serialization of form bytes

* finish up file stuff

* cleanups

* add request headers to streaming context

* remove some unwraps
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-10-07 23:07:43 -07:00
1 parent fc01a002fd
commit 92792aded9
47 files changed
+1241 -703

No files matched your search

+48 -5
View File
@@ -1,5 +1,9 @@
use crate::ServerFnError;
use axum_core::{extract::FromRequest, response::IntoResponse};
use dioxus_core::try_consume_context;
use dioxus_signals::{ReadableExt, Signal, WritableExt};
use http::request::Parts;
use std::{cell::RefCell, rc::Rc};
/// The status of the streaming response
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -13,23 +17,26 @@ pub enum StreamingStatus {
}
/// The context dioxus fullstack provides for the status of streaming responses on the server
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub struct StreamingContext {
current_status: Signal<StreamingStatus>,
request_headers: Rc<RefCell<http::request::Parts>>,
}
impl Default for StreamingContext {
fn default() -> Self {
Self::new()
impl PartialEq for StreamingContext {
fn eq(&self, other: &Self) -> bool {
self.current_status == other.current_status
&& Rc::ptr_eq(&self.request_headers, &other.request_headers)
}
}
impl StreamingContext {
/// Create a new streaming context. You should not need to call this directly. Dioxus fullstack will
/// provide this context for you.
pub fn new() -> Self {
pub fn new(parts: Parts) -> Self {
Self {
current_status: Signal::new(StreamingStatus::RenderingInitialChunk),
request_headers: Rc::new(RefCell::new(parts)),
}
}
@@ -48,6 +55,42 @@ impl StreamingContext {
pub fn current_status(&self) -> StreamingStatus {
*self.current_status.read()
}
/// Access the http request parts mutably. This will allow you to modify headers and other parts of the request.
pub fn parts_mut(&self) -> std::cell::RefMut<'_, http::request::Parts> {
self.request_headers.borrow_mut()
}
/// Extract an axum extractor from the current request. This will always use an empty body for the request,
/// since it's assumed that rendering the app is done under a `GET` request.
pub async fn extract<T: FromRequest<(), M>, M>() -> Result<T, ServerFnError> {
let this = Self::current()
.ok_or_else(|| ServerFnError::new("No StreamingContext found".to_string()))?;
let parts = this.request_headers.borrow_mut().clone();
let request =
axum_core::extract::Request::from_parts(parts, axum_core::body::Body::empty());
match T::from_request(request, &()).await {
Ok(res) => Ok(res),
Err(err) => {
let resp = err.into_response();
Err(ServerFnError::from_axum_response(resp).await)
}
}
}
/// Get the current `StreamingContext` if it exists. This will return `None` if called on the client
/// or outside of a streaming response on the server.
pub fn current() -> Option<Self> {
if let Some(rt) = dioxus_core::Runtime::try_current() {
let id = rt.try_current_scope_id()?;
if let Some(ctx) = rt.consume_context::<StreamingContext>(id) {
return Some(ctx);
}
}
None
}
}
/// Commit the initial chunk of the response. This will be called automatically if you are using the