Каковы ваши лучшие практики управления версиями WebService?

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

У меня есть эта статья от 2004 года, в которой утверждается, что настоящего стандарта не существует, и только лучшие практики. Любые лучшие решения? Как вы решаете проблемы с версиями WS?

описание проблемы

Система А

Клиент

class SystemAClient{
    SystemBServiceStub systemB;
    public void consumeFromB(){
        SystemBObject bObject = systemB.getSomethingFromB(new SomethingFromBRequest("someKey"));

    }
}

Оказание услуг

class SystemAService{
    public SystemAObject getSomethingFromA(SomethingFromARequest req){
        return new SystemAObjectFactory.getObject(req);
    }
}

Передаваемый объект

Версия 1

class SystemAObject{
     Integer id;
     String name;
     ... // getters and setters etc;
}

Версия 2

class SystemAObject{
     Long id;
     String name;
     String description;
     ... // getters and setters etc;
}

Объект запроса

Версия 1

class SomethingFromARequest {
     Integer requestedId;
     ... // getters and setters etc;

}

Версия 2

class SomethingFromARequest {
     Long requestedId;
     ... // getters and setters etc;

}

Система Б

Клиент

class SystemBClient{
    SystemAServiceStub systemA;
    public void consumeFromA(){
        SystemAObject aObject = systemA.getSomethingFromA(new SomethingFromARequest(1));
        aObject.getDescription() // fail point
        // do something with it...
    }
}

Оказание услуг

class SystemBService{
    public SystemBObject getSomethingFromB(SomethingFromBRequest req){
        return new SystemBObjectFactory.getObject(req);
    }
}

Передаваемый объект

Версия 1

class SystemBObject{
     String key;
     Integer year;
     Integer month;
     Integer day;

     ... // getters and setters etc;
}

Версия 2

class SystemBObject{
     String key;
     BDate date;
     ... // getters and setters etc;
}

class BDate{
     Integer year;
     Integer month;
     Integer day;
     ... // getters and setters etc;

}

Объект запроса

Версия 1

class SomethingFromBRequest {
     String key;
     ... // getters and setters etc;
}

Версия 2

class SomethingFromBRequest {
     String key;
     BDate afterDate;
     BDate beforeDate;
     ... // getters and setters etc;
}

Сценарии неудач

Если система A клиент версии 1 вызывает систему B службу < strong>версия 2 может дать сбой:

  • отсутствующие методы для SystemBObject (getYear(), getMonth(), getDay())
  • Неизвестный тип BDate

Если система A клиент версии 2 вызывает систему B службу < strong>версия 1 может дать сбой:

  • Неизвестный тип BDate на SomethingFromBRequest (клиент использует более новый объект запроса B, который не распознается версией B 1)
  • Если клиент системы A достаточно умен, чтобы использовать версию 1 объекта запроса, он может выйти из строя из-за отсутствующих методов в объекте SystemBObject (getDate()).

Если система B клиент версии 1 вызывает систему A службу < strong>версия 2 может дать сбой:

  • Введите несоответствие или переполнение для SystemAObject (возвращено Long, но ожидается Integer)

Если система B клиент версии 2 вызывает систему A службу < strong>версия 1 может дать сбой:

  • Введите несоответствие или переполнение в SystemARequest (запросите Long вместо Integer)
  • Если запрос каким-то образом прошел, возникают проблемы с кастингом (заглушка Long, но служба возвращает Integer, не обязательно совместимую со всеми реализациями WS)

Возможные решения

  1. Используйте числа при продвижении версий: например. SystemAObject1, SystemBRequest2 и т. д., но отсутствует API для сопоставления исходной/целевой версии.
  2. В подписи передавать XML, а не объекты (фу, передавать экранированный XML в XML, двойная сериализация, десериализация/разбор, распарсинг)
  3. Другое: напр. есть ли средство Document/literal/WS-I?

person Eran Medan    schedule 20.01.2010    source источник
comment
+1 - Подробный и четкий вопрос   -  person ruchirhhi    schedule 20.01.2010
comment
Я решил принять ответ Джастина, хотя он субъективен, и оба являются хорошим примером отличных ответов.   -  person Eran Medan    schedule 24.01.2010


Ответы (4)


Я предпочитаю метод управления версиями Salesforce.com. Каждая версия веб-служб получает отдельный URL-адрес в формате:

http://api.salesforce.com/{version}/{serviceName}

Таким образом, у вас будут URL-адреса веб-службы, которые выглядят так:

http://api.salesforce.com/14/Lead

http://api.salesforce.com/15/Lead

и так далее...

Используя этот метод, вы получаете следующие преимущества:

  1. Вы всегда знаете, с какой версией разговариваете.

  2. Обратная совместимость сохраняется.

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

person Justin Niessner    schedule 20.01.2010
comment
Наш опыт показывает, что это лучший способ решения проблемы. - person NotMe; 29.04.2010
comment
Кроме того, вы можете использовать обратный прокси-сервер и развернуть несколько веб-серверов, на которых размещаются разные версии для разных URL-адресов, чтобы облегчить жизнь при их обслуживании. Если вы можете создать файл образа для своего приложения и автоматизировать развертывание, это будет еще лучше. - person Muhatashim; 17.09.2018

Решение состоит в том, чтобы избежать несовместимых изменений ваших типов.

Возьмем, к примеру, SystemBObject. Вы описываете "версию 1" и "версию 2" этого типа, но они совсем не одного типа. Совместимое изменение этого типа включает только добавление свойств, но не изменение типа каких-либо существующих свойств. Ваше гипотетическое «обновление версии» нарушило оба этих ограничения.

Следуя этому единственному принципу, вы можете избежать ВСЕХ проблем, которые вы описали.

Поэтому, если это ваше определение типа в версии 1

class SystemBObject{  // version 1
    String key;  
    Integer year;  
    Integer month;  
    Integer day;  

    ... // getters and setters etc;  
}  

Тогда это не может быть вашим определением типа в v2:

// version 2 - NO NO NO 
class SystemBObject{ 
    String key; 
    BDate date; 
    ... // getters and setters etc; 
} 

... потому что он устранил существующие поля. Если это то изменение, которое вам нужно сделать, это не новая «версия», это новый тип, и он должен называться как таковой как в коде, так и в формате сериализации.

Другой пример: если это ваш существующий тип v1:

class SomethingFromARequest {   
    Integer requestedId;   
    ... // getters and setters etc;      
}   

... тогда это не допустимый "v2" этого типа:

class SomethingFromARequest {   
    Long requestedId;   
    ... // getters and setters etc;      
}   

... потому что вы изменили тип существующего свойства.

Эти ограничения более подробно объясняются в основном технологически нейтральным способом в управлении версиями службы Microsoft. статья.


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

Кроме того, вы можете обеспечить прямую совместимость в своих объектах передачи данных, используя нестрогую обработку и отображая «лишние» данные в «дополнительное» поле. Если XML является вашим форматом сериализации, вы можете использовать xsd:xmlAny или xsd:any и processContents="lax" для захвата любых нераспознанных элементов схемы, когда служба v1 получает запрос v2 (подробнее< /а>). Если вы используете формат сериализации JSON с более открытой моделью контента, то это бесплатно.

person Cheeso    schedule 20.01.2010
comment
Я хотел бы добавить, что новые свойства в классах запросов всегда должны быть необязательными (minOccurs = 0), иначе вы все равно сломаете существующий код клиентов. - person Dmitry Ornatsky; 20.01.2010
comment
@Cheeso - Поскольку я не эксперт по W / S и, вероятно, что-то упустил, я могу только частично согласиться, исходя из моего небольшого опыта, если вы просто добавите свойства к объектам системы A - клиенты версии 1 в Системе B все еще могут работать . Но клиент в системе B, который был скомпилирован для версии 2 служб системы A, вызывает версию 1 служб системы A — он может дать сбой на уровне SOAP (неожиданный атрибут/элемент и т. д.) — это происходит с нами, использующими GSoap (C++ GSoap клиент со службой Java Axis). Я думаю, что это то, что Дмитрий упомянул в своем комментарии, верно? - person Eran Medan; 20.01.2010
comment
Да; minOccurs должен быть равен нулю для вновь добавленных свойств в классах сообщений. - person Cheeso; 20.01.2010

Я знаю, что это поздно для игры, но я довольно глубоко копался в этом вопросе. Я действительно думаю, что лучший ответ включает в себя еще одну часть головоломки: сервисный посредник. Одним из примеров является Managed Services Engine от Microsoft — я уверен, что существуют и другие. По сути, изменив пространство имен XML вашей веб-службы (чтобы включить номер версии или дату, как упоминается в связанной статье), вы предоставляете посреднику возможность направлять различные клиентские вызовы на соответствующие реализации сервера. Дополнительная (и, ИМХО, очень крутая) функция MSE — возможность выполнять преобразование на основе политик. Вы можете определить XSLT-преобразования, которые преобразовывают запросы версии 1 в запросы версии 2 и ответы версии 2 в ответы версии 1, что позволяет выводить из эксплуатации реализации службы версии 1, не нарушая работу клиентских реализаций.

person Harper Shelby    schedule 28.04.2010
comment
Спасибо, но где ссылка на статью, которую вы упомянули? :) - person Eran Medan; 29.04.2010
comment
Я имел в виду статью, на которую вы ссылались в исходном вопросе, - это также была моя отправная точка в этом исследовании. - person Harper Shelby; 29.04.2010

Я думаю, что еще кое-что, о чем следует помнить, это ваша клиентская база, публикуете ли вы эту услугу публично, или она ограничена набором известных агентов?

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

Хотя это лишь косвенно связано с вашим вопросом, мы обнаружили, что использование нашего номера версии на основе совместимости работает довольно хорошо. На примере A.B.C...

  • A: Изменения, требующие перекомпиляции (нарушают обратную совместимость)
  • B: Изменения, которые не требуют перекомпиляции, но имеют дополнительные функции, недоступные без нее (новые операции и т. д.)
  • C: Изменения базовой механики, не влияющие на WSDL.
person Neil McF    schedule 28.04.2010