¿Es una buena práctica hacer una lista de valores de respuesta API como diccionario?

7

Tengo un punto final de API que devuelve algunas estadísticas. Actualmente la respuesta se ve como:

Opción 1:

{
    "stats": [
                {
                    "name": "some-stats-key1",
                    "value": 10
                },
                {
                    "name": "some-stats-key2",
                    "value": 20
                }
            ],
    ... other keys
}

Pero esto parece un poco complejo y tengo que hacer lo siguiente:

Opción 2:

{
    "stats": {
                "some-stats-key1": 10,
                "some-stats-key2": 20
            }
    ... other keys
}

Comprendo que la opción 1 es más fácil de ampliar, pero menos cómoda para los usuarios. ¿A qué otros problemas puedo enfrentar usando una de estas opciones? O debería hacer una solución híbrida como:

Opción 3:

{
    "stats": {
                "some-stats-key1": {
                                        "name": "some-stats-key1",
                                        "value": 10
                                    },
                "some-stats-key2": {
                                        "name": "some-stats-key2",
                                        "value": 20
                                    },
            },
    ... other keys
}

Las claves "some-stats-key1" y "some-stats-key2" son solo valores internos y se espera que el usuario de la API las asigne a nombres legibles usando la documentación. Todas las claves son únicas.

El orden de las "estadísticas" no es importante.

El caso de uso típico es obtener todas las estadísticas, relacionar claves con nombres legibles y mostrarlas como una tabla en una página web. Pero actualmente no puedo decir si nadie necesitará solo una parte de las estadísticas más adelante.

¿Existe alguna práctica recomendada para este problema?

    
pregunta Yann 04.07.2017 - 13:51

4 respuestas

8

Elegiría la opción 2. Si el consumidor de API convierte some-stats-key1 en algo legible, eso probablemente significa que él / ella tiene una lista de valores en los que está interesado (por ejemplo, some-stats-key1 y some-stats-key3 ), y se repetirá sobre esa lista. Al elegir un objeto JSON, se deserializará como un diccionario / mapa que proporciona una búsqueda conveniente para el consumidor de la API.

Esto será más engorroso con la opción 1, donde el consumidor debe recorrer la matriz JSON o crear previamente su propio diccionario con claves interesantes.

La opción 3 es un poco demasiado detallada para mí, la duplicación de los nombres de las teclas simplemente no me atrae.

Si la extensibilidad es una preocupación, siempre puede publicar un v2 de su API que devuelva algo como

"stats": {
    "some-stats-key1": { "value": 10, "error-margin": 0.1 },
    "some-stats-key2": { "value": 20, "error-margin": 0.2 }
}

y conserva la v1 para compatibilidad con versiones anteriores. Mantener la compatibilidad con versiones anteriores dentro de una única versión de la API puede ser un PITA real si no tiene un control completo sobre cómo se consume la API. He visto un "quiebre" de una API mía cuando acabo de agregar un par clave-valor extra (opcional) (es decir, sin cambiar la estructura).

    
respondido por el Glorfindel 04.07.2017 - 14:38
5

Las dos opciones tienen las ventajas clásicas de Lista vs. Mapa.

1) La Lista permite entradas duplicadas y mantiene el orden. Si estas características son importantes, use la Lista, aunque sea más desordenada.

2) El Mapa no permite duplicados. Mantener el orden es posible con un poco de trabajo extra. La gran ventaja es una mayor simplicidad en el formato de datos, y la búsqueda de un elemento en particular es trivial.

Mi opción predeterminada es siempre el Mapa más simple, pero YMMV.

    
respondido por el user949300 05.07.2017 - 23:08
3

Cuando obtengo datos de una API, siempre compruebo que todo es como lo esperaba. Por lo tanto, mi esfuerzo por procesar sus datos consiste en la verificación y el procesamiento real.

En el caso 1 tengo que verificar: a. Hay una matriz. segundo. Todos los elementos de la matriz son diccionarios. do. Cada diccionario tiene una clave "nombre". re. Todos los valores para la clave "nombre" son únicos.

En el caso 3 tengo que verificar: a. Hay un diccionario. segundo. Todos los valores en el diccionario son diccionarios. do. Cada diccionario tiene una clave "nombre" con un valor que coincide con la clave en el diccionario externo. Ligeramente mejor.

En el caso 2 tengo que verificar: a. Hay un diccionario.

(Por supuesto que también tengo que verificar los valores). Así que su caso 2 requiere la menor cantidad de control de mi parte. Realmente obtengo una estructura de datos que es inmediatamente utilizable.

El único problema con 2 es que no es extensible. Entonces, en lugar de enviar el valor como un número, puede enviar {valor: 10} que luego se puede extender de una manera compatible hacia atrás.

La redundancia es mala. Lo único que logra la redundancia es hacerme escribir más código y obligarme a pensar qué debo hacer si los bits redundantes no están de acuerdo. La versión 2 no tiene redundancia.

    
respondido por el gnasher729 05.07.2017 - 10:02
0

Desde que pidió buenas prácticas para el diseño de API:

  • Nunca devuelvo una respuesta api que contenga un objeto en el nivel superior. Todas las llamadas de servicio devuelven un conjunto (como una matriz), y ese conjunto contiene elementos (como instancias de objeto). La cantidad de objetos devueltos en la matriz es irrelevante para el servidor. Si el cliente necesita hacer una determinación a partir del número de elementos devueltos en la respuesta, esa carga es la que debe hacer cumplir el cliente. Si se espera un solo artículo, se devuelve como un conjunto de unidades
  • Cuando estoy diseñando una api, no presumo saber qué tecnología específica utilizarán mis api client para consumir dicha api, incluso cuando sé qué tecnología específica es más probable que utilicen. Mi responsabilidad es comunicar las representaciones de mi dominio de manera consistente a todos los consumidores, no convenientemente a un consumidor en particular.
  • Si existe una opción estructural para permitir que se componga la respuesta, esta estructura tiene prioridad sobre las opciones que no lo hacen
  • Me esfuerzo por evitar crear representaciones que tengan estructuras impredecibles.
  • Si se puede predecir la estructura de una representación, entonces todas las instancias dentro del conjunto de respuestas deben ser de la misma representación y el conjunto de respuestas debe ser internamente coherente.

Por lo tanto, dadas las estructuras que propusiste, la estructura que implementaría tendría un aspecto similar a este

[
   { /* returns only the 'common' metrics */
      "stats": [
                  {"key":"some-metric1","value":10},
                  {"key":"some-metric2","value":20}
               ]
   },
   { /* returns an optional metric in addition to the "common" metrics */
      "stats": [
                  {"key":"some-metric1","value":15},
                  {"key":"some-metric2","value":5},
                  {"key":"some-optional-metric", "value":42}
               ]
   },
   { /*returns the 'common' metrics as well as 2 candidates for "foo-bar" */
      "stats": [
                  {"key":"some-metric1", "value": 5},
                  {"key":"some-metric2", "value": 10},
                  {"key":"foo-bar-candidate", "value": 7},
                  {"key":"foo-bar-candidate", "value": 11}
               ]
   }
]
    
respondido por el K. Alan Bates 06.07.2017 - 03:45

Lea otras preguntas en las etiquetas