2009-08-18 7 views
45

Tengo dificultades para aplicar los principios RESTful a una nueva aplicación web en la que estoy trabajando. En particular, es la idea de que para ser RESTful, cada solicitud HTTP debe llevar suficiente información por sí misma para que su destinatario la procese para que esté en completa armonía con la naturaleza sin estado de HTTP.REST: aplicaciones complejas

La aplicación permite a los usuarios buscar medicamentos. La búsqueda acepta filtros como entrada, por ejemplo, devolver medicamentos descontinuados, incluir terapia complementaria, etc.etc. En total, hay alrededor de 30 filtros que se pueden aplicar.

Además, los datos del paciente se pueden introducir incluyendo la edad del paciente, sexo, etc. medicamentos actuales

Para ser reparador, toda esta información deben ser incluidos con cada petición? Esto parece colocar una enorme sobrecarga en la red. Además, ¿no serían las restricciones en la longitud de la URL, al menos para GET, inviables?

Respuesta

73

El "filtro como recurso" es un tacto perfecto para esto.

Puede PONER la definición de filtro en el recurso de filtro y puede devolver la ID del filtro.

PUT es idempotente, por lo que incluso si el filtro ya está allí, solo necesita detectar que ha visto el filtro antes, para que pueda devolver la identificación adecuada para el filtro.

Luego, puede agregar un parámetro de filtro a sus otras solicitudes, y pueden tomar el filtro para usar para las consultas.

GET/medicamentos? Filter = 1,234 & page = 4 & tamaño de página = 20

correría los filtros primas a través de algún tipo de proceso de canonización, sólo para tener un conjunto normalizado, de modo que, por ejemplo, filter "firstname = Bob lastname = Eubanks" es idéntico a "lastname = Eubanks firstname = Bob". Aunque soy solo yo.

La única preocupación real es que, a medida que pasa el tiempo, es posible que tenga que obsolestar algunos filtros. Simplemente puede equivocar la solicitud en caso de que alguien haga una solicitud con un filtro faltante u obsoleto.

Editar contestador pregunta ...

Vamos a empezar con los fundamentos.

Simplemente, quiere especificar un filtro para usar en consultas, pero estos filtros son (potencialmente) complicados. Si fue simple/medicamentos/1234, esto no sería un problema.

Efectivamente, siempre debe enviar el filtro a la consulta. La pregunta es cómo representar ese filtro.

El problema fundamental con cosas como las sesiones en los sistemas REST es que normalmente se gestionan "fuera de banda". Cuando, por ejemplo, ve y creas un medicamento, PONES o PUBLICAS en el recurso de medicamentos, y obtienes una referencia de ese medicamento.

Con una sesión, normalmente recuperaría una cookie, o tal vez algún otro token para representar esa sesión. Si su PUT al recurso de medicamentos creó también una sesión, entonces, en realidad, su solicitud creó dos recursos: un medicamento y una sesión.

Desafortunadamente, cuando utiliza algo así como una cookie, y necesita esa cookie para su solicitud, el nombre del recurso ya no es la verdadera representación del recurso. Ahora es el nombre del recurso (la URL) y la cookie.

Entonces, si hago un GET en el recurso llamado/medicamentos/búsqueda, y la cookie representa una sesión, y esa sesión tiene un filtro, se puede ver cómo, en efecto, ese nombre de recurso,/medicamentos/búsqueda, no es realmente útil en absoluto. No tengo toda la información que necesito para hacer un uso efectivo, debido al efecto secundario de la cookie y la sesión y el filtro que contiene.

Ahora, quizás pueda volver a escribir el nombre:/medicines/search? Session = ABC123, incrustando efectivamente la cookie en el nombre del recurso.

Pero ahora te encuentras con el típico contrato de sesiones, especialmente que son de corta duración. Entonces, ese recurso nombrado es menos útil, a largo plazo, no inútil, solo menos útil. En este momento, esta consulta me da datos interesantes. ¿Mañana? Probablemente no. Recibiré un desagradable error acerca de que la sesión se fue.

El otro problema es que las sesiones generalmente no se administran como un recurso.Por ejemplo, generalmente son un efecto secundario, vs administrado explícitamente a través de GET/PUT/DELETE. Las sesiones también son el "montón de basura" del estado de la aplicación web. En este caso, esperamos que la sesión esté debidamente llena con lo que se necesita para esta solicitud. Realmente no sabemos realmente. Nuevamente, es un efecto secundario.

Ahora, volvamos un poco la cabeza. Usemos/medicamentos/search? Filter = ABC123.

Obviamente, casualmente, esto se ve idéntico. Acabamos de cambiar el nombre de 'sesión' a 'filtro'. Pero, como se discutió, los filtros, en este caso, SON un "recurso de primera clase". Deben crearse, administrarse, etc. de la misma manera que un medicamento, un archivo JPEG o cualquier otro recurso en su sistema. Esta es la distinción clave.

Ciertamente, podría tratar las "sesiones" como un recurso de primera clase, crearlas, poner cosas en ellas directamente, etc. Pero puede ver cómo, al menos desde el punto de vista de la claridad, una sesión de "primera clase" no es realmente una buena abstracción para este caso. Usar una sesión es como ir a la tintorería y entregar todo tu bolso o maletín. "Sí, el boleto está allí en alguna parte, excavar lo que quieras, darme mi ropa", especialmente comparado con algo explícito como un filtro.

Por lo tanto, puede ver cómo, a 30,000 pies, no hay mucha diferencia en el caso entre un filtro y una sesión. Pero cuando haces zoom, son bastante diferentes.

Con el recurso de filtro, puede optar por convertirlos en algo persistente para siempre. Puedes expirarlos, puedes hacer lo que quieras. Las sesiones tienden a tener semántica preconcebida: breve duración, duración de la conexión, etc. Los filtros pueden tener cualquier semántica que desee. Están completamente separados de lo que viene con una sesión.

Si estuviera haciendo esto, ¿cómo trabajaría con los filtros?

Supongo que realmente no me importa el contenido de un filtro. Específicamente, dudo que alguna vez busque "todos los filtros que buscan por nombre". En esta coyuntura, parece información sin interés, así que no diseñaré a su alrededor.

A continuación, normalizaría los filtros, como mencioné anteriormente. Asegúrese de que los filtros equivalentes sean realmente equivalentes. Puedes hacer esto ordenando las expresiones, asegurando que los nombres de los campos estén en mayúsculas, o lo que sea.

Luego, almaceno el filtro como un documento XML o JSON, lo que sea más cómodo/apropiado para la aplicación. Le daría a cada filtro una clave única (naturalmente), pero también almacenaría un hash para el documento real con el filtro.

Haría esto para poder encontrar rápidamente si el filtro ya está almacenado. Como lo estoy normalizando, "sé" que el XML (por ejemplo) para los filtros lógicamente equivalentes sería idéntico. Entonces, cuando alguien va a PUT, o inserta un nuevo filtro, yo verificaría el hash para ver si se había almacenado antes. Es posible que recupere más de uno (los hash pueden colisionar, por supuesto), así que tendré que comprobar las cargas útiles XML reales para ver si coinciden.

Si los filtros coinciden, devuelvo una referencia al filtro existente. Si no, crearía uno nuevo y lo devolvería.

Yo tampoco permitiría un filtro ACTUALIZAR/PUBLICAR. Como distribuyo referencias a estos filtros, los haría inmutables para que las referencias puedan seguir siendo válidas. Si quisiera un filtro por "rol", por ejemplo, el filtro "obtener todos los medicamentos vencidos", crearía un recurso de "filtro nombrado" que asocia un nombre con una instancia de filtro, para que los datos de filtro reales puedan cambiar, pero la el nombre sigue siendo el mismo

Tenga en cuenta, también, que durante la creación, está en una condición de carrera (dos solicitudes tratando de hacer el mismo filtro), por lo que tendría que dar cuenta de eso. Si su sistema tiene un alto volumen de filtro, esto podría ser un cuello de botella potencial.

Espero que esto aclare el problema para usted.

+4

Esta es una de las mejores explicaciones que he visto por qué las sesiones no encajan bien en una arquitectura de estilo REST. –

+0

+1: ¡Excelente respuesta, Will! –

+0

buena explicación @ Will – jayraynet

10

REST es para API, no aplicaciones (típicas). No trates de encajar una interacción fundamentalmente con estado en un modelo sin estado solo porque lo lees en wikipedia.

Para estar tranquilo, ¿debería incluirse toda esta información en cada solicitud? Esto parece colocar una enorme sobrecarga en la red. Además, ¿no serían las restricciones en la longitud de la URL, al menos para GET, inviables?

El tamaño de los parámetros es generalmente insignificante en comparación con el tamaño de los recursos que envía el servidor. Si está usando parámetros tan grandes que son una carga de red, colóquelos en el servidor una vez y luego úselos como recursos.

No hay restricciones significativas en la longitud de la URL; si su servidor tiene dicho límite, actualícelo. Probablemente tenga años y esté lleno de vulnerabilidades de seguridad de todos modos.

+2

¿Podría proporcionar una referencia para su afirmación de que REST es para API? Según tengo entendido, REST se sintetizó a partir de la arquitectura de las aplicaciones web, que considero que debe enfrentar el usuario. Además, ¿podría explicar por qué una aplicación de búsqueda es una "interacción fundamentalmente con estado"? –

+1

REST es una alternativa a RPC para API; es una descripción de la arquitectura de la web. Las aplicaciones web rara vez se parecen a la Web en su arquitectura, y no son adecuadas para una interacción REST. Con respecto a "fundamentalmente con estado": a partir de la pregunta, parece que los filtros se acumulan en un estado y luego se aplican a un conjunto. Para mí, la mejor manera que podría representarse en un sistema sin estado es creando un recurso "conjunto" y aplicando filtros a él, muy diferente de la implementación del PO. –

+3

@John Si realmente está interesado en REST, entonces le sugiero que vaya y lea la disertación de Roy nuevamente. Creo que has entendido mal. –

0

¿Está trabajando en una API RESTful que otras aplicaciones utilizarán para buscar sus datos? ¿O está construyendo una aplicación web enfocada en el usuario final donde los usuarios iniciarán sesión y realizarán estas búsquedas?

Si sus usuarios inician sesión, entonces ya está lleno de estados ya que tendrá algún tipo de cookie de sesión para mantener el estado de inicio de sesión. Me gustaría continuar y crear un objeto de sesión que contenga todos los filtros de búsqueda. Si un usuario no ha configurado ningún filtro, este objeto estará vacío.

Aquí hay una gran publicación de blog sobre el uso de GET vs POST. Menciona un límite de longitud de URL establecido por Internet Explorer de 2.048 caracteres, por lo que desea utilizar POST para solicitudes largas.

http://carsonified.com/blog/dev/the-definitive-guide-to-get-vs-post/

5

Sin todo eso no tiene que estar en cada petición.

Cada recurso (medicamento, historia del paciente, etc.) debe tener un URI canónico que lo identifique de manera única. En algunas aplicaciones (p. Ej., Basadas en Rails) esto será algo así como "/ patients/1234" o "/ drugs/5678" pero el formato de URL no es importante.

Un cliente que ha obtenido previamente el URI para un recurso (por ejemplo, desde una búsqueda o desde un enlace incrustado en otro recurso) puede recuperarlo utilizando este URI.

+0

Eso tiene sentido. Entonces, cuando los objetos se pueden generar dinámicamente (un usuario puede crear cualquier cantidad de filtros), parece que a cada uno se le debe asignar un URI único. Este URI se devolverá a la aplicación del cliente. ¿Es eso correcto? – Alistair77

+0

@ alistair77: Correcto. Cuando el usuario crea un filtro, se debe devolver una representación del mismo junto con un encabezado 'Ubicación' para indicar dónde se puede encontrar ese filtro para las operaciones posteriores. –

12

Para estar tranquilo, ¿debería incluirse toda esta información en cada solicitud?

No. Si parece que su servidor está enviando (o recibiendo) demasiada información, es probable que haya uno o más recursos que aún no haya identificado.

El primer y más importante paso en el diseño de un sistema RESTful es identificar y nombrar sus recursos. ¿Cómo harías eso para tu sistema?

Desde su descripción, aquí está uno cantidad posible de recursos:?

  • usuario - un usuario del sistema (tal vez un médico o paciente() - papel podría necesitar ser expuesto como un recurso aquí)
  • Medicación - el material en la botella, pero también podría representar el tipo de botella (cantidad y contenido), o podría representar una botella en particular, dependiendo de si usted es una farmacia o simplemente una mesa de ayuda.
  • de Enfermedades - la condición por la cual un paciente posible que desee tomar una medicación .
  • Paciente - una persona que podría tomar un medicamento
  • Recomendación - un medicación que podría ser beneficioso para un paciente basado en un Enfermedades que padecen.

Luego puede buscar relaciones entre los recursos;

  • usuario tiene y pertenece a muchos roles
  • medicación tiene y pertenece a muchas enfermedades
  • Enfermedades tiene muchas recomendaciones .
  • Paciente tiene y pertenece a muchos Medicamentos y Enfermedades (pobre hombre)
  • Paciente tiene muchas Recomendaciones
  • Recomendación tiene uno Paciente y tiene una Enfermedades

Los detalles probablemente no sean adecuados para su problema en particular, pero la idea es simple: cree una red de relaciones entre sus recursos.

En este punto, puede ser útil para pensar acerca de la estructura URI, a pesar de tener en cuenta que REST APIs must be hypertext-driven:

# view all Recommendations for the patient 
GET http://server.com/patients/{patient}/recommendations 

# view all Recommendations for a Medication 
GET http://servier.com/medications/{medication}/recommendations 

# add a new Recommendation for a Patient 
PUT http://server.com/patients/{patient}/recommendations 

Debido a que este es el descanso, pasará la mayor parte de su tiempo defining the media types utilizado para transferir representaciones de sus recursos entre el cliente y el servidor.

Al exponer más recursos, puede reducir la cantidad de datos que se deben transferir durante cada solicitud. También observe que no hay parámetros de consulta en los URI. El servidor puede ser tan completo como debe ser para realizar un seguimiento de todo, y cada solicitud puede ser totalmente independiente.

Cuestiones relacionadas