web services - example - Diseño RESTful de una red social
rest api example (2)
Como mencionó @Mark Dickinson, hay muchas preguntas aquí, que realmente deberían estar separadas, pero haré mi mejor esfuerzo.
Jerarquía vs. diseño plano
No hay nada en REST que sugiera que no se pueden tener múltiples jerarquías paralelas (aunque entiendo que hacerlo con Rails es incómodo). Tener /users/42/photos/owner
y /photos/owner/42
contenga el mismo conjunto está bien. De manera similar, /users/42/photos/tagged
y /photos/tagged/42
pueden contener el mismo conjunto. Sin embargo, no debería preocuparse por los URI en este momento. Existe un excelente artículo Una RESTful API de Hypermedia en Tres sencillos pasos que describe cómo diseñar su API. En este artículo, los URI se deciden como el último paso.
Además, con el uso de HATEOAS contstraint, estos diversos URI deben ser descubiertos en tiempo de ejecución por el cliente, a través de los enlaces y formularios proporcionados por su aplicación.
Almacenamiento en caché cuando el contenido es diferente para diferentes usuarios
Si va a publicar contenido diferente desde la misma URL, no será almacenable en caché. Puede dividir su sitio en dos tipos de contenido, público y personalizado. El contenido público debe ser el mismo para todos y se puede almacenar en caché. El contenido personalizado es diferente para cada usuario, lo que significa que la cantidad que puede almacenar en caché se reducirá drásticamente (reducido a cero utilizando el formato de URL que ha utilizado en sus ejemplos).
Para obtener al menos una pequeña cantidad de almacenamiento en caché en el contenido personalizado, particione el contenido por el usuario, de modo que para un usuario dado obtendrá algunos éxitos de caché. Por ejemplo, en lugar de tener /users/42
que todos puedan acceder, use /<UID>/users/42
donde está el userid del usuario solicitante. por ejemplo, el usuario 234 accedería a la página de perfil para el usuario 42 usando el URI /234/users/42
. Para usuarios anónimos, puede eliminar la parte /<UID
o utilizar un ID de usuario específico para ellos, como /public/users/42
.
Proporcionando HTML y JSON / XML desde el mismo recurso
Use el encabezado Accept
. Para eso está ahí.
Limitar los datos enviados de vuelta
No necesita hacer /users
una solicitud ilegal. Treat es una colección y devuelve una lista paginada de usuarios que el solicitante puede ver. Por ejemplo, para una solicitud anónima, puede proporcionar una lista vacía (o 204 Sin contenido)
<users/>
y para un usuario que ha iniciado sesión en particular, puede proporcionar a sus amigos.
<users>
<user id="42" href="/users/42" name="John Doe"/>
<user id="53" href="/users/53" name="Jane Doe"/>
...
<next href="/users?page=2"/>
</users>
cuando ese usuario luego GET /users?page=2
usted proporcionaría la siguiente página de resultados
<users>
<user id="69" href="/users/69" name="John Smo"/>
<user id="84" href="/users/84" name="Jane Smo"/>
...
<next href="/users?page=3"/>
<prev href="/users"/>
</users>
Con la última página de resultados que no proporciona el next
enlace. Para agregar una capacidad de búsqueda, solo agrega un formulario apropiado como parte de la respuesta.
<users>
<user id="69" href="/users/69" name="John Smo"/>
<user id="84" href="/users/84" name="Jane Smo"/>
...
<next href="/users?page=2"/>
<prev href="/users"/>
<search href="/users" method="get">
<name cardinality="required" type="regex"/>
</search>
</users>
Los resultados de la búsqueda se paginan, al igual que la lista /users
. por ejemplo, la búsqueda de leet hacker
(suponiendo que tenga permiso para ver muchos piratas informáticos leet en el sistema) produciría algo así como
<users>
<user id="234" href="/users/234" name="leet hacker"/>
<user id="999" href="/users/999" name="leet hacker"/>
...
<next href="/users?name=leet+hacker&page=2"/>
<search href="/users" method="get">
<name cardinality="required" type="regex"/>
</search>
</users>
sin embargo, probablemente necesite proporcionar más detalles en el elemento del usuario, para que los hackers leet puedan ser diferenciados.
Controladores que proporcionan datos redundantes
Ambos son aceptables. Sin embargo, como anteriormente (por razones de almacenamiento en caché) usaría /<UID>/users/42
, en cuyo caso podría querer redirigir /42/users/me
a /42/users/42
.
Esto también ayuda desde el punto de vista analítico, ya que es posible que desee rastrear por separado a los usuarios el acceso a su propia página, desde el acceso de los usuarios a la página de otros usuarios, para averiguar qué usuarios son populares y cuáles son narcisistas. Incluso puede encontrar una correlación entre los dos :)
Derechos extendidos para algunos usuarios
Proporcione los enlaces de administrador y los formularios en la respuesta adecuada. Por ejemplo, acceder a los detalles de la imagen de otra persona puede proporcionar
<image id="266" href="/photos/266" caption="leet hacker with computer"
src="http://us.123rf.com/400wm/400/400/creatista/creatista0911/creatista091100003/5827629.jpg"/>
<tagged-users>
<user id="234" href="/users/234" name="leet hacker"/>
</tagged-users>
<owner id="234" href="/users/234" name="leet hacker"/>
</image>
pero para el propietario de la imagen, podría proporcionar
<image id="266" href="/photos/266" caption="leet hacker with computer"
src="http://us.123rf.com/400wm/400/400/creatista/creatista0911/creatista091100003/5827629.jpg"/>
<tagged-users>
<tagged-user id="234" href="/users/234" name="leet hacker">
<delete href="/photos/266/tagged/234" method="delete"/>
</tagged-user>
<add href="/photos/266" method="put">
<user cardinality="required" type="user-id"/>
</add>
</tagged-users>
<owner id="234" href="/users/234" name="leet hacker"/>
<delete href="/photos/266" method="delete"/>
<update href="/photos/266" method="put">
<caption cardinality="optional" type="string"/>
</update>
</image>
Actualizaciones de localización y configuración
Utilice Accept-Language
para el idioma predeterminado, pero permita que un usuario cambie el idioma en su configuración.
Estoy tratando de entender el diseño REST y me gustaría entenderlo en términos de una red social. He leído REST API Design Rulebook , busqué en otros libros y leí muchos recursos en línea. Aún así, al aplicar las reglas a los problemas del mundo real, me doy cuenta de que no entiendo completamente todo. Quiero saber si entiendo correctamente REST especificando algunos problemas:
Jerarquía vs. diseño plano
Supongamos que identifico a un usuario particular con
/users/42
Ahora, las fotos cargadas por ese usuario terminarían en
/users/42/photos
y si él / ella etiqueta a sus amigos en la foto esas etiquetas terminarían en
/users/42/photos/1337/tags
Pero esto no proporciona ninguna forma de encontrar todas las fotos donde un usuario en particular está etiquetado. ¿Debo encontrar una jerarquía diferente para eso? Parece un poco incomodo. ¿Puedo ignorar por completo la jerarquía y proporcionar fotos en combinación con consultas como esta?
/photos?owner=42
/photos?tagged=42
Almacenamiento en caché cuando el contenido es diferente para diferentes usuarios
Un servicio web debe diseñarse para proporcionar datos cacheables (de modo que el cliente pueda decidir utilizar la copia local, si cree que no se ha cambiado nada), pero ¿cómo afecta eso la configuración de privacidad para diferentes usuarios? Dos usuarios, ambos conectados, pueden tener los derechos para ver información diferente sobre, por ejemplo, el usuario 42. ¿Eso significa que de alguna manera tengo que solicitar diferentes URI para diferentes usuarios accediendo a la información de perfil del mismo usuario, o el almacenamiento en caché no será un problema siempre y cuando los usuarios proporcionen credenciales diferentes?
Proporcionando HTML y JSON / XML desde el mismo recurso
El libro que mencioné especifica la regla de que una API debe estar accesible en un subdominio que comience con api
, en su ejemplo http://api.soccer.restapi.org
. Había planeado usar el mismo controlador tanto para el acceso del usuario como para el acceso a la máquina (por ejemplo, una aplicación móvil). El controlador decidiría qué vista proporcionar ( text/html
, application/json
o application/xml
) a través del campo Accept
en el encabezado de solicitud HTTP. Supongo que sería una mala idea por algún motivo (ya que un usuario esperaría ver el subdominio www
, no api
), pero no entiendo por qué. ¿Pueden www
y api
apuntar al mismo servidor, o debería realmente tratar de mover la vista HTML a un host virtual diferente? ¿Por qué?
Creo que Ruby on Rails (Convención sobre configuración) proporcionará tanto HTML como JSON desde el mismo controlador, compartiendo así mi idea de que HTML y JSON son solo representaciones diferentes de los mismos datos.
En otras palabras, mi libro dice que un cierto recurso debe tener un solo URI, y que se deben proporcionar diferentes representaciones basadas en el campo Accept
. Redirigir al usuario entre diferentes subdominios violaría la regla de proporcionar cualquier representación del mismo recurso, y duplicar la información (es decir, señalar dos subdominios al mismo host virtual) infringe la regla de no proporcionar múltiples URI para el mismo recurso. No proporcionar el subdominio api
viola otra regla de diseño. ¿Cómo puedo resolver esto sin violar ninguna regla?
Limitar los datos enviados de vuelta
El componente de consulta se debe usar para la paginación, pero ¿puedo negarme a aceptar las solicitudes de listado que carecen de criterios de búsqueda y limitar el número de elementos, sin violar REST? Quiero reducir la carga de la base de datos y evitar que alguien mapee todo el directorio de usuarios. yo quiero
/users
ser una solicitud ilegal, mientras
/users?name=leet+hacker
sería válido, pero solo devolvería, por ejemplo, 100 artículos.
También me pregunto si es legal devolver un subconjunto de las columnas de la base de datos y más / todos ellos solo si se solicitan específicamente mediante una consulta.
Controladores que proporcionan datos redundantes
Creo que es legal proporcionar un controlador como
/users/me
pero debería proporcionar la misma información que el documento URI
/users/42
o debería redirigir a ella?
Derechos extendidos para algunos usuarios
¿Cuál es la forma RESTful de proporcionar funcionalidad adicional, por ejemplo, derechos de administración? Ahora estoy asumiendo que un administrador (de una foto, de un grupo de usuarios o de todo el sitio) podrá ver más información sobre un objeto en particular que otros usuarios. En caso de que esa información se guarde exactamente en el mismo URI y se envíe automáticamente al administrador, pero no a otra persona, ¿debería almacenarse en una ubicación diferente, debería solicitarse utilizando una determinada consulta del administrador o proporcionarse de alguna otra manera?
Actualizaciones de localización y configuración
Aunque la mayoría de las cadenas visibles para el usuario deben ser provistas por la vista, hay algunas decisiones de diseño que pueden involucrar a la API. Los más obvios son los nombres. Las redes sociales a veces permiten al usuario ingresar diferentes nombres que se mostrarán en diferentes contextos de idiomas. Los nombres en algunos idiomas, como el ruso y el árabe, no se transcriben fácilmente de forma automática. En otros idiomas, como el chino, los nombres locales e internacionales pueden ser completamente diferentes, sin semejanza ni conexión. ¿Cuál sería la forma RESTANTE de manejar esto? Tengo la sensación de que la respuesta será el campo Accept-Language
, pero ¿alguien está considerando seriamente eso para cambiar idiomas en la red social a la que pertenecen? ¿Debería devolver toda esta información a la persona que llama cada vez, o puedo confiar en la configuración? ¿Funcionará eso con un caché?
Si está utilizando WCF Web Api (o incluso si no lo está), puede valer la pena ver el soporte de OData allí. Aborda algunos de los problemas de hacer que las cosas sean más buscables.