fix: server function custom errors set status codes (#4782)
* fix: handling status codes from errors * add demo * clippy + check * fix clippy again
This commit is contained in:
1 parent
fc069f0faf
commit
8f6e7dc1f2
9 files changed
+356
-44
No files matched your search
@@ -699,6 +699,11 @@ name = "flat_router"
|
||||
path = "examples/06-routing/flat_router.rs"
|
||||
doc-scrape-examples = true
|
||||
|
||||
[[example]]
|
||||
name = "server_functions"
|
||||
path = "examples/07-fullstack/server_functions.rs"
|
||||
doc-scrape-examples = true
|
||||
|
||||
[[example]]
|
||||
name = "middleware"
|
||||
path = "examples/07-fullstack/middleware.rs"
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
//!
|
||||
//! Try running requests against the endpoints directly with `curl` or `postman` to see the actual HTTP responses!
|
||||
|
||||
use dioxus::fullstack::{Json, StatusCode};
|
||||
use dioxus::fullstack::{AsStatusCode, Json, StatusCode};
|
||||
use dioxus::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -164,10 +164,32 @@ enum MyCustomError {
|
||||
ServerFnError(#[from] ServerFnError),
|
||||
}
|
||||
|
||||
impl AsStatusCode for MyCustomError {
|
||||
fn as_status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
MyCustomError::BadRequest { .. } => StatusCode::BAD_REQUEST,
|
||||
MyCustomError::NotFound => StatusCode::NOT_FOUND,
|
||||
MyCustomError::ServerFnError(e) => e.as_status_code(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a custom typed error (serializable) so clients can handle specific cases.
|
||||
///
|
||||
/// Our custom error must implement `AsStatusCode` so it can properly set the outgoing HTTP status code.
|
||||
#[get("/api/typed-error")]
|
||||
async fn get_throws_typed_error() -> Result<(), MyCustomError> {
|
||||
Err(MyCustomError::BadRequest {
|
||||
custom_name: "Invalid input".into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Simple POST endpoint used to show a successful server function that returns `StatusCode`.
|
||||
#[post("/api/data")]
|
||||
async fn get_throws_serverfn_error() -> Result<(), ServerFnError> {
|
||||
Err(ServerFnError::ServerError {
|
||||
message: "Unauthorized access".to_string(),
|
||||
code: StatusCode::UNAUTHORIZED.as_u16(),
|
||||
details: None,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
//! This example is a simple showcase of Dioxus Server Functions.
|
||||
//!
|
||||
//! The other examples in this folder showcase advanced features of server functions like custom
|
||||
//! data types, error handling, websockets, and more.
|
||||
//!
|
||||
//! This example is meant to just be a simple starting point to show how server functions work.
|
||||
//!
|
||||
//! ## Server Functions
|
||||
//!
|
||||
//! In Dioxus, Server Functions are `axum` backend endpoints that can be called directly from the client
|
||||
//! as if you were simply calling a local Rust function. You can do anything with a server function
|
||||
//! that an Axum handler can do like extracting path, query, headers, and body parameters.
|
||||
//!
|
||||
//! ## Server Function Arguments
|
||||
//!
|
||||
//! Unlike Axum handlers, the arguments of the server functions have some special magic enabled by
|
||||
//! the accompanying `#[get]`/`#[post]` attributes. This magic enables you to choose between
|
||||
//! arguments that are purely serializable (i.e. `String`, `i32`, `Vec<T>`, etc) as the JSON body of
|
||||
//!
|
||||
//! the request *or* arguments that implement Axum's `FromRequest` trait. This magic enables simple
|
||||
//! RPC functions but also complex extractors for things like auth, sessions, cookies, and more.
|
||||
//!
|
||||
//! ## Server Function Return Types
|
||||
//!
|
||||
//! The return type of the server function is also somewhat magical. Unlike Axum handlers, all server
|
||||
//! functions must return a `Result` type, giving the client an opportunity to handle errors properly.
|
||||
//!
|
||||
//! The `Ok` type can be anything that implements `Serialize + DeserializeOwned` so it can be sent
|
||||
//! to the client as JSON, or it can be anything that implements `IntoResponse` just like an Axum handler.
|
||||
//!
|
||||
//! ## Error Types
|
||||
//!
|
||||
//! The `Err` type of the server function return type is also somewhat special. The `Err` type can be:
|
||||
//! - `anyhow::Error` (the `dioxus_core::Err` type alias) for untyped errors with rich context. Note
|
||||
//! that these errors will always downcast to `ServerFnError` on the client, losing the original
|
||||
//! error stack and type.
|
||||
//! - `ServerFnError` for typed errors with a status code and optional message.
|
||||
//! - `StatusCode` for returning raw HTTP status codes.
|
||||
//! - `HttpError` for returning HTTP status codes with custom messages.
|
||||
//! - Any custom errors that implement `From<ServerFnError>` and are `Serialize`/`Deserialize`
|
||||
//!
|
||||
//! The only way to set the HTTP status code of the response is to use one of the above error types,
|
||||
//! or to implement a custom `IntoResponse` type that sets the status code manually.
|
||||
//!
|
||||
//! The `anyhow::Error` type is the best choice for rapid development, but is somewhat limited when
|
||||
//! handling specific error cases on the client since all errors are downcast to `ServerFnError`.
|
||||
//!
|
||||
//! ## Calling Server Functions from the Client
|
||||
//!
|
||||
//! Server functions can be called from the client by simply importing the function and calling it
|
||||
//! like a normal Rust function. Unlike regular axum handlers, Dioxus server functions have a few
|
||||
//! non-obvious restrictions.
|
||||
//!
|
||||
//! Most importantly, the arguments to the server function must implement either `Deserialize` *or*
|
||||
//! `IntoRequest`. The `IntoRequest` trait is a Dioxus abstraction that represents the "inverse" of the
|
||||
//! Axum `FromRequest` trait. Anything that is sent to the server from the client must be both extractable
|
||||
//! with `FromRequest` on the server *and* constructible with `IntoRequest` on the client.
|
||||
//!
|
||||
//! Types like `WebsocketOptions` implement `IntoRequest` and pass along things like upgrade headers
|
||||
//! to the server so that the server can properly upgrade the connection.
|
||||
//!
|
||||
//! When receiving data from the server, the return type must implement `Deserialize` *or* `FromResponse`.
|
||||
//! The `FromResponse` trait is the inverse of Axum's `IntoResponse` trait, and is implemented
|
||||
//! for types like `Websocket` where the raw HTTP response is needed to complete the construction
|
||||
//! of the type.
|
||||
//!
|
||||
//! ## Server-only Extractors
|
||||
//!
|
||||
//! Because the arguments of the server function define the structure of the public API, some extractors
|
||||
//! might not make sense to expose directly, nor would they be possible to construct on the client.
|
||||
//! For example, on the web, you typically don't work directly with cookies since the browser handles
|
||||
//! them for you. In these cases, the client would omit the `Cookie` header entirely, and we would need
|
||||
//! "hoist" our extractor into a "server-only extractor".
|
||||
//!
|
||||
//! Server-only extractors are function arguments placed after the path in the `#[get]`/`#[post]` attribute.
|
||||
//! These arguments are extracted on the server, but not passed in from the client. This lets the
|
||||
//! server function remain callable from the client, while still allowing full access to axum's
|
||||
//! extractors.
|
||||
//!
|
||||
//! ```
|
||||
//! #[post("/api/authenticate", auth: AuthCookie)]
|
||||
//! async fn authenticate() -> Result<User> { /* ... */ }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Automatic Registration
|
||||
//!
|
||||
//! Unlike axum handlers, server functions do not need to be manually registered with a router.
|
||||
//! By default, *all* server functions in your app will be automatically registered with the
|
||||
//! server when you call `dioxus::launch` or create a router manually with `dioxus::server::router()`.
|
||||
//!
|
||||
//! However, not all server functions are automatically registered by default. Server functions that
|
||||
//! take a `State<T>` extractor cannot be automatically added to the router since the dioxus router
|
||||
//! type does not know how to construct the `T` type.
|
||||
//!
|
||||
//! These server functions will be registered once the `ServerFnState<T>` layer is added to the app with
|
||||
//! `router = router.layer(ServerFnState::new(your_state))`.
|
||||
//!
|
||||
//! ## Middleware
|
||||
//!
|
||||
//! Middleware can be added to server functions using the `#[middleware(MiddlewareType)]` attribute.
|
||||
//! Middleware will be applied in the order they are specified, and will be applied before any
|
||||
//! server-only extractors.
|
||||
//!
|
||||
//! To add router-level middleware, you can customize the axum `Router` using layers and extensions
|
||||
//! as you would in a normal axum app.
|
||||
//!
|
||||
//! ## Anonymous Server Functions
|
||||
//!
|
||||
//! The `#[server]` attribute can be used without a path to create an anonymous server function.
|
||||
//! These functions are still exposed as HTTP endpoints, but their names are procedurally generated
|
||||
//! from the module path, function name, and a hash of the function signature. This makes it hard to
|
||||
//! call these functions with `curl` or `postman`, but save you the trouble of coming up with unique
|
||||
//! names for simple functions that are only called from your Dioxus app.
|
||||
//!
|
||||
//! If you're shipping desktop/mobile apps, we don't recommend using anonymous server functions
|
||||
//! since the function names could change between builds and thus make older versions of your app
|
||||
//! incompatible with newer versions of your server.
|
||||
//!
|
||||
//! ## Cross-platform Clients
|
||||
//!
|
||||
//! Server functions can be called from any platform (web, desktop mobile, etc) and use the best
|
||||
//! underlying `fetch` implementation available.
|
||||
//!
|
||||
//! ## More examples
|
||||
//!
|
||||
//! With Dioxus Fullstack 0.7, pretty much anything you can do with an Axum handler, you can do with
|
||||
//! a server function. More advanced examples can be found in this folder showcasing custom data types,
|
||||
//! error handling, websockets, and more.
|
||||
|
||||
use axum_core::response::IntoResponse;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_fullstack::FromResponse;
|
||||
use dioxus_fullstack::http::StatusCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn main() {
|
||||
dioxus::launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let mut echo_action = use_action(echo);
|
||||
let mut chat_action = use_action(chat);
|
||||
let mut dog_data = use_action(get_data);
|
||||
let mut custom_data = use_action(get_custom_data);
|
||||
let mut anonymous_action = use_action(anonymous);
|
||||
let mut custom_anonymous_action = use_action(custom_anonymous);
|
||||
|
||||
rsx! {
|
||||
h1 { "Server Functions Example" }
|
||||
div {
|
||||
display: "flex",
|
||||
flex_direction: "column",
|
||||
gap: "8px",
|
||||
|
||||
button { onclick: move |_| echo_action.call("Hello from client".into()), "Echo: Hello" }
|
||||
button { onclick: move |_| chat_action.call(42u32, Some(7u32)), "Chat (user 42, room 7)" }
|
||||
button { onclick: move |_| dog_data.call(), "Get dog data" }
|
||||
button { onclick: move |_| custom_data.call(), "Get custom data" }
|
||||
button { onclick: move |_| anonymous_action.call(), "Call anonymous" }
|
||||
button { onclick: move |_| custom_anonymous_action.call(), "Call custom anonymous" }
|
||||
|
||||
button {
|
||||
onclick: move |_| {
|
||||
echo_action.reset();
|
||||
chat_action.reset();
|
||||
dog_data.reset();
|
||||
custom_data.reset();
|
||||
anonymous_action.reset();
|
||||
custom_anonymous_action.reset();
|
||||
},
|
||||
"Clear results"
|
||||
}
|
||||
|
||||
pre { "Echo result: {echo_action.result():#?}" }
|
||||
pre { "Chat result: {chat_action.result():#?}" }
|
||||
pre { "Dog data: {dog_data.result():#?}" }
|
||||
pre { "Custom data: {custom_data.result():#?}" }
|
||||
pre { "Anonymous: {anonymous_action.result():#?}" }
|
||||
pre { "Custom anonymous: {custom_anonymous_action.result():#?}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A plain server function at a `POST` endpoint that takes a string and returns it.
|
||||
/// Here, we use the `Result` return type which is an alias to `Result<T, anyhow::Error>`.
|
||||
#[post("/api/echo")]
|
||||
async fn echo(body: String) -> Result<String> {
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
/// A Server function that takes path and query parameters, as well as a server-only extractor.
|
||||
#[post("/api/{user_id}/chat?room_id", headers: dioxus_fullstack::HeaderMap)]
|
||||
async fn chat(user_id: u32, room_id: Option<u32>) -> Result<String> {
|
||||
Ok(format!(
|
||||
"User ID: {}, Room ID: {} - Headers: {:#?}",
|
||||
user_id,
|
||||
room_id.map_or("None".to_string(), |id| id.to_string()),
|
||||
headers
|
||||
))
|
||||
}
|
||||
|
||||
/// A plain server function at a `GET` endpoint that returns some JSON data. Because `DogData` is
|
||||
/// `Serialize` and `Deserialize`, it can be sent to the client as JSON automatically.
|
||||
///
|
||||
/// You can `curl` this endpoint and it will return a 200 status code with a JSON body:
|
||||
///
|
||||
/// ```json
|
||||
/// {
|
||||
/// "name": "Fido",
|
||||
/// "age": 4
|
||||
/// }
|
||||
/// ```
|
||||
#[get("/api/dog")]
|
||||
async fn get_data() -> Result<DogData> {
|
||||
Ok(DogData {
|
||||
name: "Fido".to_string(),
|
||||
age: 4,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct DogData {
|
||||
name: String,
|
||||
age: u8,
|
||||
}
|
||||
|
||||
/// A server function that returns a custom struct as JSON.
|
||||
#[get("/api/custom")]
|
||||
async fn get_custom_data() -> Result<CustomData> {
|
||||
Ok(CustomData {
|
||||
message: "Hello from the server!".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CustomData {
|
||||
message: String,
|
||||
}
|
||||
impl IntoResponse for CustomData {
|
||||
fn into_response(self) -> axum_core::response::Response {
|
||||
axum_core::response::Response::builder()
|
||||
.status(StatusCode::ACCEPTED)
|
||||
.body(serde_json::to_string(&self.message).unwrap().into())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromResponse for CustomData {
|
||||
async fn from_response(res: dioxus_fullstack::ClientResponse) -> Result<Self, ServerFnError> {
|
||||
let message = res.json::<String>().await?;
|
||||
Ok(CustomData { message })
|
||||
}
|
||||
}
|
||||
|
||||
/// An anonymous server function - the url path is generated from the module path and function name.
|
||||
///
|
||||
/// This will end up as `/api/anonymous_<hash>` where `<hash>` is a hash of the function signature.
|
||||
#[server]
|
||||
async fn anonymous() -> Result<String> {
|
||||
Ok("Hello from an anonymous server function!".to_string())
|
||||
}
|
||||
|
||||
/// An anonymous server function with a custom prefix and a fixed endpoint name.
|
||||
///
|
||||
/// This is less preferred over the `#[get]`/`#[post]` syntax but is still functional for backwards
|
||||
/// compatibility. Previously, only the `#[server]` attribute was available, but as of Dioxus 0.7,
|
||||
/// the `#[get]`/`#[post]` attributes are preferred for new code.
|
||||
#[server(prefix = "/api/custom", endpoint = "my_anonymous")]
|
||||
async fn custom_anonymous() -> Result<String> {
|
||||
Ok("Hello from a custom anonymous server function!".to_string())
|
||||
}
|
||||
@@ -32,11 +32,11 @@ pub enum ServerFnError {
|
||||
/// representation of the error.
|
||||
#[error("error running server function: {message} (details: {details:#?})")]
|
||||
ServerError {
|
||||
/// A human-readable message describing the error.
|
||||
message: String,
|
||||
|
||||
/// Optional HTTP status code associated with the error.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
code: Option<u16>,
|
||||
/// HTTP status code associated with the error.
|
||||
code: u16,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
details: Option<serde_json::Value>,
|
||||
@@ -89,7 +89,7 @@ impl ServerFnError {
|
||||
ServerFnError::ServerError {
|
||||
message: f.to_string(),
|
||||
details: None,
|
||||
code: None,
|
||||
code: 500,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ impl ServerFnError {
|
||||
|
||||
ServerFnError::ServerError {
|
||||
message,
|
||||
code: Some(status.as_u16()),
|
||||
code: status.as_u16(),
|
||||
details: None,
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ impl From<anyhow::Error> for ServerFnError {
|
||||
ServerFnError::ServerError {
|
||||
message: value.to_string(),
|
||||
details: None,
|
||||
code: None,
|
||||
code: 500,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,11 +135,9 @@ impl From<serde_json::Error> for ServerFnError {
|
||||
impl From<ServerFnError> for http::StatusCode {
|
||||
fn from(value: ServerFnError) -> Self {
|
||||
match value {
|
||||
ServerFnError::ServerError { code, .. } => match code {
|
||||
Some(code) => http::StatusCode::from_u16(code)
|
||||
.unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR),
|
||||
None => http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
},
|
||||
ServerFnError::ServerError { code, .. } => {
|
||||
http::StatusCode::from_u16(code).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
ServerFnError::Request(err) => match err {
|
||||
RequestError::Status(_, code) => http::StatusCode::from_u16(code)
|
||||
.unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR),
|
||||
@@ -174,7 +172,7 @@ impl From<HttpError> for ServerFnError {
|
||||
.unwrap_or("Unknown error")
|
||||
.to_string()
|
||||
}),
|
||||
code: Some(value.status.as_u16()),
|
||||
code: value.status.as_u16(),
|
||||
details: None,
|
||||
}
|
||||
}
|
||||
@@ -188,9 +186,8 @@ impl IntoResponse for ServerFnError {
|
||||
code,
|
||||
details,
|
||||
} => {
|
||||
let status = code
|
||||
.and_then(|c| StatusCode::from_u16(c).ok())
|
||||
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
let status =
|
||||
StatusCode::from_u16(code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
let body = if let Some(details) = details {
|
||||
serde_json::json!({
|
||||
"error": message,
|
||||
|
||||
@@ -200,9 +200,9 @@ pub fn current_status() -> StreamingStatus {
|
||||
pub fn status_code_from_error(error: &CapturedError) -> StatusCode {
|
||||
if let Some(err) = error.downcast_ref::<ServerFnError>() {
|
||||
match err {
|
||||
ServerFnError::ServerError {
|
||||
code: Some(code), ..
|
||||
} => return StatusCode::from_u16(*code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
ServerFnError::ServerError { code, .. } => {
|
||||
return StatusCode::from_u16(*code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
_ => return StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,15 +258,12 @@ impl SsrRendererPool {
|
||||
// we need to eventually be able to downcast that and get the status code from it
|
||||
if let Some(ServerFnError::ServerError { message, code, .. }) = error.downcast_ref()
|
||||
{
|
||||
if let Some(code) = code {
|
||||
status_code = Some(
|
||||
(*code)
|
||||
.try_into()
|
||||
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
);
|
||||
} else {
|
||||
status_code = Some(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
status_code = Some(
|
||||
(*code)
|
||||
.try_into()
|
||||
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
);
|
||||
|
||||
out_message = Some(message.clone());
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ pub use client::*;
|
||||
pub use axum::extract::Json;
|
||||
pub use axum::response::{NoContent, Redirect};
|
||||
|
||||
pub use crate::request::{FromResponse, FromResponseParts};
|
||||
|
||||
pub use payloads::*;
|
||||
pub mod payloads {
|
||||
use crate::{ClientRequest, ClientResponse, ClientResult, IntoRequest};
|
||||
|
||||
@@ -89,8 +89,7 @@ pub enum RestEndpointPayload<T, E> {
|
||||
pub struct ErrorPayload<E> {
|
||||
message: String,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
code: Option<u16>,
|
||||
code: u16,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
data: Option<E>,
|
||||
@@ -280,7 +279,7 @@ mod decode_ok {
|
||||
if let Ok(text) = String::from_utf8(as_bytes.to_vec()) {
|
||||
Ok(RestEndpointPayload::Error(ErrorPayload {
|
||||
message: format!("HTTP {}: {}", status.as_u16(), text),
|
||||
code: Some(status.as_u16()),
|
||||
code: status.as_u16(),
|
||||
data: None,
|
||||
}))
|
||||
} else {
|
||||
@@ -397,8 +396,10 @@ mod decode_ok {
|
||||
message: _message,
|
||||
details: _details,
|
||||
code,
|
||||
} => Err(StatusCode::from_u16(code.unwrap_or(500))
|
||||
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)),
|
||||
} => {
|
||||
Err(StatusCode::from_u16(code)
|
||||
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR))
|
||||
}
|
||||
|
||||
ServerFnError::Registration(_) | ServerFnError::MiddlewareError(_) => {
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
@@ -439,7 +440,7 @@ mod decode_ok {
|
||||
Ok(Ok(res)) => Ok(res),
|
||||
Ok(Err(res)) => match res {
|
||||
ServerFnError::ServerError { message, code, .. } => Err(HttpError {
|
||||
status: StatusCode::from_u16(code.unwrap_or(500))
|
||||
status: StatusCode::from_u16(code)
|
||||
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
message: Some(message),
|
||||
}),
|
||||
@@ -617,16 +618,33 @@ mod resp {
|
||||
fn make_axum_error(self, result: Result<Response, E>) -> Result<Response, Response>;
|
||||
}
|
||||
|
||||
/// Get the status code from the error type if possible.
|
||||
pub trait AsStatusCode {
|
||||
fn as_status_code(&self) -> StatusCode;
|
||||
}
|
||||
|
||||
impl AsStatusCode for ServerFnError {
|
||||
fn as_status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
Self::ServerError { code, .. } => {
|
||||
StatusCode::from_u16(*code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> MakeAxumError<E> for &&&ServerFnDecoder<Result<T, E>>
|
||||
where
|
||||
E: From<ServerFnError> + Serialize + DeserializeOwned + Display,
|
||||
E: AsStatusCode + From<ServerFnError> + Serialize + DeserializeOwned + Display,
|
||||
{
|
||||
fn make_axum_error(self, result: Result<Response, E>) -> Result<Response, Response> {
|
||||
match result {
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => {
|
||||
let status_code = err.as_status_code();
|
||||
let err = ErrorPayload {
|
||||
code: None,
|
||||
code: status_code.as_u16(),
|
||||
message: err.to_string(),
|
||||
data: Some(err),
|
||||
};
|
||||
@@ -636,7 +654,7 @@ mod resp {
|
||||
http::header::CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/json"),
|
||||
);
|
||||
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
*resp.status_mut() = status_code;
|
||||
Err(resp)
|
||||
}
|
||||
}
|
||||
@@ -665,7 +683,7 @@ mod resp {
|
||||
},
|
||||
Ok(other) => ErrorPayload {
|
||||
message: other.to_string(),
|
||||
code: None,
|
||||
code: 500,
|
||||
data: None,
|
||||
},
|
||||
Err(err) => match err.downcast::<HttpError>() {
|
||||
@@ -673,11 +691,11 @@ mod resp {
|
||||
message: http_err
|
||||
.message
|
||||
.unwrap_or_else(|| http_err.status.to_string()),
|
||||
code: Some(http_err.status.as_u16()),
|
||||
code: http_err.status.as_u16(),
|
||||
data: None,
|
||||
},
|
||||
Err(err) => ErrorPayload {
|
||||
code: None,
|
||||
code: 500,
|
||||
message: err.to_string(),
|
||||
data: None,
|
||||
},
|
||||
@@ -706,7 +724,7 @@ mod resp {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(status) => {
|
||||
let body = serde_json::to_string(&ErrorPayload::<()> {
|
||||
code: Some(status.as_u16()),
|
||||
code: status.as_u16(),
|
||||
message: status.to_string(),
|
||||
data: None,
|
||||
})
|
||||
@@ -732,7 +750,7 @@ mod resp {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(http_err) => {
|
||||
let body = serde_json::to_string(&ErrorPayload::<()> {
|
||||
code: Some(http_err.status.as_u16()),
|
||||
code: http_err.status.as_u16(),
|
||||
message: http_err
|
||||
.message
|
||||
.unwrap_or_else(|| http_err.status.to_string()),
|
||||
|
||||
@@ -10,9 +10,9 @@ impl NavigationProvider for DioxusNativeNavigationProvider {
|
||||
if options.method == Method::GET
|
||||
&& matches!(options.url.scheme(), "http" | "https" | "mailto")
|
||||
{
|
||||
if let Err(err) = webbrowser::open(options.url.as_str()) {
|
||||
if let Err(_err) = webbrowser::open(options.url.as_str()) {
|
||||
#[cfg(feature = "tracing")]
|
||||
tracing::error!("Failed to open URL: {}", err);
|
||||
tracing::error!("Failed to open URL: {}", _err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user