redirects, middleware, Transport, set/capture error status (#4751)
* wip: redirect * redirect example * fix file upload not in a form * wip * deny missing cfgs * wip: add router fallback but it's not working * fix suspense bug after rebuilds that queued work * more cleanup * add test for error codes * add helpful comment * bring back middleware annotation * fix cfg * add server feature * fix doctest * just disable liveview test for now * fix liveview * dont send init * cache more playwright! * add more pkgs to cache
This commit is contained in:
1 parent
8a81475ba8
commit
9243ef4128
35 files changed
+935
-800
No files matched your search
@@ -11,8 +11,8 @@ use dioxus_core::{
|
||||
SuspenseContext, TemplateNode, VNode, VirtualDom,
|
||||
};
|
||||
use dioxus_fullstack_core::{history::provide_fullstack_history_context, HttpError, ServerFnError};
|
||||
use dioxus_fullstack_core::{FullstackContext, StreamingStatus};
|
||||
use dioxus_fullstack_core::{HydrationContext, SerializedHydrationData};
|
||||
use dioxus_fullstack_core::{StreamingContext, StreamingStatus};
|
||||
use dioxus_router::ParseRouteError;
|
||||
use dioxus_ssr::Renderer;
|
||||
use futures_channel::mpsc::Sender;
|
||||
@@ -67,30 +67,29 @@ impl SsrRendererPool {
|
||||
route: &str,
|
||||
render_into: &mut Sender<Result<String, IncrementalRendererError>>,
|
||||
) -> Option<RenderFreshness> {
|
||||
if let Some(incremental) = &self.incremental_cache {
|
||||
if let Ok(mut incremental) = incremental.write() {
|
||||
match incremental.get(route) {
|
||||
Ok(Some(cached_render)) => {
|
||||
let CachedRender {
|
||||
freshness,
|
||||
response,
|
||||
..
|
||||
} = cached_render;
|
||||
_ = render_into.start_send(
|
||||
String::from_utf8(response.to_vec())
|
||||
.map_err(|err| IncrementalRendererError::Other(err.into())),
|
||||
);
|
||||
return Some(freshness);
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"Failed to get route \"{route}\" from incremental cache: {e}"
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
let incremental = self.incremental_cache.as_ref()?;
|
||||
|
||||
if let Ok(mut incremental) = incremental.write() {
|
||||
match incremental.get(route) {
|
||||
Ok(Some(cached_render)) => {
|
||||
let CachedRender {
|
||||
freshness,
|
||||
response,
|
||||
..
|
||||
} = cached_render;
|
||||
_ = render_into.start_send(
|
||||
String::from_utf8(response.to_vec())
|
||||
.map_err(|err| IncrementalRendererError::Other(err.into())),
|
||||
);
|
||||
return Some(freshness);
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to get route \"{route}\" from incremental cache: {e}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -103,6 +102,7 @@ impl SsrRendererPool {
|
||||
virtual_dom_factory: impl FnOnce() -> VirtualDom + Send + Sync + 'static,
|
||||
) -> Result<
|
||||
(
|
||||
HttpError,
|
||||
RenderFreshness,
|
||||
impl Stream<Item = Result<String, IncrementalRendererError>>,
|
||||
),
|
||||
@@ -150,6 +150,10 @@ impl SsrRendererPool {
|
||||
// before we even spawn anything, we can check synchronously if we have the route cached
|
||||
if let Some(freshness) = self.check_cached_route(&route, &mut into) {
|
||||
return Ok((
|
||||
HttpError {
|
||||
status: StatusCode::OK,
|
||||
message: None,
|
||||
},
|
||||
freshness,
|
||||
ReceiverWithDrop {
|
||||
receiver: rx,
|
||||
@@ -187,13 +191,14 @@ impl SsrRendererPool {
|
||||
|
||||
// Provide the document and streaming context to the root of the app
|
||||
let streaming_context =
|
||||
virtual_dom.in_scope(ScopeId::ROOT, || StreamingContext::new(parts));
|
||||
virtual_dom.in_scope(ScopeId::ROOT, || FullstackContext::new(parts));
|
||||
virtual_dom.provide_root_context(document.clone() as Rc<dyn dioxus_document::Document>);
|
||||
virtual_dom.provide_root_context(streaming_context.clone());
|
||||
|
||||
virtual_dom.in_scope(ScopeId::ROOT, || {
|
||||
// Wrap the memory history in a fullstack history provider to provide the initial route for hydration
|
||||
provide_fullstack_history_context(history);
|
||||
|
||||
// Provide a hydration compatible error boundary that serializes errors for the client
|
||||
dioxus_core::provide_create_error_boundary(
|
||||
dioxus_fullstack_core::init_error_boundary,
|
||||
@@ -203,6 +208,10 @@ impl SsrRendererPool {
|
||||
// rebuild the virtual dom
|
||||
virtual_dom.rebuild_in_place();
|
||||
|
||||
// Process any pending events that might have been triggered during the rebuild
|
||||
// This ensures things like error boundaries get a chance to run.
|
||||
virtual_dom.process_events();
|
||||
|
||||
// If streaming is disabled, wait for the virtual dom to finish all suspense work
|
||||
// before rendering anything
|
||||
if streaming_mode == StreamingMode::Disabled {
|
||||
@@ -212,8 +221,9 @@ impl SsrRendererPool {
|
||||
loop {
|
||||
// Check if the router has finished and set the streaming context to finished
|
||||
let streaming_context_finished = virtual_dom
|
||||
.in_scope(ScopeId::ROOT, || streaming_context.current_status())
|
||||
.in_scope(ScopeId::ROOT, || streaming_context.streaming_state())
|
||||
== StreamingStatus::InitialChunkCommitted;
|
||||
|
||||
// Or if this app isn't using the router and has finished suspense
|
||||
let suspense_finished = !virtual_dom.suspended_tasks_remaining();
|
||||
if streaming_context_finished || suspense_finished {
|
||||
@@ -228,7 +238,7 @@ impl SsrRendererPool {
|
||||
}
|
||||
}
|
||||
|
||||
// check if there are any errors
|
||||
// check if there are any errors from the root error boundary
|
||||
let error = virtual_dom.in_scope(ScopeId::ROOT_ERROR_BOUNDARY, || {
|
||||
consume_context::<ErrorContext>().error()
|
||||
});
|
||||
@@ -287,8 +297,11 @@ impl SsrRendererPool {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the FullstackContext in case the user set the statuscode manually or via a layout.
|
||||
let http_status = streaming_context.current_http_status();
|
||||
|
||||
// Now that we handled any errors from rendering, we can send the initial ok result
|
||||
_ = initial_result_tx.send(Ok(()));
|
||||
_ = initial_result_tx.send(Ok(http_status));
|
||||
|
||||
// Wait long enough to assemble the `<head>` of the document before starting to stream
|
||||
let mut pre_body = String::new();
|
||||
@@ -412,11 +425,12 @@ impl SsrRendererPool {
|
||||
let join_handle = Self::spawn_platform(create_render_future);
|
||||
|
||||
// Wait for the initial result which determines the status code
|
||||
initial_result_rx
|
||||
let status = initial_result_rx
|
||||
.await
|
||||
.map_err(|err| SSRError::Incremental(IncrementalRendererError::Other(err.into())))??;
|
||||
|
||||
Ok((
|
||||
status,
|
||||
RenderFreshness::now(None),
|
||||
ReceiverWithDrop {
|
||||
receiver: rx,
|
||||
|
||||
Reference in new issue
Block a user