Как мне написать маршрут в Axum, который обрабатывает события POST веб-перехватчика с общими данными полезной нагрузки?

Мне нужен маршрут для обработки событий веб-перехватчика, поступающих в API Axum.

Тело Json имеет свойство event, задающее имя события, и свойство data, которое будет другим объектом в зависимости от типа event.

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum DbWebhookEvent {
    SignUpCompleted,
}

#[derive(Debug, Deserialize)]
pub struct DbWebhookBody {
    event: DbWebhookEvent,
    data: ??
}

Я рассматривал возможность использования serde RawValue для этого варианта использования, но когда я добавляю время жизни в структуру DbWebhookBody, я не уверен, как это должно передаваться маршрутизатору приложения.

Затем мне нужно будет десериализовать data, как только станет известен тип события, чтобы с ним можно было действовать. Моя функция-обработчик сейчас выглядит так:

pub async fn post_db_webhook(
    Extension(app_state): Extension<Arc<AppState>>,
    Json(body): Json<DbWebhookBody>,
) -> Result<Json<DbWebhookResponse>> {
    match body.event {
        DbWebhookEvent::SignUpCompleted => {
          // need to deserialize body data to a struct
          let signup_data = SignupData::from(body.data)
          signup_completed(app_state, body.data);
        }
    }
    Ok(Json(DbWebhookResponse { ok: true }))
}

Я считаю, что вам следует использовать serde_json::Value, а не RawValue.

Dogbert 28.05.2024 16:54

serde поддерживает различные «формы» для сериализации перечислений, ваш вариант использования называется «смежно помеченными». Если вы хотите использовать RawValue, вам следует использовать Box<RawValue>, потому что RawValue сам по себе является ссылочным типом.

user2407038 28.05.2024 17:07

Спасибо. Я попробую оба варианта и посмотрю, что подходит к этому контексту.

Dave Taylor 28.05.2024 21:09
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
2
3
95
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Предпочтительно обрабатывать тело типа { "event": "name", "data": ... } как перечисление со смежным тегом в Serde, поскольку оно гораздо более безопасно с точки зрения типов. Однако это предполагает, что вы знаете имена и структуры во время компиляции.

#[derive(Debug, Deserialize)]
#[serde(tag = "event", content = "data", rename_all = "kebab-case")]
pub enum DbWebhookBody {
    SignUpCompleted(SignUpCompletedEvent),
}

Если вы хотите обрабатывать поле data в общем виде, вы можете использовать значение serde-json . Он может содержать любое значение JSON и может быть проверен без десериализации до определенного типа. Это полезно, если вам нужно обрабатывать данные, которые плохо структурированы или соответствуют схеме, известной только во время выполнения.

#[derive(Debug, Deserialize)]
pub struct DbWebhookBody {
    event: DbWebhookEvent,
    data: Value,
}

Если вы хотите отложить десериализацию поля data, вам следует использовать RawValue из serde-json. Вам нужно будет использовать его через Box<RawValue> вместо &'_ RawValue, поскольку последний должен сохранять ссылку на исходное тело запроса, которое axum не поддерживает (Json требует DeserializeOwned).

#[derive(Debug, Deserialize)]
pub struct DbWebhookBody {
    event: DbWebhookEvent,
    data: Box<RawValue>,
}

Другие вопросы по теме