Волга
Главная
API Docs
GitHub
  • English
  • Русский
Главная
API Docs
GitHub
  • English
  • Русский
  • Главная
  • Старт

    • Быстрый старт
    • Параметры маршрута
    • Параметры запроса
    • Группировка маршрутов
  • Запросы и ответы

    • Заголовки (Headers)
    • Работа с JSON
    • Работа с Form Data
    • Работа с файлами
    • Cookies
  • Middleware и инфраструктура

    • Основы Middleware
    • Пользовательские Middleware
    • Сжатие ответов
    • Распаковка запросов
    • CORS (Cross-Origin Resource Sharing)
    • Статические файлы
    • Rate Limiting
  • Безопасность и доступ

    • Аутентификация и авторизация
  • Надежность и наблюдаемость

    • Центральный обработчик ошибок
    • Логгирование и Трассировка
    • Отмена запросов
  • Протоколы и realtime

    • HTTP/1 и HTTP/2
    • HTTPS
    • WebSockets
    • Server-Sent Events (SSE)
  • Продвинутые паттерны

    • Внедрение Зависимостей
    • Пользовательская обработка методов HEAD, OPTIONS и TRACE

Центральный обработчик ошибок

Волга предоставляет централизованный механизм обработки ошибок, который перехватывает все ошибки, реализующие типаж Error, возникающие в обработчиках запросов и middleware. Для этого можно воспользоваться методом map_err типа App для регистрации функции, обрабатывающей ошибки.

Функция принимает объект типа Error и должна вернуть ответ, реализующий типаж IntoResponse.

Пример:

use volga::{App, error::Error, status};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new();
    
    app.map_get("/error", || async {
        std::io::Error::other("some error")
    });

    // Регистрируем централизованный обработчик ошибок
    app.map_err(|error: Error| async move {
        status!(500, "{:?}", error)
    });

    app.run().await
}

В этом примере мы намеренно создаем обработчик запросов, который выдает ошибку, и определяем обработчик ошибок, который генерирует HTTP-ответ с кодом состояния 500 на основе сообщения об ошибке.

Для удобства структура Error уже включает поле status, которое охватывает общие случаи (400, 401, 403, 404 и т. д.), что позволяет использовать макрос следующим образом:

status!(error.status.as_u16(), "{:?}", error)

Фактически, именно так реализован обработчик ошибок по умолчанию. Если мы удалим метод map_err, ответ останется неизменным. Однако переопределение пользовательского обработчика ошибок обеспечивает большую гибкость для логирования и трассировки.

Problem Details

Волга полностью поддерживает формат Problem Details, который предоставляет машиночитаемые сведения об ошибках в ответах HTTP. Это устраняет необходимость определять пользовательские форматы ошибок для API.

Чтобы включить эту возможность, убедитесь, что функция problem-details активирована в Cargo.toml вашего приложения:

[dependencies]
volga = { version = "...", features = ["problem-details"] }

Затем вы можете вернуть структуру Problem из обработчика запроса:

use volga::{App, error::Problem};
use serde::Serialize;

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new();

    app.map_get("/problem", || async {
        // Всегда выдает Problem Details

        Problem::new(400)
            .with_detail("Missing Parameter")
            .with_instance("/problem")
            .with_extensions(ValidationError {
                invalid_params: vec![InvalidParam { 
                    name: "id".into(), 
                    reason: "The ID must be provided".into()
                }]
            })
    }); 

    app.run().await
}

#[derive(Default, Serialize)]
struct ValidationError {
    #[serde(rename = "invalid-params")]
    invalid_params: Vec<InvalidParam>,
}

#[derive(Default, Serialize)]
struct InvalidParam {
    name: String,
    reason: String,
}

Пример ответа:

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
    "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
    "title": "Bad Request",
    "status": 400,
    "detail": "Missing Parameter",
    "instance": "/problem",
    "invalid-params": [
        { "name": "id", "reason": "The ID must be provided" }
    ]
}

Центральная обработка ошибок с Problem Details

Кроме того, вы можете комбинировать Problem с map_err, используя метод use_problem_details():

use volga::{App, error::Error};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new();
    
    app.map_get("/error", || async {
        // Всегда выдает ошибку, которая будет преобразована
        // в Problem Details

        std::io::Error::other("some error")
    });

    // Регистрируем централизованный обработчик ошибок, который выдает
    // ответы в формате Problem Details
    app.use_problem_details();  

    app.run().await
}

Пример ответа:

HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json

{
    "type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
    "title": "Internal Server Error",
    "status": 500,
    "detail": "some error",
    "instance": "/error"
}

Поля type и title определяются из кода состояния, но могут быть переопределены:

Problem::new(400)
    .with_type("https://tools.ietf.org/html/rfc9110#section-15.6.1")
    .with_title("Server Error");

А также при необходимости можно добавить дополнительные сведения:

Problem::new(400)
    .with_detail("Missing Parameter")
    .with_instance("/problem")
    .with_extensions(ValidationError {
        invalid_params: vec![InvalidParam { 
            name: "id".into(), 
            reason: "The ID must be provided".into()
        }]
    })

или

Problem::new(400)
    .with_detail("Missing Parameter")
    .with_instance("/problem")
    .add_param("reason", "The ID must be provided");

Готовые примеры можно найти по следующим ссылкам:

  • Центральная обработка ошибок.
  • Problem Details
Последнее обновление: 18.01.2026, 18:33
Next
Логгирование и Трассировка