Мне нужен маршрут для обработки событий веб-перехватчика, поступающих в 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 поддерживает различные «формы» для сериализации перечислений, ваш вариант использования называется «смежно помеченными». Если вы хотите использовать RawValue, вам следует использовать Box<RawValue>, потому что RawValue сам по себе является ссылочным типом.
Спасибо. Я попробую оба варианта и посмотрю, что подходит к этому контексту.

Предпочтительно обрабатывать тело типа { "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>,
}
Я считаю, что вам следует использовать
serde_json::Value, а неRawValue.