session - what - RESTO: aplicaciones complejas
stateful vs stateless (5)
Me cuesta 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.
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, se pueden ingresar los detalles del paciente, incluyendo la edad del paciente, el sexo, los medicamentos actuales, etc.
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?
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?
Según su descripción, aquí hay un conjunto de recursos posible:
- Usuario : un usuario del sistema (tal vez un médico o un paciente (?): El rol podría necesitar ser expuesto como un recurso aquí)
- Medicamentos : el contenido del frasco, pero también podría representar el tipo de frasco (cantidad y contenido), o podría representar un frasco en particular, dependiendo de si usted es una farmacia o simplemente un servicio de ayuda.
- Enfermedad : la afección por la cual un paciente podría querer tomar un medicamento .
- Paciente : una persona que podría tomar un medicamento
- Recomendación : un medicamento que podría ser beneficioso para un paciente en función de una enfermedad que padecen.
Entonces podrías buscar relaciones entre los recursos;
- El usuario tiene y pertenece a muchos roles
- La medicación tiene y pertenece a muchas enfermedades
- La enfermedad tiene muchas recomendaciones .
- El paciente tiene y pertenece a muchos medicamentos y enfermedades (pobre amigo)
- El paciente tiene muchas recomendaciones
- La recomendación tiene un paciente y tiene una enfermedad
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 pensar en la estructura URI, aunque tenga en cuenta que las API REST deben estar basadas en hipertexto :
# 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
Como se trata de REST, pasará la mayor parte de su tiempo definiendo los tipos de medios utilizados 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.
¿Estás trabajando en una API RESTful que otras aplicaciones utilizarán para buscar tus 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 están iniciando sesión, entonces ya tiene estado, 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/
El "filtro como recurso" es un tacto perfecto para esto.
Puede PONER la definición del 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.
OBTENER / medicamentos? Filter = 1234 & page = 4 & pagesize = 20
Me gustaría ejecutar los filtros sin procesar a través de algún tipo de proceso de canonicalización, solo para tener un conjunto normalizado, por lo que, por ejemplo, el filtro "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 deba obsoletos algunos filtros. Simplemente puede equivocar la solicitud en caso de que alguien haga una solicitud con un filtro faltante u obsoleto.
Editar respuesta pregunta ...
Comencemos con los fundamentos.
Simplemente, desea especificar un filtro para su uso en consultas, pero estos filtros son (potencialmente) complicados. Si fue simple / medicamentos / 1234, esto no sería un problema.
De hecho, 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 usa algo así como una cookie y requiere 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 realizo 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 podría volver a escribir el nombre: / medicines / search? Session = ABC123, integrando 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, vamos a ponerlo de cabeza un poco. 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.
Entonces, 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.
Luego, 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, almacenaría 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.
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 que intentan 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 ti.
No todo eso no tiene que ser en cada pedido.
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 (como una búsqueda o un enlace incrustado en otro recurso) puede recuperarlo utilizando este URI.
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 suele ser 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.