Skip to content

Commit

Permalink
update error handle example. (#945)
Browse files Browse the repository at this point in the history
* update error handle example.

* minor fix.
  • Loading branch information
fakeshadow authored Feb 16, 2024
1 parent d7bb77b commit 85d469f
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 38 deletions.
2 changes: 1 addition & 1 deletion examples/error-handle/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ version = "0.1.0"
edition = "2021"

[dependencies]
xitca-web = { version = "0.3", features = ["codegen"] }
xitca-web = { version = "0.3", features = ["codegen", "nightly"] }

thiserror = "1"
123 changes: 86 additions & 37 deletions examples/error-handle/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
#![feature(trait_upcasting)]

//! example of error handling in xitca-web.
//! code must be compiled with nightly Rust.
use std::{convert::Infallible, error, fmt};
// nightly rust features are not required for error handling in xitca-web. but they can enhance
// the experience quite a bit with certain features and they are purely opt-in.
//
// trait_upcasting nightly feature enables casting xitca_web::error::Error type to &dyn std::error::Error.
// error_generic_member_access and error_reporter nightly feature enables stack backtrace capture and display.
#![feature(trait_upcasting, error_generic_member_access, error_reporter)]

use std::{backtrace::Backtrace, convert::Infallible, error, fmt};

use xitca_web::{
error::Error,
handler::handler_service,
error::{Error, MatchError},
handler::{handler_service, html::Html, Responder},
http::{StatusCode, WebResponse},
route::get,
service::Service,
Expand All @@ -16,7 +21,9 @@ use xitca_web::{

fn main() -> std::io::Result<()> {
App::new()
// "http:://127.0.0.1:8080/" would respond with blank 400 http response and print error format with thread backtrace.
.at("/", get(handler_service(typed)))
// "http:://127.0.0.1:8080/std" would respond with blank 500 http response and print error format.
.at("/std", get(handler_service(std)))
.enclosed_fn(error_handler)
.serve()
Expand All @@ -34,27 +41,41 @@ async fn std() -> Result<&'static str, Box<dyn std::error::Error + Send + Sync>>
// a route always return an error type that would produce 400 bad-request http response.
// see below MyError implements for more explanation
async fn typed() -> Result<&'static str, MyError> {
Err(MyError::Index)
// forcefully capture thread backtrace. see error_handler function below and error::Report
// for it's usage.
Err(MyError {
backtrace: Backtrace::force_capture(),
})
}

// an enum error type. must implement following traits:
// a custom error type. must implement following traits:
// std::fmt::{Debug, Display} for formatting
// std::error::Error for backtrace and type casting
// xitca_web::dev::service::Service for http response generation.
#[derive(Debug)]
enum MyError {
Index,
// From for converting from Self to xitca_web::error::Error type.
// xitca_web::service::Service for lazily generating http response.
struct MyError {
// thread backtrace which can be requested after type erasing through std::error::Error::provide API.
backtrace: Backtrace,
}

impl fmt::Debug for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MyError").finish()
}
}

impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Index => f.write_str("error from /"),
}
f.write_str("custom error")
}
}

impl error::Error for MyError {}
impl error::Error for MyError {
// necessary for providing backtrace to xitca_web::error::Error instance.
fn provide<'a>(&'a self, request: &mut error::Request<'a>) {
request.provide_ref(&self.backtrace);
}
}

// Error<C> is the main error type xitca-web uses and at some point MyError would
// need to be converted to it.
Expand All @@ -75,31 +96,59 @@ impl<'r, C> Service<WebContext<'r, C>> for MyError {
}

// a middleware function used for intercept and interact with app handler outputs.
async fn error_handler<S, C, Res>(s: &S, ctx: WebContext<'_, C>) -> Result<Res, Error<C>>
async fn error_handler<S, C>(s: &S, mut ctx: WebContext<'_, C>) -> Result<WebResponse, Error<C>>
where
S: for<'r> Service<WebContext<'r, C>, Response = Res, Error = Error<C>>,
S: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Error<C>>,
{
s.call(ctx).await.map_err(|e| {
// debug format error info.
println!("{e:?}");

// display format error info.
println!("{e}");

// utilize std::error::Error trait methods for backtrace and more advanced error info.
let _source = e.source();

// upcast trait and downcast to concrete type again.
// this offers the ability to regain typed error specific error handling.
// *. this is a runtime feature and not reinforced at compile time.
if let Some(e) = (&*e as &dyn error::Error).downcast_ref::<MyError>() {
match e {
MyError::Index => {}
match s.call(ctx.reborrow()).await {
Ok(res) => Ok(res),
Err(e) => {
// debug format error info.
println!("{e:?}");

// display format error info.
println!("{e}");

// generate http response actively. from here it's OK to early return it in Result::Ok
// variant as error_handler function's output
let _res = e.call(ctx.reborrow()).await?;
// return Ok(_res);

// above are error handling enabled by stable rust.

// below are error handling enabled by nightly rust.

// utilize std::error module for backtrace and more advanced error info.
let report = error::Report::new(&e).pretty(true).show_backtrace(true);
// display error report
println!("{report}");

// upcast trait and downcast to concrete type again.
// this offers the ability to regain typed error specific error handling.
// *. this is a runtime feature and not reinforced at compile time.
if let Some(_e) = (&*e as &dyn error::Error).downcast_ref::<MyError>() {
// handle typed error.
}
}

e
})
// type casting can also be used to handle xitca-web's "internal" error types for overriding
// default error behavior.
// *. "internal" means these error types have their default error formatter and http response generator.
// *. "internal" error types are public types exported through `xitca_web::error` module. it's OK to
// override them for custom formatting/http response generating.
if (&*e as &dyn error::Error).downcast_ref::<MatchError>().is_some() {
// MatchError is error type for request not matching any route from application service.
// in this case we override it's default behavior by generating a different http response.
return (Html("<h1>404 Not Found</h1>"), StatusCode::NOT_FOUND)
.respond(ctx)
.await;
}

// the most basic error handling is to ignore it and return as is. xitca-web is able to take care
// of error by utilizing it's according trait implements(Debug,Display,Error and Service impls)
Err(e)
}
}
}

// if you prefer proc macro please reference examples/macros
// if you prefer proc macro please reference examples/macros where the trait implements can be drastically easier
// at the cost of some compile time.

0 comments on commit 85d469f

Please sign in to comment.