RFC 7807. Що це і для чого він потрібен бекенд розробникам

Зміст

RFC 7807

Що таке RFC 7807?

RFC 7807 — це стандарт, що описує формат передачі помилок у HTTP API. Він пропонує використовувати уніфікований формат JSON-об'єкта, який називається "Problem Details". Ідея в тому, щоб зробити повідомлення про помилки структурованими, інформативними та зрозумілими як для розробників, так і для клієнтів.

Чому це важливо

Як часто вам доводилося бачити неструктуровані відповіді на помилки типу:

{
  "error": "INVALID_REQUEST",
  "message": "User ID is missing"
}

Начебто зрозуміло, але що робити з таким респонсом далі?
RFC 7807 дозволяє не лише стандартизувати ці повідомлення, а й надати більше контексту про те, що пішло не так і як це виправити. Завдяки цьому, помилки стають значно простішими для обробки.


Формат Problem Details

Основна структура відповіді:

{
  "type": "https://api.example.com/problems/invalid-user",
  "title": "Invalid User ID",
  "status": 400,
  "detail": "The user ID provided is not valid.",
  "instance": "/users/123"
}
  • type — URL, який описує тип проблеми (може бути реальним посиланням на документацію або просто строкою).

  • title — короткий опис проблеми.

  • status — HTTP статус-код.

  • detail — деталі помилки.

  • instance — URI запиту, що викликав проблему (необов'язково).

Можна також додавати кастомні поля для специфічних кейсів:

{
  "type": "https://api.example.com/problems/invalid-input",
  "title": "Invalid Input",
  "status": 422,
  "detail": "The input data is invalid.",
  "invalidField": "email"
}

Як виглядає респонс без RFC 7807 і з ним

Без RFC 7807:

{
  "error": "USER_NOT_FOUND",
  "message": "User with ID 123 not found"
}

З RFC 7807:

{
  "type": "https://api.example.com/problems/user-not-found",
  "title": "User Not Found",
  "status": 404,
  "detail": "User with ID 123 was not found in the database.",
  "instance": "/users/123"
}

Погодьтесь, другий варіант виглядає більш зрозуміло і гнучко для подальшої автоматизації.


Best Practices

  1. Використовуйте URL у полі type.
    Це може бути статична сторінка документації вашого API або умовний посилання на тип помилки. Наприклад: https://api.example.com/problems/invalid-input.

  2. Не додавайте зайвого.
    Поля, що не мають практичної користі, лише ускладнюють відповідь.

  3. Кастомізуйте для своєї доменної області.
    Ви можете додавати власні поля, але не змінюйте обов'язкові, описані у RFC.

  4. Обов'язково логуйте.
    Навіть якщо користувач отримує зрозумілий респонс, логування помилки на стороні сервера є критично важливим для дебагу.


Реалізація в Java Spring Boot

Налаштування Controller Advice

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(AuthenticationException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ProblemDetail handleAuthenticationError(AuthenticationException ex, HttpServletRequest request) {
        ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(
            HttpStatus.UNAUTHORIZED,
            ex.getMessage()
        );
        problemDetail.setType(URI.create("https://api.example.com/problems/authentication-failed"));
        problemDetail.setTitle("Authentication Failed");
        problemDetail.setInstance(URI.create(request.getRequestURI()));
        return problemDetail;
    }
}

Кастомна помилка

public class AuthenticationException extends RuntimeException {
    public AuthenticationException(String message) {
        super(message);
    }
}

Ендпоінт для автентифікації, наприклад, з використанням FIDO2

@RestController
@RequestMapping("/auth")
public class AuthenticationController {

    @PostMapping("/fido2")
    public ResponseEntity<String> authenticateWithFido2(@RequestBody Fido2Request request) {
        if (!fido2Service.validate(request)) {
            throw new AuthenticationException("Invalid FIDO2 credentials provided");
        }
        return ResponseEntity.ok("Authentication successful");
    }
}

Приклад респонсу при помилці автентифікації

{
  "type": "https://api.example.com/problems/authentication-failed",
  "title": "Authentication Failed",
  "status": 401,
  "detail": "Invalid FIDO2 credentials provided.",
  "instance": "/auth/fido2"
}

Інтеграція та взаємодія з іншими компонентами

Стандартизована взаємодія

  • Інші сервіси або фронтенд можуть очікувати однакову структуру помилок, що спрощує їх обробку.

  • Наприклад, фронтенд може мати єдиний обробник для всіх помилок із зрозумілими полями type, title, detail та status.

На фронтенді це може виглядати так:

fetch("/api/auth/fido2", { method: "POST", body: JSON.stringify(data) })
  .then(response => {
    if (!response.ok) {
      return response.json().then(error => {
        console.error(`Error Type: ${error.type}`);
        alert(error.title + ": " + error.detail);
      });
    }
  });

Машинна обробка помилок

  • Поле type дозволяє програмно класифікувати помилки. Наприклад, різні type можуть запускати різні сценарії в клієнтській програмі.

  • У інтеграціях між сервісами це дозволяє швидко реагувати на певні типи помилок без аналізу тексту.

Приклад коду в іншому сервісі який інтегрується з нашим:

if (problemDetail.getType().equals(URI.create("https://api.example.com/problems/authentication-failed"))) {
    retryAuthentication();
} else if (problemDetail.getType().equals(URI.create("https://api.example.com/problems/invalid-input"))) {
    logInvalidInput();
}

Зрозумілий для користувача інтерфейс

Завдяки структурі, помилки легко локалізувати. Наприклад, title може бути коротким і локалізованим для кінцевого користувача, тоді як detail містить технічну інформацію для розробника.

Приклад локалізації:

{
  "type": "https://api.example.com/problems/authentication-failed",
  "title": "Аутентифікація не вдалася",
  "status": 401,
  "detail": "Облікові дані FIDO2 недійсні.",
  "instance": "/auth/fido2"
}

Спрощений дебагінг і логування

Сервер надає достатньо контексту для розробників, включаючи URI запиту (через поле instance), статус, та інші деталі. Це значно полегшує діагностику проблем.

Розширення без порушення сумісності

Ви можете додавати кастомні поля, не порушуючи стандарту. Наприклад, для інтеграції з платіжним сервісом можна передавати інформацію про специфічні поля:

{
  "type": "https://api.example.com/problems/invalid-payment",
  "title": "Payment Error",
  "status": 400,
  "detail": "The payment method is not supported.",
  "instance": "/payments/456",
  "paymentMethod": "crypto",
  "retryAfter": 3600
}

На фронтенді:

if (error.paymentMethod === "crypto") {
  alert("Please try another payment method.");
} else {
  console.log(`Retry after: ${error.retryAfter} seconds`);
}

Готовність до автоматизації

Завдяки структурованим даним API-інтеграції можуть автоматично реагувати на помилки, наприклад, повторювати запит або повідомляти відповідний відділ підтримки.

if (problemDetail.getStatus() == 429) { // Too Many Requests
    waitAndRetry(problemDetail.getRetryAfter());
}

Висновки

  1. RFC 7807 робить роботу з помилками у вашому API прозорою та зручною.

  2. Замість простих рядків ви отримуєте структуру, яка легко інтегрується в клієнтські додатки.

  3. Реалізація у Spring Boot є простою та дозволяє швидко адаптуватися до стандарту.

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Oleksandr Klymenko
Oleksandr Klymenko@overpathz

Java Software Engineer

5.2KПрочитань
1Автори
79Читачі
На Друкарні з 19 квітня

Більше від автора

  • Java. jOOQ

    Довгочит буде про jOOQ — бібліотеку, яка зручно поєднує світ Java і SQL. Якщо ви працюєте з базами даних у Java, то, скоріш за все, зустрічались з такими дилемами:

    Теми цього довгочиту:

    Java
  • Secure networking. Deep Dive

    Глибоке занурення в протоколи TLS/SSL та інфраструктуру відкритих ключів (PKI). Основні поняття, процес встановлення захищеного з'єднання, роль сертифікатів та ланцюжка довіри

    Теми цього довгочиту:

    Security
  • Поширені помилки у дизайні REST API

    У довгочиті розглядаються поширені помилки при проектуванні REST API та способи їх уникнення: версіонування, використання DTO, підхід CQRS, робота з мікросервісами, та інші практики для підвищення продуктивності, безпеки й зручності API

    Теми цього довгочиту:

    Java

Вам також сподобається

  • [Frontend] Універсальні рішення

    Потряпляючи у пастки антипатернів велику кількість разів ви навчитесь писати гарні “універсальні рішення".

    Теми цього довгочиту:

    It
  • Порівняння рішень на Java, Kotlin та Scala

    Давайте розглянемо, як задачу https://leetcode.com/problems/relative-ranks/solutions/6326961/sorting-mapping-approach можна вирішити трьома мовами програмування — Java, Kotlin та Scala.

    Теми цього довгочиту:

    Java

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Вам також сподобається

  • [Frontend] Універсальні рішення

    Потряпляючи у пастки антипатернів велику кількість разів ви навчитесь писати гарні “універсальні рішення".

    Теми цього довгочиту:

    It
  • Порівняння рішень на Java, Kotlin та Scala

    Давайте розглянемо, як задачу https://leetcode.com/problems/relative-ranks/solutions/6326961/sorting-mapping-approach можна вирішити трьома мовами програмування — Java, Kotlin та Scala.

    Теми цього довгочиту:

    Java