Неправильное использование кодов статуса HTTP обрушивает ваш мониторинг API и логику клиента

Неправильное использование кодов статуса HTTP обрушивает ваш мониторинг API и логику клиента

9 июня 2025 г.

Когда ваш API говорит: «Все в порядке!» но возвращает ошибки

TL; DR: возвращение успешного статуса HTTP, когда фактический результат содержит ошибку, сбивает с толку потребителей API.

Проблемы 😔

  • Путаница кода статуса
  • Отладки сложности
  • Обработка ошибок клиента
  • Нарушение контракта API
  • Обработание текста человека вместо проверки кода
  • Непоследовательное поведение
  • Наименее неожиданное нарушение принципа

Решения 😃

  1. Сопоставление статуса с контентом
  2. Используйте правильные коды ошибок
  3. Следуйте стандартам HTTP
  4. Реализовать последовательные ответы
  5. Коды статуса теста
  6. Отдельные метаданные от полезной нагрузки
  7. Избегайте смешивания успеха и ошибок
  8. Определите ясноедоговор

Контекст 💬

Вы создаете API, который успешно обрабатывает запросы на уровне транспорта HTTP, но сталкивается с ошибками на уровне приложения.

Вместо возвращения соответствующих кодов состояния ошибок HTTP, таких как 400 (плохой запрос) или 500 (внутренняя ошибка сервера), вы возвращаете 200 OK с информацией об ошибках в корпусе ответа.

Это создает разрыв между тем, что указывает статус HTTP, и тем, что произошло, что затрудняет клиенты правильно обрабатывать ошибки и для систем мониторинга для обнаружения проблем.

Пример кода 📖

Неправильно ❌

use axum::{
  http::StatusCode,
  response::Json,
  routing::post,
  Router,
};
use serde_json::{json, Value};

async fn process_payment(
  Json(payload): Json<Value>
) -> (StatusCode, Json<Value>) {
  let amount = payload.get("amount")
    .and_then(|v| v.as_f64());
  
  if amount.is_none() || amount.unwrap() <= 0.0 {
    return (
      StatusCode::OK, // Wrong: returning 200 for error
      Json(json!({"error": true, "message": "Invalid amount"}))
    );
  }
  
  if amount.unwrap() > 10000.0 {
    return (
      StatusCode::OK, // Wrong: returning 200 for error 
      Json(json!({"error": true, "message": "Amount too large"}))
    );
  }
  
  // Simulate processing error
  if let Some(card) = payload.get("card_number") {
    if card.as_str().unwrap_or("").len() < 16 {
      return (
        StatusCode::OK, // Wrong: returning 200 for error
        Json(json!({"error": true, "message": "Invalid card"}))
      );
    }
  }
  
  (
    StatusCode::OK, // THIS the only real 200 Status
    Json(json!({"success": true, "transaction_id": "12345"}))
  )
}

pub fn create_router() -> Router {
  Router::new().route("/payment", post(process_payment))
}

Справа 👉

use axum::{
  http::StatusCode,
  response::Json,
  routing::post,
  Router,
};
use serde_json::{json, Value};

async fn process_payment(
  Json(payload): Json<Value>
) -> (StatusCode, Json<Value>) {
  let amount = payload.get("amount")
    .and_then(|v| v.as_f64());
  
  if amount.is_none() || amount.unwrap() <= 0.0 {
    return (
      StatusCode::BAD_REQUEST, // Correct: 400 for bad input
      Json(json!({"error": "Invalid amount provided"}))
    );
  }
  
  if amount.unwrap() > 10000.0 {
    return (
      StatusCode::UNPROCESSABLE_ENTITY, 
      // Correct: 422 for business rule
      Json(json!({"error": "Amount exceeds transaction limit"}))
    );
  }
  
  // Validate card number
  if let Some(card) = payload.get("card_number") {
    if card.as_str().unwrap_or("").len() < 16 {
      return (
        StatusCode::BAD_REQUEST, 
        // Correct: 400 for validation error
        Json(json!({"error": "Invalid card number format"}))
      );
    }
  } else {
    return (
      StatusCode::BAD_REQUEST, 
      // Correct: 400 for missing field
      Json(json!({"error": "Card number is required"}))
    );
  }
  
  // successful processing
  (
    StatusCode::OK, 
    // Correct: 200 only for actual success
    Json(json!({"transaction_id": "12345", "status": "completed"}))
  )
}

pub fn create_router() -> Router {
  Router::new().route("/payment", post(process_payment))
}

Обнаружение 🔍

  • [x]Полуавтоматический

Вы можете обнаружить этот запах, когда вы видите HTTP 200 ответов, которые содержат поля ошибок, логические флаги ошибок или сообщения об отказе.

Ищите API, которые всегда возвращаются 200 независимо от фактического результата.

Проверьте, могут ли ваши системы мониторинга должным образом обнаружить сбои и использовать тестирование на мутации.

Если они не могут различать успех и неудачу на основе кодов статуса, у вас, вероятно, есть эта проблема.

Вы также можете посмотреть ошибки на стороне клиента, вызванные несоответствующими ожиданиями.

Исключения 🛑

  • Нарушение изменений на существующих клиентах API может потребовать нарушения изменения, чтобы исправить этот запах.

Теги 🏷

  • Исключения

Уровень 🔋

  • Средний

Почему биение важна 🗺

Существуют коды статуса HTTP, чтобы обеспечить стандартизированный способ донести результаты запросов междусистемаПолем

Когда вы нарушаете эту переписку, возвращая коды успеха для сбоев, вы создаете несоответствие между семантическим значением протокола HTTP и фактическим поведением вашего приложения.

Это заставляет каждого клиента проанализировать тела реагирования определять успех или неудачу, делая ошибку непоследовательной и ненадежной.

Системы мониторинга, балансировщики нагрузки и прокси полагаются на коды статуса для принятия решений о маршрутизации и медицинском обслуживании - вводящие в заблуждение коды могут привести к тому, что эти системы могут сделать неправильные предположения о здоровье вашего API.

СцеплениеВаши решения о неправильном коде состояния сломаютКартПолем

Моделирование взаимосвязи один к одному между кодом состояния HTTP и фактическим бизнес-результатом обеспечивает ясность и предсказуемость. Когда 200 ОК возвращает внутреннюю ошибку, клиент предполагает, что все в порядке, что приводит к безмолвным сбоям и неправильному поведению вниз по течению.

Поддержав этобиение, мы гарантируем, что разработчики и системы, взаимодействующие с API, могут доверять ответу без дополнительных проверок.

Поколение AI 🤖

Генераторы кодов ИИ часто создают этот запах, когда разработчики просят «простые примеры API» без указания надлежащей обработки ошибок.

Генераторы, как правило, сосредотачиваются на счастливом пути и возвращают 200 для всех ответов, чтобы избежать сложности.

Когда вы предлагаете ИИ создать API REST, вы должны явно запросить правильную обработку кода состояния HTTP и самостоятельно проверить стандарты.

Обнаружение ИИ 🥃

Многие помощники ИИ могут обнаружить это несоответствие.

Попробуйте их! 🛠

Помните: помощники ИИ делают много ошибок

Предлагаемое подсказка: Правильно плохое поведение кодов HTTP

Без надлежащих инструкций

С конкретными инструкциями

Чатгпт

Чатгпт

Клод

Клод

Недоумение

Недоумение

Второй пилот

Второй пилот

Близнецы

Близнецы

DeepSeek

DeepSeek

Meta ai

Meta ai

Грок

Грок

Qwen

Qwen

Заключение 🏁

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

Когда вы возвращаете вводящие в заблуждение коды статуса, вы нарушаете неявный договор, который HTTP обеспечивает усложнение API для интеграции и поддержания.

Всегда убедитесь, что ваши коды статуса точно отражают фактический результат операции.

Отношения 👩‍❤

https://hackernoon.com/code-smell-270-boolean-apis?embedable=true

https://hackernoon.com/code-smell-272-api-chain?embedable=true

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-xv?embedable=true

https://hackernoon.com/code-smell-244-incomplete-error-information-and-how-ta-fix-it?embedable=true

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-xv?embedable=true

Больше информации 📕

https://en.wikipedia.org/wiki/list_of_http_status_codes?embedable=true

Отказ от ответственности 📘

Кодовые запахи - моимнениеПолем


Лучшее сообщение об ошибке - это то, что никогда не появляется

Томас Фукс

https://hackernoon.com/400-thought-предвидение-software-engineering-quotes?embedable=true


Эта статья является частью серии Codesmell.

https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-i-xqz3evd?embedable=true


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE