0 follower

Versionado

Una buena API ha de ser versionada: los cambios y las nuevas características son implementadas en las nuevas versiones del API, en vez de estar continuamente modificando sólo una versión. Al contrario que en las aplicaciones Web, en las cuales tienes total control del código de ambas partes lado del cliente y lado del servidor, las APIs están destinadas a ser usadas por los clientes fuera de tu control. Por esta razón, compatibilidad hacia atrás (BC Backward compatibility) de las APIs ha de ser mantenida siempre que sea posible. Si es necesario un cambio que puede romper la BC, debes de introducirla en la nueva versión del API, e incrementar el número de versión. Los clientes que la usan pueden continuar usando la antigua versión de trabajo del API; los nuevos y actualizados clientes pueden obtener la nueva funcionalidad de la nueva versión del API.

Consejo: referirse a Semántica del versionado para más información en el diseño del número de versión del API.

Una manera común de implementar el versionado de la API es embeber el número de versión en las URLs de la API. Por ejemplo, https://example.com/v1/users se refiere al punto final /users de la versión 1 de la API.

Otro método de versionado de la API, la cual está ganando predominancia recientemente, es poner el número de versión en las cabeceras de la petición HTTP. Esto se suele hacer típicamente a través la cabecera Accept:

// vía parámetros
Accept: application/json; version=v1
// vía de el tipo de contenido del proveedor
Accept: application/vnd.company.myapp-v1+json

Ambos métodos tienen sus pros y sus contras, y hay gran cantidad de debates sobre cada uno. Debajo puedes ver una estrategia práctica para el versionado de la API que es una mezcla de estos dos métodos:

  • Pon cada versión superior de la implementación de la API en un módulo separado cuyo ID es el número de la versión mayor (p.e. v1, v2). Naturalmente, las URLs de la API contendrán números de versión mayores.
  • Dentro de cada versión mayor (y por lo tanto, dentro del correspondiente módulo), usa la cabecera de HTTP Accept para determinar el número de la versión menor y escribe código condicional para responder a la menor versión como corresponde.

Para cada módulo sirviendo una versión mayor, el módulo debe incluir las clases de recursos y y controladores que especifican la versión. Para separar mejor la responsabilidad del código, puedes conservar un conjunto común de clases base de recursos y controladores, y hacer subclases de ellas en cada versión individual del módulo. Dentro de las subclases, impementa el código concreto como por ejemplo Model::fields().

Tu código puede estar organizado como lo que sigue:

api/
    common/
        controllers/
            UserController.php
            PostController.php
        models/
            User.php
            Post.php
    modules/
        v1/
            controllers/
                UserController.php
                PostController.php
            models/
                User.php
                Post.php
        v2/
            controllers/
                UserController.php
                PostController.php
            models/
                User.php
                Post.php

La configuración de tu aplicación puede tener este aspecto:

return [
    'modules' => [
        'v1' => [
            'basePath' => '@app/modules/v1',
            'controllerNamespace' => 'app\modules\v1\controllers',
        ],
        'v2' => [
            'basePath' => '@app/modules/v2',
            'controllerNamespace' => 'app\modules\v2\controllers',
        ],
    ],
    'components' => [
        'urlManager' => [
            'enablePrettyUrl' => true,
            'enableStrictParsing' => true,
            'showScriptName' => false,
            'rules' => [
                ['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']],
                ['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']],
            ],
        ],
    ],
];

Como consecuencia del código anterior, https://example.com/v1/users devolverá la lista de usuarios en la versión 1, mientras https://example.com/v2/users devolverá la versión 2 de los usuarios.

Gracias a los módulos, el código de las diferentes principales versiones puede ser aislado. Pero los módulos hacen posible reutilizar el código a través de los módulos vía clases base comunes y otros recursos compartidos.

Para tratar con versiones menores, puedes tomar ventaja de la característica de negociación de contenido provista por el comportamiento (behavior) contentNegotiator. El comportamiento contentNegotiator definirá la propiedad yii\web\Response::$acceptParams cuando determina qué tipo de contenido soportar.

Por ejemplo, si una petición es enviada con la cabecera HTTP Accept: application/json; version=v1, después de la negociación de contenido, yii\web\Response::$acceptParams contendrá el valor ['version' => 'v1'].

Basado en la información de versión contenida en acceptParams, puedes escribir código condicional en lugares como acciones, clases de recursos, serializadores, etc. para proveer la funcionalidad apropiada.

Dado que por definición las versiones menores requireren mantener la compatibilidad hacia atrás, con suerte no tendrás demasiadas comprobaciones de versión en tu código. De otra manera, probablemente puede ocurrir que necesites crear una versión mayor.

Found a typo or you think this page needs improvement?
Edit it on github !