@@ -81,6 +81,23 @@ pub enum ServerFnError {
8181 /// Occurs on the server if there is an error creating an HTTP response.
8282 #[ error( "error creating response {0}" ) ]
8383 Response ( String ) ,
84+
85+ /// A redirect response returned while running a server function or extractor.
86+ ///
87+ /// This is treated as control-flow rather than an ordinary error so we can preserve the
88+ /// `Location` header (which is otherwise lost when converting axum responses into `ServerFnError`).
89+ #[ error( "redirect ({code}) to {location}" ) ]
90+ Redirect {
91+ /// HTTP status code associated with the redirect (typically 302/303/307/308).
92+ code : u16 ,
93+ /// The value of the `Location` header.
94+ location : String ,
95+ /// Marker source to let core treat this as expected control-flow (not an error log).
96+ #[ doc( hidden) ]
97+ #[ serde( skip, default ) ]
98+ #[ source]
99+ control_flow : dioxus_core:: RedirectControlFlow ,
100+ } ,
84101}
85102
86103impl ServerFnError {
@@ -93,6 +110,15 @@ impl ServerFnError {
93110 }
94111 }
95112
113+ /// Create a redirect error (control-flow) with a status code and `Location`.
114+ pub fn redirect ( code : u16 , location : impl Into < String > ) -> Self {
115+ ServerFnError :: Redirect {
116+ code,
117+ location : location. into ( ) ,
118+ control_flow : dioxus_core:: RedirectControlFlow :: default ( ) ,
119+ }
120+ }
121+
96122 /// Create a new server error (status code 500) with a message and details.
97123 pub async fn from_axum_response ( resp : axum_core:: response:: Response ) -> Self {
98124 let status = resp. status ( ) ;
@@ -138,6 +164,9 @@ impl From<ServerFnError> for http::StatusCode {
138164 ServerFnError :: ServerError { code, .. } => {
139165 http:: StatusCode :: from_u16 ( code) . unwrap_or ( http:: StatusCode :: INTERNAL_SERVER_ERROR )
140166 }
167+ ServerFnError :: Redirect { code, .. } => {
168+ http:: StatusCode :: from_u16 ( code) . unwrap_or ( http:: StatusCode :: INTERNAL_SERVER_ERROR )
169+ }
141170 ServerFnError :: Request ( err) => match err {
142171 RequestError :: Status ( _, code) => http:: StatusCode :: from_u16 ( code)
143172 . unwrap_or ( http:: StatusCode :: INTERNAL_SERVER_ERROR ) ,
@@ -166,6 +195,7 @@ impl From<ServerFnError> for HttpError {
166195 fn from ( value : ServerFnError ) -> Self {
167196 let status = StatusCode :: from_u16 ( match & value {
168197 ServerFnError :: ServerError { code, .. } => * code,
198+ ServerFnError :: Redirect { code, .. } => * code,
169199 _ => 500 ,
170200 } )
171201 . unwrap_or ( StatusCode :: INTERNAL_SERVER_ERROR ) ;
@@ -195,6 +225,23 @@ impl From<HttpError> for ServerFnError {
195225impl IntoResponse for ServerFnError {
196226 fn into_response ( self ) -> axum_core:: response:: Response {
197227 match self {
228+ Self :: Redirect { code, location, .. } => {
229+ use http:: header:: LOCATION ;
230+ let status =
231+ StatusCode :: from_u16 ( code) . unwrap_or ( StatusCode :: INTERNAL_SERVER_ERROR ) ;
232+ axum_core:: response:: Response :: builder ( )
233+ . status ( status)
234+ . header ( LOCATION , location)
235+ . body ( axum_core:: body:: Body :: empty ( ) )
236+ . unwrap_or_else ( |_| {
237+ axum_core:: response:: Response :: builder ( )
238+ . status ( StatusCode :: INTERNAL_SERVER_ERROR )
239+ . body ( axum_core:: body:: Body :: from (
240+ "{\" error\" :\" Internal Server Error\" }" ,
241+ ) )
242+ . unwrap ( )
243+ } )
244+ }
198245 Self :: ServerError {
199246 message,
200247 code,
0 commit comments