Intentar diseñar una API para aplicaciones externas con previsión para el cambio no es fácil, pero pensar un poco por adelantado puede facilitar la vida más adelante. Estoy tratando de establecer un esquema que admita cambios futuros sin dejar de ser compatible con versiones anteriores al dejar en su lugar controladores de versiones anteriores.
La principal preocupación en este artículo es qué patrón se debe seguir para todos los puntos finales definidos para un producto / empresa dado.
Esquema base
Dada una plantilla de URL base de https://rest.product.com/
He ideado que todos los servicios residen /api
junto con /auth
y otros puntos finales que no se basan en el descanso, como /doc
. Por lo tanto, puedo establecer los puntos finales básicos de la siguiente manera:
https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...
Puntos finales de servicio
Ahora para los puntos finales mismos. La preocupación por POST
, GET
, DELETE
no es el objetivo principal de este artículo y es la preocupación de los propios actos.
Los puntos finales se pueden dividir en espacios de nombres y acciones. Cada acción también debe presentarse de una manera que admita cambios fundamentales en el tipo de retorno o los parámetros requeridos.
Al tomar un servicio de chat hipotético donde los usuarios registrados pueden enviar mensajes, podemos tener los siguientes puntos finales:
https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send
Ahora para agregar soporte de versión para futuros cambios de API que pueden estar rompiendo. Podríamos agregar la firma de la versión después /api/
o después /messages/
. Dado el send
punto final, podríamos tener lo siguiente para v1.
https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send
Entonces, mi primera pregunta es, ¿cuál es un lugar recomendado para el identificador de versión?
Código de controlador de gestión
Por lo tanto, ahora hemos establecido que necesitamos admitir versiones anteriores, por lo que debemos manejar de alguna manera el código para cada una de las nuevas versiones que pueden dejar de funcionar con el tiempo. Suponiendo que estamos escribiendo puntos finales en Java, podríamos gestionar esto a través de paquetes.
package com.product.messages.v1;
public interface MessageController {
void send();
Message[] list();
}
Esto tiene la ventaja de que todo el código se ha separado mediante espacios de nombres donde cualquier cambio importante significaría una nueva copia de los puntos finales del servicio. El detrimento de esto es que todo el código debe copiarse y las correcciones de errores desean aplicarse a las versiones nuevas y anteriores, y deben aplicarse / probarse para cada copia.
Otro enfoque es crear controladores para cada punto final.
package com.product.messages;
public class MessageServiceImpl {
public void send(String version) {
getMessageSender(version).send();
}
// Assume we have a List of senders in order of newest to oldest.
private MessageSender getMessageSender(String version) {
for (MessageSender s : senders) {
if (s.supportsVersion(version)) {
return s;
}
}
}
}
Esto ahora aísla el control de versiones de cada punto final y hace que las correcciones de errores sean compatibles con el puerto posterior, ya que en la mayoría de los casos solo se deben aplicar una vez, pero significa que debemos hacer un poco más de trabajo en cada punto final individual para admitir esto.
Entonces está mi segunda pregunta "¿Cuál es la mejor manera de diseñar el código de servicio REST para admitir versiones anteriores".
fuente