HATEOAS: ¿URL absolutas o relativas?

Respuestas:

83

Existe una ambigüedad conceptual sutil cuando la gente dice "URI relativo".

Según la definición de RFC3986 , un URI genérico contiene:

  URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

  hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment

Lo complicado es que, cuando se omiten el esquema y la autoridad, la parte de la "ruta" en sí misma puede ser una ruta absoluta (comienza con /) o una ruta relativa "sin raíces". Ejemplos:

  1. Un URI absoluto o un URI completo:"http://example.com:8042/over/there?name=ferret"
  2. Y este es un uri relativo, con ruta absoluta :/over/there
  3. Y este es un uri relativo, con ruta relativa : hereo ./hereo ../hereo etc.

Entonces, si la pregunta era "si un servidor debería producir una ruta relativa en una respuesta tranquila", la respuesta es "No" y la razón detallada está disponible aquí . Creo que la mayoría de las personas (incluyéndome a mí) en contra de "URI relativo" están en realidad en contra de "ruta relativa".

Y en la práctica, la mayoría de los marcos MVC del lado del servidor pueden generar fácilmente un URI relativo con una ruta absoluta como /absolute/path/to/the/controller, y la pregunta es "si la implementación del servidor debe anteponer un prefijo a scheme://hostname:portla ruta absoluta". Como la pregunta del OP. No estoy muy seguro de este.

Por un lado, sigo pensando que se recomienda que el servidor devuelva un uri completo. Sin embargo, el servidor nuncahostname:port debería codificar lo que está dentro del código fuente como este (de lo contrario, preferiría recurrir a un uri relativo con una ruta absoluta). La solución es que el lado del servidor siempre obtenga ese prefijo del encabezado "Host" de la solicitud HTTP. Sin embargo, no estoy seguro de si esto funciona para todas las situaciones.

Por otro lado, no parece muy problemático para el cliente concatenar el http://example.com:8042y la ruta absoluta. Después de todo, el cliente ya conoce ese esquema y el nombre de dominio cuando envía la solicitud al servidor, ¿verdad?

Con todo, yo diría que recomiendo usar un URI absoluto, posiblemente un retroceso al URI relativo con una ruta absoluta, nunca use una ruta relativa .

RayLuo
fuente
2
Esta es una buena respuesta (+1) con la que estoy de acuerdo, excepto la conclusión final. Sin embargo, en mi respuesta sostengo que la especificación HTTP define, por ejemplo , "absoluto" para referirse a una ruta absoluta , no a un URI totalmente calificado. Por lo tanto, no estoy de acuerdo con su (2): es un URI absoluto, pero el cliente debe inferir el protocolo de red y el host, por lo que no es un URI completamente calificado. Y, por lo tanto, también estoy en desacuerdo con su definición de (1) que es tanto un URI completo como un URI absoluto.
Lawrence Dol
Gracias por el comentario. Solo tomo prestado el concepto de ruta absoluta y ruta relativa del sistema de archivos. Con términos diferentes, no veo una diferencia sustancial entre tu opinión y la mía. También recomienda la forma 1 y 2, y usted contra la forma 3, ¿no es así?
RayLuo
2
Prácticamente hablando, estoy a favor de (2); Creo que (1) requiere que el backend tenga mucho conocimiento específico de HTTP (es decir, sobre los detalles del entorno HTTP específico, no HTTP en general), y (3) parece requerir demasiado del cliente. Pero mi razonamiento se basó en el borrador de la especificación original, y los ejemplos se cambiaron en una versión posterior de una manera que invalida mi razonamiento.
Lawrence Dol
Personalmente, no estoy (todavía) del todo convencido de que HATEOAS y, por lo tanto, la demanda de devolver los URI tenga mucho sentido para una API. Simplemente no veo que mis API se dirijan al cliente de una manera similar a navegar por un sitio web; los casos de uso parecen estar muy motivados por funciones ad hoc.
Lawrence Dol
@LawrenceDol Tengo la misma confusión sobre HATEOAS al principio. Ahora lo considero una cuestión de elección. Sus clientes pueden usar la función adhoc para consumir su api con seguridad, pero si ellos / usted lo desea, aún pueden desarrollar un patrón para que lo sigan, de modo que el cliente no necesite codificar cada URL exacta. Eso es HATEOAS.
RayLuo
13

Depende de quién esté escribiendo el código del cliente. Si está escribiendo el cliente y el servidor, no hay mucha diferencia. O sufrirá el dolor de construir las URL en el cliente o en el servidor.

Sin embargo, si está construyendo el servidor y espera que otras personas escriban el código del cliente, entonces lo amarán mucho más si proporciona URI completos. Resolver URI relativos puede ser un poco complicado. En primer lugar, la forma de resolverlos depende del tipo de medio devuelto. Html tiene la etiqueta base, Xml puede tener xml: etiquetas base en cada elemento anidado, los feeds Atom pueden tener una base en el feed y una base diferente en el contenido. Si no le proporciona a su cliente información explícita sobre el URI base, entonces debe obtener el URI base del URI de la solicitud, ¡o tal vez del encabezado Content-Location! Y cuidado con la barra al final. El URI base se determina ignorando todos los caracteres a la derecha de la última barra. Esto significa que la barra inclinada final es ahora muy significativa al resolver URI relativos.

El único otro problema que requiere una pequeña mención es el tamaño del documento. Si devuelve una lista grande de elementos donde cada elemento puede tener varios enlaces, el uso de URL absolutas puede agregar una cantidad significativa de bytes a su entidad si no comprime la entidad. Este es un problema de rendimiento y debe decidir si es significativo caso por caso.

Darrel Miller
fuente
11

La única diferencia real parecería ser que es más fácil para los clientes consumir URI absolutos en lugar de tener que construirlos a partir de la versión relativa. Por supuesto, esa diferencia sería suficiente para influir en mí para hacer la versión absoluta.

Hank Gay
fuente
7

A medida que su aplicación se amplía, es posible que desee realizar un equilibrio de carga, una conmutación por error, etc. Si devuelve URI absolutos, las aplicaciones del lado del cliente seguirán su configuración de servidores en evolución.

CyberFonic
fuente
Siempre que defina "absoluta" como ruta absoluta (por ejemplo /xxx/yyy...) y no como un URI totalmente calificado (por ejemplo http://api.example.com/xxx/yyy...).
Lawrence Dol
6

Usando la tricotomía de RayLou mi organización ha optado por favorecer (2). La razón principal es evitar los ataques XSS (Cross-Site Scripting). El problema es que, si un atacante puede inyectar su propia raíz URL en la respuesta que proviene del servidor, las solicitudes de usuario posteriores (como una solicitud de autenticación con nombre de usuario y contraseña) se pueden reenviar al propio servidor del atacante *.

Algunos han planteado el problema de poder redirigir las solicitudes a otros servidores para el equilibrio de carga, pero (aunque esa no es mi área de especialización) apostaría a que hay mejores formas de habilitar el equilibrio de carga sin tener que redirigir explícitamente a los clientes a diferentes Hospedadores.

* Por favor, avíseme si hay algún defecto en esta línea de razonamiento. El objetivo, por supuesto, no es prevenir todos los ataques, sino al menos una vía de ataque.

Rahs
fuente
Me alegro de que mi respuesta anterior haya sido útil para su organización. Sí, personalmente también prefiero (2), también conocido como ruta absoluta sin esquema. Sin embargo, tengo curiosidad por tu razonamiento. ¿Cómo hizo que su cliente aceptara solo su URL sin esquema? Un cliente genérico, como un navegador, no rechazaría en absoluto una URL sin esquema. Entonces, ¿supongo que tendría que escribir su propio código del lado del cliente para validar las URL antes de seguirlas? Si bien eso es técnicamente factible (pero no necesariamente útil), este tipo de validación del lado del cliente generalmente no es parte de la discusión de REST o HATEOAS.
RayLuo
3
Sé que esta es una publicación antigua, pero solo quiero señalar que "si un atacante puede inyectar su propia raíz de URL en la respuesta" es una razón sin sentido. Si pueden "inyectar su propia URL" en los lugares correctos de la respuesta, apuesto a que podrían, con la misma facilidad, simplemente reemplazar su nombre de host por el suyo. Entonces, desde el punto de vista de la seguridad, no lo veo como un argumento válido.
Magnus Eriksson
5

Siempre debes usar la URL completa. Actúa como el identificador único para el recurso ya que todas las URL deben ser únicas.

También diría que debes ser coherente. Dado que el encabezado HTTP de ubicación espera una URL completa basada en la especificación HTTP, la URL completa se envía de vuelta en el encabezado de ubicación al cliente cuando se crea un nuevo recurso. Sería extraño que proporcionaras una URL completa en el encabezado de la ubicación y luego las URI relativas en los enlaces dentro del cuerpo de tu respuesta.

Mark Bober
fuente
1
Bueno, la especificación HTTP para el encabezado Location dice URI absoluto. Un URI absoluto debe contener un esquema (por ejemplo, http).
Mark Bober
Pero la pregunta no es cómo construir identificadores opacos sin contexto , sino cómo construir enlaces . Este último puede inferir correctamente "en la misma ubicación de red que este documento", y eso es exactamente lo que el ejemplo de especificación de unLocation encabezado de : un URI absoluto que no contiene el esquema de URI o la ubicación de red del servidor. Si bien los enlaces y los ID a menudo se combinan, no son lo mismo: el primero tiene contexto, el segundo no.
Lawrence Dol
¿Puede enviar un enlace a la parte de la especificación de la que está hablando?
Mark Bober
Un URI absoluto especifica un esquema; un URI que no es absoluto se dice que es relativo. Los URI también se clasifican según sean opacos o jerárquicos. Un URI opaco es un URI absoluto cuya parte específica del esquema no comienza con un carácter de barra ('/'). Los URI opacos no están sujetos a más análisis. Algunos ejemplos de URI opacos son: mailto: [email protected] news: comp.lang.java urn: isbn: 096139210x
Mark Bober
1
Oye no te preocupes hombre. Otro punto sobre este tema es que he visto personas que usan hrefs como ID. Para que el cliente no necesite reconstruir la URL a partir de algún archivo de configuración y una identificación, solo conoce la URL y puede almacenar en caché en función de ella.
Mark Bober
2

Una consideración importante en los resultados de API grandes es la sobrecarga de red adicional de incluir el URI completo repetidamente. Lo crea o no, gzip no resuelve por completo este problema (no estoy seguro de por qué). Nos sorprendió la cantidad de espacio que ocupaba el URI completo cuando había cientos de enlaces incluidos en un resultado.

George Sibble
fuente
2

Un inconveniente de usar URI absolutos es que la API no se puede usar como proxy.

Retirarlo ... no es cierto. Debería buscar una URL completa que incluya el dominio.

Jay Pete
fuente
3
¿Por qué el URI absoluto no puede usar el nombre de host del proxy?
Ed Summers
1
Trabajando en este problema exacto en este momento. Queremos que todas las solicitudes pasen primero por una especie de capa de "equilibrio de carga". Los URI absolutos a los servidores directamente romperán este modelo.
mag382
1
Estoy usando Nginx para proxy de un sitio con URL absolutas. Es perfectamente capaz de reemplazar la URL de backend con la URL de proxy equivalente. Específicamente, está enviando un proxy a windyroad.artifactoryonline.com (que tiene URL completamente calificadas y redireccionamientos completamente calificados) a repo.windyroad.com.au
Tom Howard
2

En cuanto a los pros, veo la reducción de bytes a transmitir a expensas del manejo adicional requerido por un cliente para la ruta (absoluta). Si está desesperado por guardar cada byte, incluso después de probar la codificación de contenido como gzip, el uso adecuado de los encabezados de almacenamiento en caché, el uso de etags y solicitudes condicionales en el cliente, entonces esto puede ser necesario al final, pero espero rendimientos mucho más altos en sus otros esfuerzos fueron.

Respecto a los contras, veo una pérdida de control sobre cómo se puede dirigir el flujo de clientes entre recursos en el futuro (balanceo de carga, pruebas A / B, ...), y lo consideraría una mala práctica en cuanto a la gestión de una web. API. La URL que proporciona ya no es básicamente opaca para el cliente (consulte Tim Berners-Lee Axioms of Web Architecture sobre opacidad de URI ). Al final, usted se vuelve responsable de mantener contentos a los clientes con respecto al uso creativo de su API, incluso si solo se trata de la estructura de su espacio de URL. Si alguna vez necesita permitir una modificación de URL definida explícitamente, considere el uso de plantillas de URI como se usa en el lenguaje de aplicación de hipertexto .

Michael Hartle
fuente