
Hace tiempo que quería escribir este artículo. Me preguntaba de qué lado entrar más correctamente. Pero, de repente, recientemente, apareció un artículo similar sobre Habré, que causó una tormenta en un vaso. Lo que más me sorprendió fue el hecho de que el artículo comenzó a manejarse en menos, aunque ni siquiera declaró algo, sino que planteó la cuestión del uso de códigos de respuesta del servidor web en REST. El debate estalló en caliente. Y la apoteosis fue que el artículo entró en borrador ... kilobytes de comentarios, opiniones, etc. Solo desapareció. Muchos se convirtieron en víctimas del karmo, considere, para nada :)
En general, fue el destino de ese artículo lo que me impulsó a escribir este. Y realmente espero que sea útil y aclare mucho.
Te advierto que todo lo escrito a continuación es una experiencia real, no un acto de equilibrio cognitivo. Y así, condujeron.
HTTP
Lo primero que debe hacer es separar muy claramente las capas. La capa de transporte es http. Bueno, en realidad DESCANSO. Esto es algo fundamentalmente importante para aceptar todo y "usted mismo" en él. Hablemos solo sobre http primero.
Usé el término "capa de transporte". Y no hice una reserva. La cuestión es que http implementa las funciones de transporte de solicitudes al servidor y contenido al cliente, independientemente de tcp / ip. Sí, se basa en tcp / ip. Y parece que es necesario considerarlo como su transporte. Pero no Y esta es la razón: las conexiones de socket no son directas, es decir Esta no es una conexión cliente-servidor. Tanto la solicitud http como la respuesta http pueden recorrer un largo camino a través de una tonelada de servicios. Se pueden agregar o descomponer por el contrario. Puede ser almacenado en caché, puede ser modificado.
Es decir tanto la solicitud http como la respuesta http tienen su propia ruta. Y no depende del extremo posterior, ni del extremo frontal. Le pido que preste especial atención a esto.
Las rutas http no son estáticas. Pueden ser muy complicados. Por ejemplo, si se integra un equilibrador en la infraestructura, puede enviar solicitudes recibidas a cualquiera de los nodos posteriores. Al mismo tiempo, el propio backend puede implementar su propia estrategia para trabajar con solicitudes. Algunos de ellos irán directamente a microservicios, otros serán procesados por el propio servidor web, otros serán complementados y transferidos a otra persona, y otros serán emitidos desde el caché, etc. Así es como funciona Internet. Nada nuevo
Y aquí es importante entender: ¿por qué necesitamos códigos de respuesta? La cuestión es que todo el modelo descrito anteriormente toma decisiones basadas en ellos. Es decir Estos son códigos que le permiten tomar decisiones de infraestructura y transporte durante el enrutamiento http.
Por ejemplo, si el equilibrador encuentra un código de respuesta del respaldo 503, al enviar una solicitud, puede tomar esto como base para considerar que el nodo no está disponible temporalmente. Observo que la respuesta con el código 503 proporciona un encabezado Retry-After. Después de recibir del encabezado el intervalo para sondeos repetidos, el equilibrador dejará el nodo solo durante el período especificado y trabajará con los disponibles. Además, tales estrategias se implementan "fuera de la caja" por los servidores web.
Un pequeño tema para una comprensión profunda: ¿qué pasa si el nodo responde 500? ¿Qué debe hacer el equilibrador? ¿Cambiar a otro? Y muchos responderán, por supuesto, todos los motivos 5xx para deshabilitar un nodo. Y se equivocarán. El código 500 es un código de error inesperado. Es decir uno que nunca vuelva a suceder. Y lo más importante, cambiar a otro nodo puede no cambiar nada. Es decir simplemente deshabilitamos los nodos sin el menor beneficio.
En el caso de 500, las estadísticas nos ayudan. El servidor WEB local del nodo puede traducir el nodo mismo al estado de indisponibilidad con un gran número de 500 respuestas. En este caso, el equilibrador que contacte a este nodo recibirá una respuesta 503 y no lo tocará. El resultado es el mismo, pero ahora, esta solución es significativa y elimina las respuestas "falsas".
Pero eso no es todo. En esta situación, el monitoreo permitirá a los administradores conectarse a la situación para mantener el nodo. Es decir no solo obtenemos la implementación de un servicio altamente accesible, con balanceadores, etc., sino también un proceso de soporte efectivo.
Y todo esto le permite crear códigos de respuesta del servidor. Cualquier arquitectura de aplicación WEB debe comenzar con el diseño de la capa de transporte. Espero que no haya dudas al respecto.
DESCANSO
Haré una pregunta retórica: ¿qué es? ¿Y qué le respondiste? No daré enlaces a pruebas obvias, pero lo más probable es que no sea lo que es en realidad :) Esto es solo una ideología, un estilo. Algunas consideraciones sobre el tema: la mejor manera de comunicarse con la parte posterior. Y no solo comunicarse, sino comunicarse en la infraestructura WEB. Es decir basado en http. Con todas esas "cosas útiles" sobre las que escribí anteriormente. Las decisiones finales para implementar su interfaz son siempre
suyas .
¿Alguna vez te has preguntado por qué no se inventó un transporte separado para REST? Por ejemplo, para websocket lo es. Sí, también comienza con http, pero luego, una vez establecida la conexión, generalmente es una canción separada. ¿Por qué no hacer lo mismo para REST?
La respuesta es simple: ¿por qué? Existe un protocolo hermoso, listo y verificado: http. Se escala bien. Le permite implementar servicios complejos y altamente accesibles que pueden hacer frente a una carga pesada. Todo lo que se necesita es introducir algunas reglas conceptuales para que los desarrolladores se entiendan entre sí.
De aquí se desprende una conclusión simple y obvia: todo lo que es inherente a http es inherente a REST. Estas son entidades inseparables. No hay un encabezado REST separado, ni siquiera una pista de que REST es REST. Para cualquier servidor REST, la solicitud es exactamente la misma que cualquier otra. Es decir REST es justo lo que tenemos en mente.
REST códigos de respuesta http
¿Hablemos sobre qué código debe responder su servidor a una solicitud REST? Personalmente, me parece que de todo lo anterior, la respuesta ya es obvia, porque REST no es diferente de cualquier otra solicitud, debe estar sujeto exactamente a las mismas reglas. El código de respuesta es una parte integral de REST y debe ser relevante para la esencia de la respuesta. Es decir si el objeto no fue encontrado por solicitud, es 404, si el cliente realizó una solicitud incorrecta 400, etc. Pero, con mayor frecuencia, el debate no termina ahí. Por lo tanto, continuaré.
¿Es posible responder todo con el código 200? ¿Y quién te lo prohibirá? Por favor ... el código 200 es el mismo código que los demás. Es cierto que la base de este enfoque es una tesis muy simple: mi sistema es perfecto, no tiene errores. Si eres una persona que puede crear tales sistemas, ¡esto solo puede ser envidiado!
Pero lo más probable ... no es perfecta. Y los errores suceden. Y sucede que suceden debido a circunstancias más allá de nuestro control. Y aquí una solución típica es crear su propio sistema de codificación de errores. ¿Eso es malo? Si, eso es malo. Esto es súper malo Averigüemos por qué.
Y así, tomando el código 200 como el único verdadero, nos hacemos responsables del desarrollo de toda la capa (capa crítica) del sistema: manejo de errores. Es decir El trabajo de muchas personas para desarrollar esta capa se envía a la chatarra. Y comienza la construcción de su "bicicleta". Pero este mega edificio está condenado al fracaso.
Comencemos con el código. Si vamos a responder las 200, nosotros mismos tendremos que manejar los errores. El método clásico es tratar de construir. Cada segmento de código lo envolvemos con código adicional. Manejadores que hacen algo útil. Por ejemplo, ponen algo en el registro. Algo importante Eso localizará el error. ¿Y si el error surgió no donde se esperaba? ¿O si ocurrió un error en el controlador de errores? Es decir Esta estrategia a nivel de código no funciona a priori. Y al final, el intérprete o la plataforma procesará sus errores. SO finalmente. La esencia del error es que no lo estás esperando. No necesita ocultarlo, necesita encontrarlo y arreglarlo. Por lo tanto, si REST responde a algunas solicitudes con un error de 500, esto es
normal . Y lo que es más,
correcto .
Volvamos a la pregunta: ¿por qué es esto correcto? Porque:
- Code 500 es un token de infraestructura en función del cual se puede deshabilitar el nodo en el que se produce el problema;
- Los códigos 5xx son lo que se está monitoreando y si surge dicho código, cualquier sistema de monitoreo lo notificará de inmediato. Y el servicio de soporte a tiempo podrá conectarse a la solución del problema;
- No escribes código adicional. No pierdas un tiempo precioso en esto. No compliques la arquitectura. No trata problemas inusuales para usted: escribe el código de la aplicación. Lo que quieren de ti. ¿Qué están pagando?
- Un rastro que se cae por error 500 será mucho más útil que sus intentos de superarlo.
- Si la solicitud REST devuelve el código 500, el frente que ya está en el momento de procesar la respuesta sabrá qué algoritmo debe procesar. Además, la esencia del asunto no cambiará de ninguna manera, no ha recibido nada sensato ni de 200 ni de 500. Pero de 500 recibió un beneficio: la constatación de que se trata de un error INESPERADO.
- El código 500 vendrá con una garantía. No importa cuán malo o bueno haya escrito su código. Este es tu punto de apoyo.
Por separado, clavaré un clavo en todo el "cuerpo" del código 200:
7. Incluso si se esfuerza por evitar otros códigos de respuesta del servidor que no sean 200 a sus solicitudes, no puede hacer esto. Cualquier servidor intermediario puede responder a su solicitud con absolutamente cualquier código. Y DEBE procesar dicha respuesta correctamente.
Total, a nivel lógico, la lucha por el código 200 no tiene sentido.
Ahora volvamos al nivel de infraestructura. Muy a menudo escucho la opinión: el código 5xx no es un nivel de aplicación, no se le puede dar respaldo. Ejem, bueno ... hay una contradicción en la declaración misma. Puedes dar. Pero este código no es un nivel de aplicación. Eso es mas cierto. Para entender esto, propongo considerar el caso:
Estás implementando una puerta de enlace. Tiene varios DC, cada uno con su propio canal de comunicación a un determinado servicio privado. Bueno, por ejemplo, para pagar a través de VPN. Y hay un canal de comunicación con Internet. Recibe una solicitud para una operación con una puerta de enlace, pero ... el servicio no está disponible.
¿Y qué deberías responder? A quien? Este es un problema de infraestructura y, específicamente, la copia de seguridad se topó con él. Por supuesto, debe responder con audacia al 503. Estas acciones llevarán al hecho de que el equilibrador deshabilitará el nodo durante algún tiempo. Al mismo tiempo, el equilibrador, si está configurado correctamente, sin interrumpir la conexión con el cliente, enviará la solicitud a otro nodo. Y ... el cliente final, con un alto grado de probabilidad, recibió 200. Y no una descripción personalizada del error, que no lo ayudará de ninguna manera.
Dónde y qué código usar
La pregunta no es simple. No hay una respuesta definitiva para ello. Para cada sistema, se diseña una capa de transporte y los códigos que contiene pueden ser específicos.
Hay estándares aceptados. Se pueden encontrar fácilmente y, nuevamente, no daré pruebas obvias. Pero, le daré lo que no es obvio:
developer.mozilla.org/en/docs/Web/HTTP/StatusPor que el La cuestión es que los manejadores de código pueden comportarse de manera diferente, dependiendo de la implementación y el contexto de "comprender el código". Por ejemplo, los navegadores tienen una estrategia de almacenamiento en caché basada en códigos de respuesta. Y algunos servicios tienen sus propios códigos personalizados. Por ejemplo, CloudFlare.
Es decir Para tomar decisiones sobre el uso de códigos, debe basarse en todos los elementos incluidos en la capa de transporte desde su código en la parte posterior hasta el código en el cliente. Esta es la única forma de encontrar las respuestas correctas. Ni siquiera intentaré darles a todos una píldora universal aquí.
Raíces del mal
Este es el tercer proyecto al que llego sufre del código 200 en REST. Es sufrimiento No hay otra palabra Si lees cuidadosamente todo hasta el momento actual, ya entiendes que tan pronto como el proyecto comience a crecer, tiene una necesidad de desarrollo de infraestructura, para su sostenibilidad. El código 200 mata todos estos intentos de raíz. Y lo primero que tienes que hacer es romper los estereotipos.
La raíz del mal, me parece, radica en el hecho de que el código 500 es lo primero que un desarrollador web encuentra en su carrera profesional. Se puede decir una lesión infantil. Y todos sus esfuerzos al principio se redujeron a obtener el código 200.
Por cierto, por alguna razón, en la misma etapa, se desarrolla una fuerte opinión de que solo las respuestas con el código 200 se pueden suministrar con un cuerpo. Por supuesto, esto no es así, y cualquier respuesta puede "venir" con cualquier código. El código es un código. El cuerpo es el cuerpo.
Además, con el desarrollo del desarrollador, necesita gestionar los errores de su propia aplicación. Pero ..., él no sabe cómo usar los registros. No se puede configurar el servidor web. El esta estudiando. Y esos muy "grandes" nacen. Porque están disponibles para él y él puede hacerlos rápidamente. Además, en este "gran" monta nuevas ruedas, fortalece el cuadro, etc. Y este gran se convierte en su compañero durante un período de tiempo suficientemente largo, hasta ... hasta que tenga tareas realmente complejas y de múltiples componentes. Y aquí, como dicen, está prohibida la entrada al supermercado con el "gran" y el patinaje sobre ruedas.
PD: El autor del artículo mencionado lo restauró de los borradores:
habr.com/en/post/440382 , para que también pueda familiarizarse con él.
PPS: Traté de expresar todas las facetas de la necesidad de usar códigos de respuesta relevantes en REST. No responderé a los comentarios, por favor, entiéndeme correctamente. Los leeré con gran atención, pero no tengo nada que agregar. ¡Muchas gracias por ser lo suficientemente paciente como para leer el artículo!