From 85d469ff7d75e50c4625b61c2932fd4bc51d427f Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sat, 17 Feb 2024 02:05:19 +0800 Subject: [PATCH] update error handle example. (#945) * update error handle example. * minor fix. --- examples/error-handle/Cargo.toml | 2 +- examples/error-handle/src/main.rs | 123 +++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 38 deletions(-) diff --git a/examples/error-handle/Cargo.toml b/examples/error-handle/Cargo.toml index 18cf4532..3accc882 100644 --- a/examples/error-handle/Cargo.toml +++ b/examples/error-handle/Cargo.toml @@ -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" diff --git a/examples/error-handle/src/main.rs b/examples/error-handle/src/main.rs index 614a80cc..c548296a 100644 --- a/examples/error-handle/src/main.rs +++ b/examples/error-handle/src/main.rs @@ -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, @@ -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() @@ -34,27 +41,41 @@ async fn std() -> Result<&'static str, Box> // 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 is the main error type xitca-web uses and at some point MyError would // need to be converted to it. @@ -75,31 +96,59 @@ impl<'r, C> Service> for MyError { } // a middleware function used for intercept and interact with app handler outputs. -async fn error_handler(s: &S, ctx: WebContext<'_, C>) -> Result> +async fn error_handler(s: &S, mut ctx: WebContext<'_, C>) -> Result> where - S: for<'r> Service, Response = Res, Error = Error>, + S: for<'r> Service, Response = WebResponse, Error = Error>, { - 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::() { - 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::() { + // 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::().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("

404 Not Found

"), 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.