@@ -6,6 +6,7 @@ use dioxus_cli_config::base_path;
6
6
use dioxus_interpreter_js:: INITIALIZE_STREAMING_JS ;
7
7
use dioxus_isrg:: { CachedRender , IncrementalRendererError , RenderFreshness } ;
8
8
use dioxus_lib:: document:: Document ;
9
+ use dioxus_router:: prelude:: ParseRouteError ;
9
10
use dioxus_ssr:: Renderer ;
10
11
use futures_channel:: mpsc:: Sender ;
11
12
use futures_util:: { Stream , StreamExt } ;
50
51
}
51
52
}
52
53
54
+ /// Errors that can occur during server side rendering before the initial chunk is sent down
55
+ pub enum SSRError {
56
+ /// An error from the incremental renderer. This should result in a 500 code
57
+ Incremental ( IncrementalRendererError ) ,
58
+ /// An error from the dioxus router. This should result in a 404 code
59
+ Routing ( ParseRouteError ) ,
60
+ }
61
+
53
62
struct SsrRendererPool {
54
63
renderers : RwLock < Vec < Renderer > > ,
55
64
incremental_cache : Option < RwLock < dioxus_isrg:: IncrementalRenderer > > ,
@@ -112,7 +121,7 @@ impl SsrRendererPool {
112
121
RenderFreshness ,
113
122
impl Stream < Item = Result < String , dioxus_isrg:: IncrementalRendererError > > ,
114
123
) ,
115
- dioxus_isrg :: IncrementalRendererError ,
124
+ SSRError ,
116
125
> {
117
126
struct ReceiverWithDrop {
118
127
receiver : futures_channel:: mpsc:: Receiver <
@@ -145,6 +154,8 @@ impl SsrRendererPool {
145
154
Result < String , dioxus_isrg:: IncrementalRendererError > ,
146
155
> ( 1000 ) ;
147
156
157
+ let ( initial_result_tx, initial_result_rx) = futures_channel:: oneshot:: channel ( ) ;
158
+
148
159
// before we even spawn anything, we can check synchronously if we have the route cached
149
160
if let Some ( freshness) = self . check_cached_route ( & route, & mut into) {
150
161
return Ok ( (
@@ -188,7 +199,7 @@ impl SsrRendererPool {
188
199
virtual_dom. provide_root_context ( Rc :: new ( history) as Rc < dyn dioxus_history:: History > ) ;
189
200
virtual_dom. provide_root_context ( document. clone ( ) as std:: rc:: Rc < dyn Document > ) ;
190
201
191
- // poll the future , which may call server_context()
202
+ // rebuild the virtual dom , which may call server_context()
192
203
with_server_context ( server_context. clone ( ) , || virtual_dom. rebuild_in_place ( ) ) ;
193
204
194
205
// If streaming is disabled, wait for the virtual dom to finish all suspense work
@@ -197,6 +208,41 @@ impl SsrRendererPool {
197
208
ProvideServerContext :: new ( virtual_dom. wait_for_suspense ( ) , server_context. clone ( ) )
198
209
. await
199
210
}
211
+ // check if there are any errors
212
+ let errors = with_server_context ( server_context. clone ( ) , || {
213
+ virtual_dom. in_runtime ( || {
214
+ let error_context: ErrorContext = ScopeId :: APP
215
+ . consume_context ( )
216
+ . expect ( "The root should be under an error boundary" ) ;
217
+ let errors = error_context. errors ( ) ;
218
+ errors. to_vec ( )
219
+ } )
220
+ } ) ;
221
+ if errors. is_empty ( ) {
222
+ // If routing was successful, we can return a 200 status and render into the stream
223
+ _ = initial_result_tx. send ( Ok ( ( ) ) ) ;
224
+ } else {
225
+ // If there was an error while routing, return the error with a 400 status
226
+ // Return a routing error if any of the errors were a routing error
227
+ let routing_error = errors. iter ( ) . find_map ( |err| err. downcast ( ) . cloned ( ) ) ;
228
+ if let Some ( routing_error) = routing_error {
229
+ _ = initial_result_tx. send ( Err ( SSRError :: Routing ( routing_error) ) ) ;
230
+ return ;
231
+ }
232
+ #[ derive( thiserror:: Error , Debug ) ]
233
+ #[ error( "{0}" ) ]
234
+ pub struct ErrorWhileRendering ( String ) ;
235
+ let mut all_errors = String :: new ( ) ;
236
+ for error in errors {
237
+ all_errors += & error. to_string ( ) ;
238
+ all_errors += "\n "
239
+ }
240
+ let error = ErrorWhileRendering ( all_errors) ;
241
+ _ = initial_result_tx. send ( Err ( SSRError :: Incremental (
242
+ IncrementalRendererError :: Other ( Box :: new ( error) ) ,
243
+ ) ) ) ;
244
+ return ;
245
+ }
200
246
201
247
let mut pre_body = String :: new ( ) ;
202
248
@@ -325,6 +371,11 @@ impl SsrRendererPool {
325
371
myself. renderers . write ( ) . unwrap ( ) . push ( renderer) ;
326
372
} ) ;
327
373
374
+ // Wait for the initial result which determines the status code
375
+ initial_result_rx. await . map_err ( |err| {
376
+ SSRError :: Incremental ( IncrementalRendererError :: Other ( Box :: new ( err) ) )
377
+ } ) ??;
378
+
328
379
Ok ( (
329
380
RenderFreshness :: now ( None ) ,
330
381
ReceiverWithDrop {
@@ -447,7 +498,7 @@ impl SSRState {
447
498
RenderFreshness ,
448
499
impl Stream < Item = Result < String , dioxus_isrg:: IncrementalRendererError > > ,
449
500
) ,
450
- dioxus_isrg :: IncrementalRendererError ,
501
+ SSRError ,
451
502
> {
452
503
self . renderers
453
504
. clone ( )
0 commit comments