unidad tokens servicios services seguridad prácticas implementar buenas web-services architecture versioning

web-services - tokens - unidad 1 seguridad en servicios web



¿Cuáles son sus mejores prácticas de versionamiento de servicio web? (4)

Creo que otra cosa a tener en cuenta es su base de clientes, ¿está publicando este servicio públicamente o está restringido a un conjunto de agentes conocidos?

Estoy involucrado en la última situación, y hemos descubierto que no es tan difícil resolver esto a través de una simple comunicación / partes interesadas.

Aunque solo está relacionado de manera indirecta con su pregunta, hemos encontrado que el hecho de basar nuestro número de versiones en la compatibilidad parece funcionar bastante bien. Usando ABC como ejemplo ...

  • A: Cambios que requieren recompilación (rompe la compatibilidad hacia atrás)
  • B: Cambios que no requieren recompilación, pero tienen funciones adicionales que no están disponibles sin hacerlo (nuevas operaciones, etc.)
  • C: Cambios en los mecanismos subyacentes que no alteran el WSDL

Tenemos 2 productos separados que necesitan comunicarse entre sí a través de servicios web. ¿Cuál es la mejor práctica para apoyar la versificación de la API?

Tengo este artículo de 2004 que afirma que no existe un estándar real, y solo las mejores prácticas. ¿Alguna solución mejor? ¿Cómo resuelves las versiones WS?

Descripción del problema

Sistema a

Cliente

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

Servicio

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

Objeto transferible

Versión 1

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

Versión 2

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

Solicitar objeto

Versión 1

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

Versión 2

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

Sistema b

Cliente

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

Servicio

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

Objeto transferible

Versión 1

class SystemBObject{ String key; Integer year; Integer month; Integer day; ... // getters and setters etc; }

Versión 2

class SystemBObject{ String key; BDate date; ... // getters and setters etc; } class BDate{ Integer year; Integer month; Integer day; ... // getters and setters etc; }

Solicitar objeto

Versión 1

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

Versión 2

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

Escenarios de falla

Si un cliente del Sistema A de la versión 1 llama a un servicio del Sistema B de la versión 2 , puede fallar en:

  • métodos que faltan en SystemBObject ( getYear() , getMonth() , getDay() )
  • Tipo desconocido BDate

Si un cliente del Sistema A de la versión 2 llama a un servicio del Sistema B de la versión 1 , puede fallar:

  • Tipo desconocido BDate en el SomethingFromBRequest (Un cliente utiliza un objeto de solicitud B más reciente que B versión 1 no reconoce)
  • Si el cliente del Sistema A es lo suficientemente inteligente como para usar la versión 1 del objeto de solicitud, puede fallar en los métodos faltantes en el objeto getDate() )

Si un cliente del Sistema B de la versión 1 llama a un servicio del Sistema A de la versión 2 , puede fallar:

  • Escriba missmatch o overflow en SystemAObject (devuelto Integer pero esperado Integer )

Si un cliente del Sistema B de la versión 2 llama a un servicio del Sistema A de la versión 1 , puede fallar en:

  • Escriba missmatch o overflow en SystemARequest (solicitud Long lugar de Integer )
  • Si la solicitud se aprobó de alguna manera, los problemas de conversión (el código auxiliar es Long pero el servicio devuelve un Integer no es necesariamente compatible en todas las implementaciones de WS)

Soluciones posibles

  1. Use números cuando avance en las versiones: por ejemplo, SystemAObject1 , SystemAObject1 , etc., pero esto le falta una API para la versión de origen / destino correspondiente
  2. En la firma, pase XML y no objetos (yuck, pase XML escapado en XML, doble serialización, deserialización / análisis, análisis no analizado)
  3. Otro: por ejemplo, ¿Document / literal / WS-I tiene un remedio?

La solución es evitar cambios incompatibles a sus tipos.

Tomemos, por ejemplo, SystemBObject. Describe "versión 1" y "versión 2" de este tipo, pero no son del mismo tipo. Un cambio compatible con este tipo implica solo agregar propiedades, y no cambiar el tipo de ninguna propiedad existente. Su hipotética "Actualización de versión" ha violado ambas restricciones.

Al seguir esa única línea de referencia, puede evitar TODOS los problemas que describió.

Por lo tanto, si esta es su definición de tipo en la versión 1

class SystemBObject{ // version 1 String key; Integer year; Integer month; Integer day; ... // getters and setters etc; }

Entonces, esta no puede ser tu definición de tipo en v2:

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

... porque ha eliminado campos existentes. Si ese es el cambio que necesita hacer, no es una nueva "versión", es un tipo nuevo, y debe ser nombrado como tal, tanto en código como en el formato de serialización.

Otro ejemplo: si este es tu tipo v1 existente:

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

... entonces esto no es un "v2" válido de ese tipo:

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

... porque ha cambiado el tipo de la propiedad existente.

Estas restricciones se explican con mucho más detalle de una manera mayoritariamente neutral en lo que respecta a la tecnología en el artículo de Microsoft Service Versioning .

Además de evitar esa fuente de incompatibilidad, puede y debe incluir un número de versión en el tipo. Este puede ser un simple número de serie. Si tiene la costumbre de registrar o auditar mensajes, y el ancho de banda y el espacio de almacenamiento no son un problema, es posible que desee aumentar el número entero simple con un UUID para identificar una instancia de cada versión única de un tipo.

Además, puede diseñar compatibilidad hacia adelante en sus objetos de transferencia de datos, mediante el procesamiento laxo y la asignación de datos "adicionales" en un campo "adicional". Si XML es su formato de serialización, entonces puede usar xsd: xmlAny o xsd: any y processContents = "lax" para capturar cualquier elemento de esquema no reconocido, cuando un servicio v1 recibe una solicitud v2 ( more ). Si su formato de serialización es JSON, con su modelo de contenido más abierto, esto es gratis.


Prefiero el método de versionamiento de Salesforce.com. Cada versión de los Servicios Web obtiene una URL distinta en el formato de:

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

Así que tendrás URLs de servicios web que parecen:

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

y así...

Con este método, obtienes los beneficios de:

  1. Siempre sabes con qué versión estás hablando.

  2. Se mantiene la compatibilidad hacia atrás.

  3. No tiene que preocuparse por los problemas de dependencia. Cada versión tiene el conjunto completo de servicios. Solo debe asegurarse de no mezclar las versiones entre las llamadas (pero eso depende del consumidor del servicio, no de usted como desarrollador).


Sé que esto es tarde para el juego, pero he estado investigando este tema con bastante profundidad. Realmente creo que la mejor respuesta involucra otra pieza del rompecabezas: un intermediario de servicios. El motor de servicios administrados de Microsoft es un ejemplo de uno: estoy seguro de que también existen otros. Básicamente, al cambiar el espacio de nombres XML de su servicio web (para incluir un número de versión o fecha, como lo menciona el artículo vinculado), le permite al intermediario la capacidad de enrutar las diversas llamadas de clientes a las implementaciones de servidor apropiadas. Una característica adicional (y, IMHO, muy interesante) de MSE es la capacidad de realizar una transformación basada en políticas. Puede definir transformaciones XSLT que convierten las solicitudes v1 en solicitudes v2 y las respuestas v2 en respuestas v1, lo que le permite retirar las implementaciones del servicio v1 sin romper las implementaciones del cliente.