捕获请求中的 Panic

Catch Panic 用于捕获运行过程中程序处理请求时出现的奔溃. 具体 API 请查看文档.

注意: 使用 CatchPanic 需要在 Cargo.toml 中启用 catch-panic feature:

salvo= { version = "xxx", features = ["catch-panic"] }

中间件介绍

CatchPanic 是一个用于捕获处理程序中 panic 的中间件。当处理请求的过程中发生 panic 时,它会捕获这些 panic 并将 500 Internal Server Error 写入响应,而不是让整个服务器崩溃。

重要提示: 该中间件应该作为第一个中间件使用,以确保能捕获到其他中间件或处理程序中的 panic。

基本用法

use salvo_core::prelude::*;
use salvo_extra::catch_panic::CatchPanic;

#[handler]
async fn hello() {
    panic!("panic error!");
}

#[tokio::main]
async fn main() {
    let router = Router::new().hoop(CatchPanic::new()).get(hello);
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    Server::new(acceptor).serve(router).await;
}

与其他框架的对比 快速理解概念

Axum

类型Axum 中 Towercatch_panic 中间件:

use axum::{
    Router,
    routing::get,
    http::StatusCode,
};
use tower::ServiceBuilder;
use tower_http::catch_panic::CatchPanicLayer;

async fn panic_handler() -> &'static str {
    panic!("panic error!");
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(panic_handler))
        .layer(
            ServiceBuilder::new()
                .layer(CatchPanicLayer::new())
        );
    
    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Gin (Go)

在 Go 语言的 Gin 框架中,类似于 Recovery 中间件:

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default() // 默认已包含 Recovery 中间件
    
    r.GET("/", func(c *gin.Context) {
        panic("panic error!")
    })
    
    r.Run(":8080")
}

示例代码

main.rs
Cargo.toml
use salvo::prelude::*;

// Handler that deliberately panics to demonstrate panic catching
#[handler]
async fn hello() {
    panic!("panic error!");
}

#[tokio::main]
async fn main() {
    // Initialize logging system
    tracing_subscriber::fmt().init();

    // Set up router with CatchPanic middleware to handle panics gracefully
    // This prevents the server from crashing when a panic occurs in a handler
    let router = Router::new().hoop(CatchPanic::new()).get(hello);

    // Bind server to port 8698 and start serving
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    Server::new(acceptor).serve(router).await;
}