Как стать автором
Обновить

Комментарии 13

Очень похоже на то что мы используем для Java. Есть enum с набором версий

@ApiVersionList
public enum RestApiVersion {
    v1_0,
    /* initial version */
    v2_0,
    /* - add new endpoint (DtoV1x0) */
    v3_0,
    /* - change endpoint (DtoV2x0) */
}

А в котроллерах методы с аннотациями

@Service
public class ExampleController {

    @GetMapping("/isalive/check")
    public static boolean isAlive() {
        return true;
    }
  
    @ApiVersion(value = "v2_0", deleted = "v3_0")
    @GetMapping("/someService")
    public static DtoV2x0 getDtoV2() {
        return null;
    }
   
    @ApiVersion("v3_0")
    @GetMapping("/someService")
    public static DtoV3x0 getDtoV3() {
        return null;
    }  

}

isAlive - без аннотации - есть во всех версиях

getDtoV2 только во 2

getDtoV3 только с 3 и далее

Дальше annotation processor сгенерирует три реальных контроллера, каждый со своим набором методов.

Версии методов и DTO могут быть любые, но для удобства мы их синхронизируем с версией API где они появились впервые.

До боли напоминает массу сущиствующих туторов, в том числе из msdn. В чем новизна конкретно этого howto?

Как быть с post/put ендпойнтами, если в новой версии v2 нужно внести изменения в схему БД? Допустим, в v2 приложения добавили новое not null поле в схему таблицы, и значение должно выбираться осознанно (не дефолтное), которое будет влиять на остальной процесс бизнес логики.

Т.е. в методах контроллера добавится обязательный входной параметр, без которого ломается бизнес логика? В таком случае, похоже, только рефакторинг, т.к. версия v1, не принимая этот параметр, ломается автоматически.

На практике не удавалось сталкиваться с версионированием api, так как была возможность повлиять на клиентскую часть сервиса. Но всегда интересовал этот вопрос, особенно по самому чувствительному месту - данным в базе. Я так понимаю, все v1,v2,v3,..,vN должны соблюдать обратную совместимость. И вся идея версионирования api - это по сути инкремент минорной версии y в семантическом версионировании x.y.z, а не x. И если у нас случится изменение x, то надо инвалидировать все пердыдущие версии api, т.е. объявить даже не deprecated, a obsolete?

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

В устаревшем методе надо возвращать код ошибки HTTP (например 500) и или кастомный код (в 200 OK). В одном из проектов спечциально для этого, я делаю обьект { success, message, payload } контейнером для всех своих ответов по операциям, и в случае ошибки пишу success=false, message='error_i_am_your_father'. Так как REST фактически не регламентирован, то вокруг того какой подход верен идут ожесточенные споры)

По принципу наименьшего удивления устаревший метод не должен отвечать 500, так как это непредвиденная ошибка. И 200 тоже, так как фактически сервер не отработал. Success false и код 200 это вообще бред.

Бред не бред, а использование только GET/POST запросов с контейнером вместо CRUD like GET/PUT/POST/DELETE - это общепринятая практика. Потому что стандарта на REST нет и каждый пишет как хочет.

Да и сервер фактически отработал. Потому что неуспешность, как и отсутствие ответа - это тоже ответ. Если у вас есть решение как более корректно уведомить сторону клиента в публином api и не сломать его, то прошу к коллайдеру )

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

Имхо, то, как будет развиваться апи и что делать с устаревшими методами должно быть заранее обговорено или описано в каком-либо документе. Там же можно обозначить код ошибки, который мы будем отсылать на устаревшие методы (405, 418, не важно). Вместе с ним отвечаем, что метод устарел, обнови кодовую базу.
Само обновление мне видится в 2-х вариантах:
1-й вариант (научнофантастический) - мы заранее предупреждаем о "ломающих изменениях", далее кто успел обновить кодовую базу, тот сам себе молодец.
2-й - версионирование в пределах возможного. (все-таки некоторые изменения логики могут быть полностью несовместимы с устаревшими методами). Опционально описать время поддержки старых версии апи.

Вот за что я ненавижу "туториалы" (написание верное), так это за то, что никто не трудится объяснить фундаментальные вещи. Вот что такое синтаксис:

[Route("api/v{version:apiVersion}/[controller]")] [ApiController] [ApiVersion("2.0")]

Что это за магия "version:apiVersion"? Как она работает? Как мне сделать "{something:somethingElse}"? Я должен слепо верить что-ли?

То, что можно сделать контроллер на v2 коню понятно. Так все делают. А вот почему надо именно тот пакет юзаьть? Чем оно лучше просто тупо второго контроллера на v2?

Отвечая сам себе - эта магия зовется Custom Route Constraints (https://docs.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#route-constraint).

Но ответа на вопрос какую конкретно задачу данный пакет решает лучше простого наколеночного v2 (особенно при том, что старый код ничего о версионировании не знает) все-ж хочется.

Непонятно как у вас два класса с одинаковым названием WeatherForecastController в дефолтном namespace, пусть и в разных папках, не вызвали ошибки компиляции о том, что такой класс уже существует.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории