Paginación, clasificación y complementos personalizados en Ktor | de Hitesh Chopra | octubre 2022

Hoy, en este artículo, vamos a hablar sobre cómo implementar la paginación, la clasificación y los complementos personalizados con Ktor. Esta es una extensión de mi artículo anterior sobre el desarrollo de API REST con Ktor. Lea el artículo antes de leerlo para tener una buena base sobre qué es Ktor y cómo desarrollar API REST básicas con Ktor. Así que comencemos sin mucha demora.Aunque la mayoría ya lo sabéis, para mis amigos que no sepan qué es la paginación, os lo explicaré rápidamente con la ayuda de un ejemplo. Si está navegando por una aplicación con una lista de elementos como Swiggy, Netflix, Disney Hotstar o una aplicación que muestra los resultados de la búsqueda como una lista de elementos, es posible que haya notado que inicialmente solo se ven 7 u 8 elementos de la lista. y si se desplaza más hacia la derecha (si la lista es horizontal), aparecerá una marca de carga o un efecto de brillo antes de que se carguen los siguientes 7-8 elementos. Esto se llama paginación. Técnicamente, con una API paginada, especificamos el número de página y la cantidad de elementos para cargar en la lista y, en consecuencia, solo se devuelven muchos elementos de la API. Esto es muy útil cuando nuestra lista es muy grande. Hay dos tipos de paginación, paginación desplazada y paginación de conjunto de claves. Usé paginación compensada en este ejemplo. Hay dos aspectos principales para compensar la paginación: limit (el número de resultados) y offset (el número de registros a omitir). Comencemos con el código rápidamente. Como se mencionó en el artículo anterior, usamos Ktorm ORM en nuestro proyecto.divertida fetchPaginatedNotes(página: Int, tamaño: Int): Lista {valor Límite: int = tamañovalor Tamaño de página: int = tamañovalor saltar: int = (página – 1) * tamaño de páginaatrás base de datos.from(NotesEntity).select().limit(offset = skip, limit = limit).map {valor identificación = eso[NotesEntity.id]valor nota = eso[NotesEntity.note]Nota (ID ?: -1, Nota ?: «»)}} Como puede ver, la función que devuelve la lista paginada de elementos acepta los parámetros de página y tamaño como parámetros de consulta.lado -: Página es el número de página desde el que se deben mostrar los resultados. Supongamos que la lista tiene un total de 100 elementos y el tamaño de cada lista paginada devuelta es 10. Quiero que comience a buscar desde el elemento 91 de la lista. Mi número de página es 10.Talla -:size es el número de elementos que desea que devuelva la lista. Vamos, quiero ver la lista de artículos del 91 al 100, indicaré el tamaño como 10. La URL final se ve así:https://www.url.com/endpoint?page=9&size=10Ahora la pregunta que puede haber pasado por tu mente es cuál es la lógica detrás de esto y cuál es saltar en la función escrita arriba? De hecho, la paginación basada en desplazamiento no es más que una consulta SQL modificada. Sí, el límite y la compensación son conceptos de SQL. Debajo del capó, la consulta para esto sería algo como -:ELEGIR * AFUERA Observaciones BORDE 10 COMPENSAR 10;el desplazamiento es el Número de registros que se deben omitir, solo aceptamos el tamaño de página y el número de página en los parámetros de consulta porque tiene más sentido para el cliente. Podemos calcular fácilmente OFFSET a partir de pageSize y pageNumbercompensación = (pageNumber — 1) * pageSizeTomando el mismo ejemplo, quiero recuperar la lista del elemento 91 al 100 de la lista, asumiendo que el tamaño de página es 10 y el número de página es 9, como se discutió anteriormente, el desplazamiento serácompensación = (10 — 1) * 10 = 90 Esto significa que se omitirán un total de 90 elementos y se mostrarán los elementos a partir del elemento 91. El límite es 10, por lo que solo se mostrarán 10 elementos. Así es como se puede manejar la paginación desplazada en Ktor 🙂 Todos saben qué es la clasificación, sin entrar en detalles, veamos cómo se puede manejar elegantemente la clasificación ascendente y descendente en Ktor. Vamos a ver /Observaciones punto final en nuestras notas Ruter.recibir(«/Observaciones») {Si (valor sortType = llamada.consulta.parámetros de consulta[«sort»]) {// Ordenar de forma ascendente o descendenteASCENDING, DESCENDING -> {call.respond(HttpStatusCode.OK,service.fetchSortedNotes(isDescending = sortType == DESCENDING))}// No se solicitó clasificacióncero -> {llamar.responder(HttpStatusCode.OKservice.fetchAllNotes())}// Tipo de clasificación no válidodiferente -> {llamar.responder(HttpStatusCode.Inaceptable,NotaRespuesta(Éxito = INCORRECTO, Datos = «Ingrese un tipo de clasificación válido (ascendente o descendente)»))}}}objeto constantes {valor constante ASCENDENTE = «asc»const val APAGANDO = «desmontar»} Antes de ordenar, déjame ser rápido cero y diferente hacer aquí Es una buena práctica implementar la clasificación en el extremo de la API original en lugar de tener un extremo separado como requisito previo, por lo que queremos manejarlo con y sin clasificación en nuestro /Observaciones punto final

  1. cero -: Llamada telefónica.consulta.parámetros de consulta[“sort”] here se refiere a la consulta de clasificación en el punto final. Si esa consulta no está allí, es Cero, Simplemente busque y devuelva todos los elementos presentes en la base de datos sin ningún tipo de clasificación.
  2. diferente -: si el valor de la consulta para ordenar es diferente a ascendente o descendiendo, No es válido y no queremos enviar la respuesta correcta al cliente en este caso porque es una solicitud no válida, por lo que devolví un código de error. 406 que significa ______________ Inaceptable.
  3. Ascendiendo descendiendo -: Como sugiere el nombre, esto se usa para enviar una lista ascendente o descendente de elementos al cliente. En función del tipo de intercalación solicitado por el cliente, el servidor calcula (consulta) la intercalación correcta y se la devuelve al cliente.

Echemos un vistazo a nuestra función que consulta lo mismo.divertida fetchSortedNotes(esDescendente: Booleano? = INCORRECTO): lista {valor Notas = Entidad de notasvalor notasOrdenadasPorNombre = si (está descendiendo == Es correcto) {Observaciones.una noticia.desc()} diferente {Observaciones.una noticia.asc()}atrás base de datos.from(NotesEntity).select().orderBy(notesSortedByName).map {valor identificación = eso[NotesEntity.id]valor nota = eso[NotesEntity.note]Nota (ID ?: -1, Nota ?: «»)}}La función es simple y se explica por sí misma. Internamente, lo que hace esta función de extensión desc() y asc() es consultar orden por nota. Las consultas exactas para esto serían -:SELECCIONE * DE Notas ORDEN POR Nota;SELECCIONE * DESDE notas ORDEN POR notas desc;Si es un desarrollador de back-end o ha probado Node JS u otros marcos BE en ocasiones, es posible que haya notado que hay algunas acciones que se deben realizar al recibir una solicitud o enviar una respuesta. Esta acción puede incluir agregar una verificación de validación o autorización, imprimir registros de errores en la consola o cargarlos en los registros de Firebase, agregar encabezados a cada solicitud o verificar que la solicitud se realizó con todos los encabezados requeridos. Es posible que desee implementar estas acciones para todos o la mayoría de sus puntos finales de API. Entonces, ¿qué hacemos para escribir esta lógica como validación y registro para cada punto final individual? No, usamos el llamado middleware en Node JS. Eso es lo que dice en Ktor enchufar. Solía ​​​​llamarse una función de ktor, pero luego se cambió a un complemento porque la función del nombre es muy genérica. Hay muchos complementos integrados que ofrece Ktor, como registro de llamadas para monitoreo, autenticación para autorización normal, compresión, Cors y muchos más. También podemos usar algunos complementos de terceros como Koin que nos ofrece dependencias en tiempo de ejecución, jwt para autorización avanzada, etc. Además de los complementos integrados y de terceros, el equipo de Ktor nos facilita la creación de nuestros propios complementos personalizados que se adaptan a nuestro caso de uso y los usamos fácilmente en todas o algunas de nuestras rutas.

A partir de la versión 2.0.0, Ktor proporciona una nueva API para crear complementos personalizados. En general, esta API no requiere la comprensión de los conceptos internos de Ktor, como canalizaciones, etapas, etc. En su lugar, tiene acceso a diferentes etapas de procesamiento de solicitudes y respuestas utilizando los controladores onCall, onCallReceive y onCallRespond.

El proceso de creación de un complemento y lo que significa onCall, onCallReceive y onCallRespond, etc. está muy claramente definido en la documentación aquí, así que no entraría en todos y cada uno, pero creemos rápidamente dos complementos tan pequeños pero útiles y usémoslos en nuestro aplicaciónimportar io.ktor.servidor.complementos.*valor ErrorLoggerPlugin = createApplicationPlugin(nombre = «ErrorLoggerPlugin») {activado (llamada fallida) { _, porque ->imprimir(«ERROR DE COMPLEMENTO: la llamada a la API falló debido a ${porque.Mensaje}»)}}valor RequestLoggerPlugin = createApplicationPlugin(nombre = «RequestLoggerPlugin») {al llamar { Llamada telefónica ->Llamada telefónica.consulta.origen.aplicar {imprimir(«URL de solicitud: $esquema://$host:$puerto$uri»)}}}Como sugiere el nombre, el primer ErrorLoggerPlugin es responsable de imprimir todas las llamadas API fallidas y el motivo del error. En una aplicación de producción real, puede considerarla como el monitoreo de los registros de errores en Firebase u otros SDK y alertarlo si hay un aumento repentino en los errores de la API. El segundo RequestLoggerPlugin de nuevo como. Como sugiere el nombre, se registran todas las solicitudes entrantes del cliente, otra métrica importante para el monitoreo.

Instalar complementos

Hemos visto cómo crear nuestro propio complemento personalizado, ahora veamos cómo usarlo en nuestra aplicación. Queremos usar los complementos en 1. todos los puntos finales o 2. puntos finales seleccionados. Ktor nos lo pone fácil nuevamente, para los complementos con ámbito de aplicación, simplemente use createApplicationPlugin mientras crea su complemento y, si tiene el alcance de una ruta, use createRouteScopedPlugin.divertida Aplicación.módulo() {configureLogging()}divertida Aplicación.configureLogging() {instalar(ErrorLoggerPlugin)instalar(SolicitarLoggerPlugin)} Instalé estos dos complementos según el caso de uso de mi aplicación, pero también puede instalarlos para una ruta específica, por ejemplo, .configureLogging() y agregarlo en notesRoute() .divertida Aplicación.módulo() {configureLogging()configureRouting()}divertida Aplicación.configureLogging() {instalar(ErrorLoggerPlugin)instalar(SolicitarLoggerPlugin)}divertida Aplicación.configureRouting() {reenvío {NotasRutas()AuthenticateRoutes (Secreto, Expositor, Audiencia)}}divertida Route.notes() { instalar(RequestLoggerPlugin)recibir(«/Observaciones») {}} y RequestLoggerPlugin a -:valor RequestLoggerPlugin = createRouteScopedPlugin(nombre = «RequestLoggerPlugin») {al llamar { Llamada telefónica ->Llamada telefónica.consulta.origen.aplicar {imprimir(«URL de solicitud: $esquema://$host:$puerto$uri»)}}}Esto le permite crear e implementar complementos personalizados para su aplicación o rutas específicas. Puedes encontrar el código completo en mi repositorio de GitHub ktor-apiPróximamente habrá muchos más artículos interesantes sobre Ktor, Kotlin y Android 🙂 Comparta sus comentarios en los comentarios y mencione cuál desea que sea el próximo artículo para Ktor. Gracias por dedicar su valioso tiempo leyendo este artículo, ¡compártalo con sus amigos!

Deja una respuesta

Tu dirección de correo electrónico no será publicada.