HTTP-заголовок Deprecation используется для информирования клиентов, что конечная точка API устарела или будет устаревшей. Согласно последнему черновику предложения, поле должно быть типа RFC 9651 Date, выраженной в формате времени Unix. Дата может быть как в прошлом (API уже устарел), так и в будущем (API устареет с этой даты).

Вот пример API, объявленного устаревшим в пятницу, 30 июня 2023 г., в 23:59:59 UTC:

Deprecation: @1688169599
  • Дополнительное предупреждение. Потребители вашего API могут не прочитать предупреждение об устаревании, которое вы отправляете электронной почтой или другими каналами связи, поэтому для них это еще один способ узнать о предстоящем прекращении поддержки.
  • Поддержка в коде. В отличие от документации, которая часто отделена от вашего кода API, заголовок Deprecated отправляется API во время выполнения и может обновляться напрямую разработчиками.
  • Поддержка инструментов. Клиенты API могут со временем принять шаблон проверки сетевых ответов на наличие заголовка устаревания и логирования предупреждений в средах помимо продакшена.

Эта конвенция не всегда была такой, многие провайдеры API отклоняются от такого формата. Некоторые предпочитают формат HTTP-даты RFC 7231 (часто выражаемый в формате GMT), который выглядит следующим образом:

Deprecation: Wed, 11 Oct 2023 23:59:59 GMT

Очевидное преимущество этого формата — его гораздо легче прочитать разработчику API, проверяющему ответы сети. Однако некоторые инструменты (например, клиенты API) могут найти этот формат в разборе более сложным, чем время Unix.

Еще проще вместо даты использовать логическое значение:

Deprecation: true

В качестве ответа на вопрос «является ли этот API устаревшим?». Недостаток логического значения заключается в том, что оно требует заблаговременного сообщения об устаревании API другими способами, кроме заголовков. Учитывая, что заголовок Deprexation не получил широкого распространения, этот процесс обычно все равно необходим. Другой недостаток заключается в том, что подход ограничивает возможности клиентских библиотек API предупреждать вас о предстоящем прекращении поддержки API.

Хотя заголовок устаревания полезен сам по себе, в рамках целостного процесса прекращения поддержки API вы можете объединить его с другими заголовками.

Тип отношения ссылки об устаревании

Чтобы сообщить дополнительную информацию об устаревании API, вы можете использовать тип отношения ссылки к текущему документу. Например, вы можете дать ссылку на документацию или на сообщение об устаревании API в блоге, вот так:

Link:
  <https://developer.example.com/deprecation>; rel="deprecation";
  type="text/html"

Особенно полезно это может быть, когда устарело более одной конечной точки. Одно из ограничений заголовка Deprecation состоит в том, что он не предоставляет контекста области устаревания: API устарел весь или только эта конечная точка? Ссылка должно помочь восполнить этот пробел.

Момент, когда устаревший API перестанет отвечать на запросы — это закат конечной точки API. Чтобы сообщить, когда он наступит, обычно используется HTTP-заголовок Sunset (RFC 8594). Он часто сочетается с заголовком Deprecated, чтобы дать полную картину окончания срока жизни API:

Deprecation: @1688169599
Sunset: Sun, 30 Jun 2024 23:59:59 UTC

Обратите внимание, что заголовок Sunset по «историческим причинам» использует формат даты, отличный от формата в Deprecated.

Ниже приведены несколько примеров реализации заголовка устаревания в разнообразных популярных фреймворках API.

Node.js и Express

const express = require("express");
const app = express();

app.get("/v1/old-endpoint", (req, res) => {
  const deprecationDate = new Date().now();
  res.set("Deprecation", deprecationDate);
  res.json({ message: "This endpoint is deprecated." });
});

app.listen(3000, () => console.log("Server running on port 3000"));

Python и Flask

from flask import Flask, make_response
from datetime import datetime

app = Flask(__name__)

@app.route('/v1/old-endpoint')
def old_endpoint():
    response = make_response({'message': 'This endpoint is deprecated.'})
    deprecation_date = int(datetime(2023, 10, 11, 23, 59, 59).timestamp())
    response.headers['Deprecation'] = deprecation_date
    return response

if __name__ == '__main__':
    app.run(port=3000)

Java и Spring Boot

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.temporal.*;

@RestController
public class ApiController {

    @GetMapping("/v1/old-endpoint")
    public ResponseEntity<String> oldEndpoint() {
        String deprecationDate = Instant.now().getEpochSecond()
        return ResponseEntity.ok()
                .header("Deprecation", deprecationDate)
                .body("This endpoint is deprecated.");
    }
}

Go и net/http

package main

import (
    "fmt"
    "net/http"
    "time"
)

func oldEndpoint(w http.ResponseWriter, r *http.Request) {
    deprecationDate := time.Now().Unix()
    w.Header().Set("Deprecation", deprecationDate)
    w.Header().Set("Sunset", "Wed, 11 Nov 2023 23:59:59 GMT")
    fmt.Fprintln(w, `{"message": "This endpoint is deprecated."}`)
}

func main() {
    http.HandleFunc("/v1/old-endpoint", oldEndpoint)
    http.ListenAndServe(":3000", nil)
}

PHP и Laravel

<?php

use Illuminate\Support\Facades\Route;

Route::get('/v1/old-endpoint', function () {
    $deprecationDate = time();
    return response()->json(['message' => 'This endpoint is deprecated.'])
        ->header('Deprecation', $deprecationDate);
});

Если заголовок не используется на стороне клиента, нет смысла его отправлять.

Вот простой пример на JavaScript:

fetch("/v1/old-endpoint")
  .then((response) => {
    const deprecationHeader = response.headers.get("Deprecation");
    if (deprecationHeader) {
      const deprecationDate = new Date(parseInt(deprecationHeader) * 1000);
      console.warn(
        `This API was deprecated on ${deprecationDate.toUTCString()}`,
      );
    }
    return response.json();
  })
  .then((data) => {
    console.log(data);
  });

Существует лишь несколько альтернатив с точки зрения индикации статуса устаревания во время выполнения.

HTTP-заголовок Warning — это заголовок общего назначения, который содержит дополнительную информацию о статусе или преобразовании сообщения. В основном он используется, чтобы предупредить клиентов о потенциальных проблемах, которые могут не быть отражены в коде состояния.

В заголовке Warning используется трехзначный код предупреждения, необязательный агент (обычно это имя хоста) и текст предупреждения, заключенный в двойные кавычки. Код предупреждения 299 идеально подходит для того, чтобы обозначить прекращения поддержки, поскольку это постоянное предупреждение, способное передавать любое сообщение.

Warning: 299 - "Deprecated API: This endpoint is deprecated and will be removed on 2023-11-11."

Некоторые компании, такие как UKG, задокументировали этот паттерн, но те API, вероятно, появились еще до введения Deprecated.

Плюсы и минусы применения заголовка Warning

Плюсы

  • Стандартизированный формат. Заголовок Warning является частью спецификации HTTP.
  • Осведомленность клиента. Многие HTTP-клиенты и браузеры задуманы обрабатывать Warning.
  • Гибкость. Позволяет отправлять подробные сообщения об устаревании.

Минусы

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

Я рекомендовал бы избегать заголовка Warning и зарезервировать его для актуальных проблем во времени выполнения. Напомню, что устаревшие API должны работать до момента в заголовке Sunset. И если Warning занят сообщением об устаревании, как сообщать об актуальной проблеме?

Нестандартный подход: тело ответа в JSON

В зависимости от формата ответа API можно включить дополнительные свойства, которые разбираются клиентами во время выполнения.

{
  "deprecated": 1688169599,
  "sunset": "2023-11-11T23:59:59Z",
  "data": {...}
}

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

Хотя заголовок Deprecated полезен в процессе прекращения поддержки API, вам нужно знать гораздо больше. Для этого ознакомьтесь с полным руководством по прекращению поддержки REST API.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Adrian Machado: Understanding The HTTP Deprecation Header

Предыдущая статьяПерестановка чисел в C++: руководство
Следующая статьяА вы сможете ответить на этот знаменитый вопрос из собеседования?