Implement Into<CaptureError> for the default server function error type (#4205)

* implement Into<CaptureError> for the server function error type

* document the server function error type

* fix tests
This commit is contained in:
Evan Almloff authored and GitHub committed 2025-06-20 18:37:51 -07:00
1 parent 67f717647f
commit f610c6b8c1
20 files changed
+292 -72

No files matched your search

+1 -1
View File
@@ -196,7 +196,7 @@ pub async fn connect_to_database() -> SqlitePool {
pub type Session = pub type Session =
axum_session_auth::AuthSession<crate::auth::User, i64, SessionSqlitePool, sqlx::SqlitePool>; axum_session_auth::AuthSession<crate::auth::User, i64, SessionSqlitePool, sqlx::SqlitePool>;
pub async fn get_session() -> Result<Session, ServerFnError> { pub async fn get_session() -> ServerFnResult<Session> {
extract::<Session, _>() extract::<Session, _>()
.await .await
.map_err(|_| ServerFnError::new("AuthSessionLayer was not found")) .map_err(|_| ServerFnError::new("AuthSessionLayer was not found"))
+9 -8
View File
@@ -75,7 +75,8 @@ fn app() -> Element {
div { div {
button { onclick: move |_| { button { onclick: move |_| {
async move { async move {
login().await.unwrap(); login().await?;
Ok(())
} }
}, },
"Login Test User" "Login Test User"
@@ -84,9 +85,9 @@ fn app() -> Element {
div { div {
button { button {
onclick: move |_| async move { onclick: move |_| async move {
if let Ok(data) = get_user_name().await { let data = get_user_name().await?;
user_name.set(data); user_name.set(data);
} Ok(())
}, },
"Get User Name" "Get User Name"
} }
@@ -95,9 +96,9 @@ fn app() -> Element {
div { div {
button { button {
onclick: move |_| async move { onclick: move |_| async move {
if let Ok(data) = get_permissions().await { let data = get_permissions().await?;
permissions.set(data); permissions.set(data);
} Ok(())
}, },
"Get Permissions" "Get Permissions"
} }
@@ -107,20 +108,20 @@ fn app() -> Element {
} }
#[server] #[server]
pub async fn get_user_name() -> Result<String, ServerFnError> { pub async fn get_user_name() -> ServerFnResult<String> {
let auth = auth::get_session().await?; let auth = auth::get_session().await?;
Ok(auth.current_user.unwrap().username.to_string()) Ok(auth.current_user.unwrap().username.to_string())
} }
#[server] #[server]
pub async fn login() -> Result<(), ServerFnError> { pub async fn login() -> ServerFnResult {
let auth = auth::get_session().await?; let auth = auth::get_session().await?;
auth.login_user(2); auth.login_user(2);
Ok(()) Ok(())
} }
#[server] #[server]
pub async fn get_permissions() -> Result<String, ServerFnError> { pub async fn get_permissions() -> ServerFnResult<String> {
let method: axum::http::Method = extract().await?; let method: axum::http::Method = extract().await?;
let auth = auth::get_session().await?; let auth = auth::get_session().await?;
let current_user = auth.current_user.clone().unwrap_or_default(); let current_user = auth.current_user.clone().unwrap_or_default();
+7 -7
View File
@@ -18,11 +18,11 @@ pub fn app() -> Element {
button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| count -= 1, "Down low!" }
button { button {
onclick: move |_| async move { onclick: move |_| async move {
if let Ok(data) = get_server_data().await { let data = get_server_data().await?;
println!("Client received: {}", data); println!("Client received: {}", data);
text.set(data.clone()); text.set(data.clone());
post_server_data(data).await.unwrap(); post_server_data(data).await?;
} Ok(())
}, },
"Run a server function" "Run a server function"
} }
@@ -30,14 +30,14 @@ pub fn app() -> Element {
} }
} }
#[server(PostServerData)] #[server]
async fn post_server_data(data: String) -> Result<(), ServerFnError> { async fn post_server_data(data: String) -> ServerFnResult {
println!("Server received: {}", data); println!("Server received: {}", data);
Ok(()) Ok(())
} }
#[server(GetServerData)] #[server]
async fn get_server_data() -> Result<String, ServerFnError> { async fn get_server_data() -> ServerFnResult<String> {
Ok("Hello from the server!".to_string()) Ok("Hello from the server!".to_string())
} }
+5 -5
View File
@@ -20,11 +20,11 @@ fn app() -> Element {
button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| count -= 1, "Down low!" }
button { button {
onclick: move |_| async move { onclick: move |_| async move {
if let Ok(data) = get_server_data().await { let data = get_server_data().await?;
println!("Client received: {}", data); println!("Client received: {}", data);
text.set(data.clone()); text.set(data.clone());
post_server_data(data).await.unwrap(); post_server_data(data).await?;
} Ok(())
}, },
"Run a server function!" "Run a server function!"
} }
@@ -33,14 +33,14 @@ fn app() -> Element {
} }
#[server] #[server]
async fn post_server_data(data: String) -> Result<(), ServerFnError> { async fn post_server_data(data: String) -> ServerFnResult {
println!("Server received: {}", data); println!("Server received: {}", data);
Ok(()) Ok(())
} }
#[server] #[server]
async fn get_server_data() -> Result<String, ServerFnError> { async fn get_server_data() -> ServerFnResult<String> {
Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?) Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?)
} }
+5 -5
View File
@@ -59,11 +59,11 @@ fn Home() -> Element {
button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| count -= 1, "Down low!" }
button { button {
onclick: move |_| async move { onclick: move |_| async move {
if let Ok(data) = get_server_data().await { let data = get_server_data().await?;
println!("Client received: {}", data); println!("Client received: {}", data);
text.set(data.clone()); text.set(data.clone());
post_server_data(data).await.unwrap(); post_server_data(data).await?;
} Ok(())
}, },
"Run server function!" "Run server function!"
} }
@@ -73,13 +73,13 @@ fn Home() -> Element {
} }
#[server(PostServerData)] #[server(PostServerData)]
async fn post_server_data(data: String) -> Result<(), ServerFnError> { async fn post_server_data(data: String) -> ServerFnResult {
println!("Server received: {}", data); println!("Server received: {}", data);
Ok(()) Ok(())
} }
#[server(GetServerData)] #[server(GetServerData)]
async fn get_server_data() -> Result<String, ServerFnError> { async fn get_server_data() -> ServerFnResult<String> {
Ok("Hello from the server!".to_string()) Ok("Hello from the server!".to_string())
} }
+3 -3
View File
@@ -9,13 +9,13 @@ fn app() -> Element {
button { button {
onclick: move |_| async move { onclick: move |_| async move {
response.write().clear(); response.write().clear();
if let Ok(stream) = test_stream().await { let stream = test_stream().await?;
response.write().push_str("Stream started\n"); response.write().push_str("Stream started\n");
let mut stream = stream.into_inner(); let mut stream = stream.into_inner();
while let Some(Ok(text)) = stream.next().await { while let Some(Ok(text)) = stream.next().await {
response.write().push_str(&text); response.write().push_str(&text);
} }
} Ok(())
}, },
"Start stream" "Start stream"
} }
@@ -24,7 +24,7 @@ fn app() -> Element {
} }
#[server(output = StreamingText)] #[server(output = StreamingText)]
pub async fn test_stream() -> Result<TextStream, ServerFnError> { pub async fn test_stream() -> ServerFnResult<TextStream<ServerFnError>> {
let (tx, rx) = futures::channel::mpsc::unbounded(); let (tx, rx) = futures::channel::mpsc::unbounded();
tokio::spawn(async move { tokio::spawn(async move {
loop { loop {
+1 -1
View File
@@ -45,7 +45,7 @@ fn app() -> Element {
#[server(protocol = Websocket<JsonEncoding, JsonEncoding>)] #[server(protocol = Websocket<JsonEncoding, JsonEncoding>)]
async fn uppercase_ws( async fn uppercase_ws(
input: BoxedStream<String, ServerFnError>, input: BoxedStream<String, ServerFnError>,
) -> Result<BoxedStream<String, ServerFnError>, ServerFnError> { ) -> ServerFnResult<BoxedStream<String, ServerFnError>> {
let mut input = input; let mut input = input;
// Create a channel with the output of the websocket // Create a channel with the output of the websocket
+2 -2
View File
@@ -58,7 +58,7 @@ fn App() -> Element {
} }
#[server] #[server]
async fn get_meaning(of: String) -> Result<Option<u32>, ServerFnError> { async fn get_meaning(of: String) -> ServerFnResult<Option<u32>> {
Ok(of.contains("life").then(|| 42)) Ok(of.contains("life").then(|| 42))
} }
``` ```
@@ -131,7 +131,7 @@ fn App() -> Element {
} }
#[server] #[server]
async fn get_meaning(of: String) -> Result<Option<u32>, ServerFnError> { async fn get_meaning(of: String) -> ServerFnResult<Option<u32>> {
Ok(of.contains("life").then(|| 42)) Ok(of.contains("life").then(|| 42))
} }
``` ```
+1 -1
View File
@@ -23,7 +23,7 @@ fn PrintHtmlRequestInfo() -> Element {
```rust ```rust
# use dioxus::prelude::*; # use dioxus::prelude::*;
#[server] #[server]
async fn read_headers() -> Result<(), ServerFnError> { async fn read_headers() -> ServerFnResult {
// Since we are calling this from a server function, the server context that is may be from the // Since we are calling this from a server function, the server context that is may be from the
// initial request or a request from the client // initial request or a request from the client
let context = server_context(); let context = server_context();
+221
View File
@@ -0,0 +1,221 @@
use std::{
error::Error,
fmt::{Debug, Display},
str::FromStr,
};
use dioxus_lib::prelude::{dioxus_core::CapturedError, RenderError};
use serde::{de::DeserializeOwned, Serialize};
use server_fn::{
codec::JsonEncoding,
error::{FromServerFnError, ServerFnErrorErr},
};
/// A default result type for server functions, which can either be successful or contain an error. The [`ServerFnResult`] type
/// is a convenient alias for a `Result` type that uses [`ServerFnError`] as the error type.
///
/// # Example
/// ```rust
/// use dioxus::prelude::*;
///
/// #[server]
/// async fn parse_number(number: String) -> ServerFnResult<f32> {
/// let parsed_number: f32 = number.parse()?;
/// Ok(parsed_number)
/// }
/// ```
pub type ServerFnResult<T = (), E = String> = std::result::Result<T, ServerFnError<E>>;
/// An error type for server functions. This may either be an error that occurred while running the server
/// function logic, or an error that occurred while communicating with the server inside the server function crate.
///
/// ## Usage
///
/// You can use the [`ServerFnError`] type in the Error type of your server function result or use the [`ServerFnResult`]
/// type as the return type of your server function. When you call the server function, you can handle the error directly
/// or convert it into a [`CapturedError`] to throw into the nearest [`ErrorBoundary`](dioxus_lib::prelude::ErrorBoundary).
///
/// ```rust
/// use dioxus::prelude::*;
///
/// #[server]
/// async fn parse_number(number: String) -> ServerFnResult<f32> {
/// // You can convert any error type into the `ServerFnError` with the `?` operator
/// let parsed_number: f32 = number.parse()?;
/// Ok(parsed_number)
/// }
///
/// #[component]
/// fn ParseNumberServer() -> Element {
/// let mut number = use_signal(|| "42".to_string());
/// let mut parsed = use_signal(|| None);
///
/// rsx! {
/// input {
/// value: "{number}",
/// oninput: move |e| number.set(e.value()),
/// }
/// button {
/// onclick: move |_| async move {
/// // Call the server function to parse the number
/// // If the result is Ok, continue running the closure, otherwise bubble up the
/// // error to the nearest error boundary with `?`
/// let result = parse_number(number()).await?;
/// parsed.set(Some(result));
/// Ok(())
/// },
/// "Parse Number"
/// }
/// if let Some(value) = parsed() {
/// p { "Parsed number: {value}" }
/// } else {
/// p { "No number parsed yet." }
/// }
/// }
/// }
/// ```
///
/// ## Differences from [`CapturedError`]
///
/// Both this error type and [`CapturedError`] can be used to represent boxed errors in dioxus. However, this error type
/// is more strict about the kinds of errors it can represent. [`CapturedError`] can represent any error that implements
/// the [`Error`] trait or can be converted to a string. [`CapturedError`] holds onto the type information of the error
/// and lets you downcast the error to its original type.
///
/// [`ServerFnError`] represents server function errors as [`String`]s by default without any additional type information.
/// This makes it easy to serialize the error to JSON and send it over the wire, but it means that you can't get the
/// original type information of the error back. If you need to preserve the type information of the error, you can use a
/// [custom error variant](#custom-error-variants) that holds onto the type information.
///
/// ## Custom error variants
///
/// The [`ServerFnError`] type accepts a generic type parameter `T` that is used to represent the error type used for server
/// functions. If you need to keep the type information of your error, you can create a custom error variant that implements
/// [`Serialize`] and [`DeserializeOwned`]. This allows you to serialize the error to JSON and send it over the wire,
/// while still preserving the type information.
///
/// ```rust
/// use dioxus::prelude::*;
/// use serde::{Deserialize, Serialize};
/// use std::fmt::Debug;
///
/// #[derive(Clone, Debug, Serialize, Deserialize)]
/// pub struct MyCustomError {
/// message: String,
/// code: u32,
/// }
///
/// impl MyCustomError {
/// pub fn new(message: String, code: u32) -> Self {
/// Self { message, code }
/// }
/// }
///
/// #[server]
/// async fn server_function() -> ServerFnResult<String, MyCustomError> {
/// // Return your custom error
/// Err(ServerFnError::ServerError(MyCustomError::new(
/// "An error occurred".to_string(),
/// 404,
/// )))
/// }
/// ```
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum ServerFnError<T = String> {
/// An error running the server function
ServerError(T),
/// An error communicating with the server
CommunicationError(ServerFnErrorErr),
}
impl ServerFnError {
/// Creates a new `ServerFnError` from something that implements `ToString`.
///
/// # Examples
/// ```rust
/// use dioxus::prelude::*;
///
/// #[server]
/// async fn server_function() -> ServerFnResult<String, MyCustomError> {
/// // Return your custom error
/// Err(ServerFnError::new("Something went wrong"))
/// }
/// ```
pub fn new(error: impl ToString) -> Self {
Self::ServerError(error.to_string())
}
}
impl<T> ServerFnError<T> {
/// Returns true if the error is a server error
pub fn is_server_error(&self) -> bool {
matches!(self, ServerFnError::ServerError(_))
}
/// Returns true if the error is a communication error
pub fn is_communication_error(&self) -> bool {
matches!(self, ServerFnError::CommunicationError(_))
}
/// Returns a reference to the server error if it is a server error
/// or `None` if it is a communication error.
pub fn server_error(&self) -> Option<&T> {
match self {
ServerFnError::ServerError(err) => Some(err),
ServerFnError::CommunicationError(_) => None,
}
}
/// Returns a reference to the communication error if it is a communication error
/// or `None` if it is a server error.
pub fn communication_error(&self) -> Option<&ServerFnErrorErr> {
match self {
ServerFnError::ServerError(_) => None,
ServerFnError::CommunicationError(err) => Some(err),
}
}
}
impl<T: Serialize + DeserializeOwned + Debug + 'static> FromServerFnError for ServerFnError<T> {
type Encoder = JsonEncoding;
fn from_server_fn_error(err: ServerFnErrorErr) -> Self {
Self::CommunicationError(err)
}
}
impl<T: FromStr> FromStr for ServerFnError<T> {
type Err = <T as FromStr>::Err;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
std::result::Result::Ok(Self::ServerError(T::from_str(s)?))
}
}
impl<T: Display> Display for ServerFnError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ServerFnError::ServerError(err) => write!(f, "Server error: {}", err),
ServerFnError::CommunicationError(err) => write!(f, "Communication error: {}", err),
}
}
}
impl From<ServerFnError> for CapturedError {
fn from(error: ServerFnError) -> Self {
Self::from_display(error)
}
}
impl From<ServerFnError> for RenderError {
fn from(error: ServerFnError) -> Self {
RenderError::Aborted(CapturedError::from(error))
}
}
impl<E: Error> From<E> for ServerFnError {
fn from(error: E) -> Self {
Self::ServerError(error.to_string())
}
}
+5 -2
View File
@@ -1,12 +1,14 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
#![deny(missing_docs)] #![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(all(feature = "web", feature = "document"))] #[cfg(all(feature = "web", feature = "document"))]
mod web; mod web;
mod error;
#[cfg(all(feature = "web", feature = "document"))] #[cfg(all(feature = "web", feature = "document"))]
pub use web::FullstackWebDocument; pub use web::FullstackWebDocument;
@@ -19,8 +21,9 @@ pub use dioxus_fullstack_hooks::history::FullstackHistory;
pub mod prelude { pub mod prelude {
pub use dioxus_fullstack_hooks::prelude::*; pub use dioxus_fullstack_hooks::prelude::*;
pub use crate::error::{ServerFnError, ServerFnResult};
pub use dioxus_server_macro::*; pub use dioxus_server_macro::*;
pub use server_fn::{self, ServerFn as _, ServerFnError}; pub use server_fn::{self, ServerFn as _};
#[cfg(feature = "server")] #[cfg(feature = "server")]
pub use dioxus_server::prelude::*; pub use dioxus_server::prelude::*;
@@ -44,6 +44,6 @@ fn current_platform_features() -> Vec<String> {
} }
#[server] #[server]
async fn get_server_features() -> Result<Vec<String>, ServerFnError> { async fn get_server_features() -> ServerFnResult<Vec<String>> {
Ok(current_platform_features()) Ok(current_platform_features())
} }
@@ -5,12 +5,9 @@
// - Hydration // - Hydration
#![allow(non_snake_case)] #![allow(non_snake_case)]
use dioxus::{ use dioxus::prelude::{
prelude::{
server_fn::{codec::JsonEncoding, BoxedStream, Websocket}, server_fn::{codec::JsonEncoding, BoxedStream, Websocket},
*, *,
},
CapturedError,
}; };
use futures::{channel::mpsc, SinkExt, StreamExt}; use futures::{channel::mpsc, SinkExt, StreamExt};
@@ -85,7 +82,7 @@ async fn assert_server_context_provided() {
} }
#[server(PostServerData)] #[server(PostServerData)]
async fn post_server_data(data: String) -> Result<(), ServerFnError> { async fn post_server_data(data: String) -> ServerFnResult {
assert_server_context_provided().await; assert_server_context_provided().await;
println!("Server received: {}", data); println!("Server received: {}", data);
@@ -93,7 +90,7 @@ async fn post_server_data(data: String) -> Result<(), ServerFnError> {
} }
#[server(GetServerData)] #[server(GetServerData)]
async fn get_server_data() -> Result<String, ServerFnError> { async fn get_server_data() -> ServerFnResult<String> {
assert_server_context_provided().await; assert_server_context_provided().await;
Ok("Hello from the server!".to_string()) Ok("Hello from the server!".to_string())
} }
@@ -101,14 +98,14 @@ async fn get_server_data() -> Result<String, ServerFnError> {
// Make sure the default codec work with empty data structures // Make sure the default codec work with empty data structures
// Regression test for https://github.com/DioxusLabs/dioxus/issues/2628 // Regression test for https://github.com/DioxusLabs/dioxus/issues/2628
#[server] #[server]
async fn get_server_data_empty_vec(empty_vec: Vec<String>) -> Result<Vec<String>, ServerFnError> { async fn get_server_data_empty_vec(empty_vec: Vec<String>) -> ServerFnResult<Vec<String>> {
assert_server_context_provided().await; assert_server_context_provided().await;
assert!(empty_vec.is_empty()); assert!(empty_vec.is_empty());
Ok(Vec::new()) Ok(Vec::new())
} }
#[server] #[server]
async fn server_error() -> Result<String, ServerFnError> { async fn server_error() -> ServerFnResult<String> {
assert_server_context_provided().await; assert_server_context_provided().await;
tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
Err(ServerFnError::new("the server threw an error!")) Err(ServerFnError::new("the server threw an error!"))
@@ -142,9 +139,7 @@ fn Errors() -> Element {
#[component] #[component]
pub fn ThrowsError() -> Element { pub fn ThrowsError() -> Element {
use_server_future(server_error)? use_server_future(server_error)?.unwrap()?;
.unwrap()
.map_err(CapturedError::from_display)?;
rsx! { rsx! {
"success" "success"
} }
@@ -187,7 +182,7 @@ fn Assets() -> Element {
#[server(protocol = Websocket<JsonEncoding, JsonEncoding>)] #[server(protocol = Websocket<JsonEncoding, JsonEncoding>)]
async fn echo_ws( async fn echo_ws(
input: BoxedStream<String, ServerFnError>, input: BoxedStream<String, ServerFnError>,
) -> Result<BoxedStream<String, ServerFnError>, ServerFnError> { ) -> ServerFnResult<BoxedStream<String, ServerFnError>> {
let mut input = input; let mut input = input;
let (mut tx, rx) = mpsc::channel(1); let (mut tx, rx) = mpsc::channel(1);
@@ -83,7 +83,7 @@ pub struct Content {
} }
#[server] #[server]
async fn server_content(id: usize) -> Result<Content, ServerFnError> { async fn server_content(id: usize) -> ServerFnResult<Content> {
let content_tree = [ let content_tree = [
Content { Content {
title: "The robot says hello world".to_string(), title: "The robot says hello world".to_string(),
@@ -25,6 +25,6 @@ fn main() {
} }
#[server(endpoint = "static_routes")] #[server(endpoint = "static_routes")]
async fn static_routes() -> Result<Vec<String>, ServerFnError> { async fn static_routes() -> ServerFnResult<Vec<String>> {
Ok(vec!["/".to_string()]) Ok(vec!["/".to_string()])
} }
+6 -6
View File
@@ -18,11 +18,11 @@ use syn::{__private::ToTokens, parse_quote};
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// # #[derive(serde::Deserialize, serde::Serialize)] /// # #[derive(serde::Deserialize, serde::Serialize)]
/// # pub struct BlogPost; /// # pub struct BlogPost;
/// # async fn load_posts(category: &str) -> Result<Vec<BlogPost>, ServerFnError> { unimplemented!() } /// # async fn load_posts(category: &str) -> ServerFnResult<Vec<BlogPost>> { unimplemented!() }
/// #[server] /// #[server]
/// pub async fn blog_posts( /// pub async fn blog_posts(
/// category: String, /// category: String,
/// ) -> Result<Vec<BlogPost>, ServerFnError> { /// ) -> ServerFnResult<Vec<BlogPost>> {
/// let posts = load_posts(&category).await?; /// let posts = load_posts(&category).await?;
/// // maybe do some other work /// // maybe do some other work
/// Ok(posts) /// Ok(posts)
@@ -118,7 +118,7 @@ use syn::{__private::ToTokens, parse_quote};
/// input = Cbor, /// input = Cbor,
/// output = Json /// output = Json
/// )] /// )]
/// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<usize, ServerFnError> { /// pub async fn my_wacky_server_fn(input: Vec<String>) -> ServerFnResult<usize> {
/// unimplemented!() /// unimplemented!()
/// } /// }
/// ///
@@ -155,7 +155,7 @@ use syn::{__private::ToTokens, parse_quote};
/// #[server] /// #[server]
/// // The TraceLayer will log all requests to the console /// // The TraceLayer will log all requests to the console
/// #[middleware(tower_http::timeout::TimeoutLayer::new(std::time::Duration::from_secs(5)))] /// #[middleware(tower_http::timeout::TimeoutLayer::new(std::time::Duration::from_secs(5)))]
/// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<usize, ServerFnError> { /// pub async fn my_wacky_server_fn(input: Vec<String>) -> ServerFnResult<usize> {
/// unimplemented!() /// unimplemented!()
/// } /// }
/// ``` /// ```
@@ -171,7 +171,7 @@ use syn::{__private::ToTokens, parse_quote};
/// ```rust,ignore /// ```rust,ignore
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<String, ServerFnError> { /// pub async fn my_wacky_server_fn(input: Vec<String>) -> ServerFnResult<String> {
/// let headers: axum::http::header::HeaderMap = extract().await?; /// let headers: axum::http::header::HeaderMap = extract().await?;
/// Ok(format!("The server got a request with headers: {:?}", headers)) /// Ok(format!("The server got a request with headers: {:?}", headers))
/// } /// }
@@ -198,7 +198,7 @@ use syn::{__private::ToTokens, parse_quote};
/// } /// }
/// ///
/// #[server] /// #[server]
/// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<String, ServerFnError> { /// pub async fn my_wacky_server_fn(input: Vec<String>) -> ServerFnResult<String> {
/// let FromContext(pool): FromContext<DatabasePool> = extract().await?; /// let FromContext(pool): FromContext<DatabasePool> = extract().await?;
/// Ok(format!("The server read {:?} from the shared context", pool)) /// Ok(format!("The server read {:?} from the shared context", pool))
/// } /// }
+1 -1
View File
@@ -23,7 +23,7 @@ fn PrintHtmlRequestInfo() -> Element {
```rust ```rust
# use dioxus::prelude::*; # use dioxus::prelude::*;
#[server] #[server]
async fn read_headers() -> Result<(), ServerFnError> { async fn read_headers() -> ServerFnResult {
// Since we are calling this from a server function, the server context that is may be from the // Since we are calling this from a server function, the server context that is may be from the
// initial request or a request from the client // initial request or a request from the client
let context = server_context(); let context = server_context();
+3 -3
View File
@@ -157,7 +157,7 @@ impl ServeConfigBuilder {
/// } /// }
/// ///
/// #[server] /// #[server]
/// async fn read_context() -> Result<u32, ServerFnError> { /// async fn read_context() -> ServerFnResult<u32> {
/// // You can extract values from the server context with the `extract` function /// // You can extract values from the server context with the `extract` function
/// let FromContext(value) = extract().await?; /// let FromContext(value) = extract().await?;
/// Ok(value) /// Ok(value)
@@ -224,7 +224,7 @@ impl ServeConfigBuilder {
/// } /// }
/// ///
/// #[server] /// #[server]
/// async fn read_context() -> Result<u32, ServerFnError> { /// async fn read_context() -> ServerFnResult<u32> {
/// // You can extract values from the server context with the `extract` function /// // You can extract values from the server context with the `extract` function
/// let FromContext(value) = extract().await?; /// let FromContext(value) = extract().await?;
/// Ok(value) /// Ok(value)
@@ -284,7 +284,7 @@ impl ServeConfigBuilder {
/// } /// }
/// ///
/// #[server] /// #[server]
/// async fn read_context() -> Result<u32, ServerFnError> { /// async fn read_context() -> ServerFnResult<u32> {
/// // You can extract values from the server context with the `extract` function /// // You can extract values from the server context with the `extract` function
/// let FromContext(value) = extract().await?; /// let FromContext(value) = extract().await?;
/// Ok(value) /// Ok(value)
+11 -11
View File
@@ -48,7 +48,7 @@ impl AtomicResponsePartsModified {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn read_headers() -> Result<(), ServerFnError> { /// async fn read_headers() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// let headers: http::HeaderMap = server_context.extract().await?; /// let headers: http::HeaderMap = server_context.extract().await?;
/// println!("{:?}", headers); /// println!("{:?}", headers);
@@ -141,7 +141,7 @@ mod server_fn_impl {
/// .launch(app); /// .launch(app);
/// ///
/// #[server] /// #[server]
/// async fn read_context() -> Result<u32, ServerFnError> { /// async fn read_context() -> ServerFnResult<u32> {
/// // You can extract values from the server context with the `extract` function /// // You can extract values from the server context with the `extract` function
/// let FromContext(value) = extract().await?; /// let FromContext(value) = extract().await?;
/// Ok(value) /// Ok(value)
@@ -201,7 +201,7 @@ mod server_fn_impl {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn set_headers() -> Result<(), ServerFnError> { /// async fn set_headers() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// let response_parts = server_context.response_parts(); /// let response_parts = server_context.response_parts();
/// let cookies = response_parts /// let cookies = response_parts
@@ -225,7 +225,7 @@ mod server_fn_impl {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn set_headers() -> Result<(), ServerFnError> { /// async fn set_headers() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// server_context.headers_mut() /// server_context.headers_mut()
/// .insert("Cookie", http::HeaderValue::from_static("dioxus=fullstack")); /// .insert("Cookie", http::HeaderValue::from_static("dioxus=fullstack"));
@@ -247,7 +247,7 @@ mod server_fn_impl {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn set_status() -> Result<(), ServerFnError> { /// async fn set_status() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// *server_context.status_mut() = http::StatusCode::INTERNAL_SERVER_ERROR; /// *server_context.status_mut() = http::StatusCode::INTERNAL_SERVER_ERROR;
/// Ok(()) /// Ok(())
@@ -268,7 +268,7 @@ mod server_fn_impl {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn set_version() -> Result<(), ServerFnError> { /// async fn set_version() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// *server_context.version_mut() = http::Version::HTTP_2; /// *server_context.version_mut() = http::Version::HTTP_2;
/// Ok(()) /// Ok(())
@@ -289,7 +289,7 @@ mod server_fn_impl {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn set_version() -> Result<(), ServerFnError> { /// async fn set_version() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// *server_context.version_mut() = http::Version::HTTP_2; /// *server_context.version_mut() = http::Version::HTTP_2;
/// Ok(()) /// Ok(())
@@ -323,7 +323,7 @@ mod server_fn_impl {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn read_headers() -> Result<(), ServerFnError> { /// async fn read_headers() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// let request_parts = server_context.request_parts(); /// let request_parts = server_context.request_parts();
/// let id: &i32 = request_parts /// let id: &i32 = request_parts
@@ -347,7 +347,7 @@ mod server_fn_impl {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn read_headers() -> Result<(), ServerFnError> { /// async fn read_headers() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// let id: i32 = server_context.request_parts_mut() /// let id: i32 = server_context.request_parts_mut()
/// .extensions /// .extensions
@@ -370,7 +370,7 @@ mod server_fn_impl {
/// ```rust, no_run /// ```rust, no_run
/// # use dioxus::prelude::*; /// # use dioxus::prelude::*;
/// #[server] /// #[server]
/// async fn read_headers() -> Result<(), ServerFnError> { /// async fn read_headers() -> ServerFnResult {
/// let server_context = server_context(); /// let server_context = server_context();
/// let headers: http::HeaderMap = server_context.extract().await?; /// let headers: http::HeaderMap = server_context.extract().await?;
/// println!("{:?}", headers); /// println!("{:?}", headers);
@@ -526,7 +526,7 @@ impl<T: 'static> std::error::Error for NotFoundInServerContext<T> {}
/// .launch(app); /// .launch(app);
/// ///
/// #[server] /// #[server]
/// async fn read_context() -> Result<u32, ServerFnError> { /// async fn read_context() -> ServerFnResult<u32> {
/// // You can extract values from the server context with the `extract` function /// // You can extract values from the server context with the `extract` function
/// let FromContext(value) = extract().await?; /// let FromContext(value) = extract().await?;
/// Ok(value) /// Ok(value)
+1 -1
View File
@@ -50,7 +50,7 @@
//! } //! }
//! //!
//! #[server(GetServerData)] //! #[server(GetServerData)]
//! async fn get_server_data() -> Result<String, ServerFnError> { //! async fn get_server_data() -> ServerFnResult<String> {
//! Ok("Hello from the server!".to_string()) //! Ok("Hello from the server!".to_string())
//! } //! }
//! ``` //! ```