Kotlin Ktor REST Service
Servicio web para API REST con Kotlin y Ktor.
- Kotlin Ktor REST Service
Acerca de
El proyecto consiste en realizar un servicio REST con Kotlin y Ktor. Para ello vamos a usar la tecnologías que nos propone Jetbrains para hacer todo el trabajo, desde la creación de la API REST, hasta la implementación de la misma, así como la serialización de objetos y/o acceso al almacenamiento de los mismos.
Ktor
Ktor es un nuevo framework para desarrollar servicios y clientes asincrónicos. Es 100% Kotlin y se ejecuta en usando Coroutines. Admite proyectos multiplataforma, lo que significa que puede usarlo para cualquier proyecto dirigido a JVM, Android, iOS o Javascript. En este proyecto aprovecharemos Ktor para crear un servicio web para consumir una API REST. Además, aplicaremos Ktor para devolver páginas web.
Punto de Entrada
El servidor tiene su entrada y configuración en la clase Application. Esta lee la configuración en base al fichero de configuración y a partir de aquí se crea una instancia de la clase Application en base a la configuración de module().
Creando rutas
Las rutas se definen creando una función de extensión sobre Route. A su vez, usando DSL se definen las rutase en base a las petición HTTP sobre ella. Podemos responder a la petición usando call.respondText(), para texto; call.respondHTML(), para contenido HTML usando Kotlin HTML DSL; o call.respond() para devolver una respuesta en formato JSON o XML. finalmente asignamos esas rutas a la instancia de Application, es decir, dentro del método module(). Un ejemplo de ruta puede ser:
routing {
// Entrada en la api
get("/") {
call.respondText("👋 Hola Kotlin REST Service con Kotlin-Ktor")
}
}
Serializando a JSON
Para serializar objetos a JSON, usamos la librería de serialización de Kotlin, especialmente para hacer la negociación de contenido en JSON.
Para ello, las clases POJO a serailizar son indicadas con @Serializable.
import kotlinx.serialization.Serializable
@Serializable
data class Customer(var id: String, val firstName: String, val lastName: String, val email: String)
Posteriormente, en nuestra Application de Ktor, instalamos como un plugin la negociación de contenido en JSON.
install(ContentNegotiation) {
json()
}
Procesando Request
Dentro de un controlador de ruta, puedes obtener acceso a una solicitud utilizando la propiedad call.request. Esto devuelve la instancia de ApplicationRequest y proporciona acceso a varios parámetros de solicitud.
routing {
get("/") {
val uri = call.request.uri
call.respondText("Request uri: $uri")
}
}
Parámetros de ruta
Para obtener acceso a los valores de los parámetros de ruta mediante la propiedad call.parameters. Por ejemplo, call.parameters["login"] devolverá admin para la ruta /user/admin
get("/user/{login}") {
if (call.parameters["login"] == "admin") {
call.respondText("Request admin: ${call.parameters["login"]}")
}
}
Parámetros de consulta
Para obtener acceso a los parámetros de una cadena de consulta, puede usar la propiedad ApplicationRequest.queryParameters. Por ejemplo, si se realiza una solicitud a /products?price=asc, puede acceder al parámetro de consulta de precio.
get("/products") {
if (call.request.queryParameters["price"] == "asc") {
call.respondText("Request price: ${call.request.queryParameters["price"]}")
}
}
Parámetros de cuerpo
Ktor proporciona un complemento de negociación de contenido para negociar el tipo de medio de la solicitud y deserializar el contenido a un objeto de un tipo requerido. Para recibir y convertir contenido para una solicitud, llama al método de recepción que acepta una clase de datos como parámetro.
post("/customer") {
val customer = call.receive<Customer>()
customerStorage.add(customer)
call.respondText("Customer stored correctly", status = HttpStatusCode.Created)
}
Peticiones multiparte
Si necesita recibir un archivo enviado como parte de una solicitud de varias partes, llame a la función receiveMultipart y luego recorra cada parte según sea necesario. En el siguiente ejemplo, PartData.FileItem se usa para recibir un archivo como flujo de bytes.
post("/upload") {
// multipart data (suspending)
val multipart = call.receiveMultipart()
multipart.forEachPart { part ->
val fileName = part.originalFileName as String
var fileBytes = part.streamProvider().readBytes()
File("uploads/$fileName").writeBytes(fileBytes)
part.dispose()
}
call.respondText("$fileName is uploaded to 'uploads/$fileName'")
}
Autenticación y Autorización
Podemos implementar métodos de autenticación y autorización variados con Ktor. Este ejemplo se ha procedido a usar JWT Tokens. Para ello se ha instalado las librerías necesarias para el procesamiento de tokens JWT. Los parámetros para generar el token se han configurado en el fichero de configuración. Debemos tener en cuenta algunos parámetros para proteger y verificar los tokens, así como su tiempo de vida. Posteriormente lo instalamos como un plugin más en la configuración de la aplicación. Podemos configurar su verificador y ademas validar el payload para analizar que el cuerpo del token es válido, tal y como se indica el la documentación de Ktor.
install(Authentication) {
jwt {
// Configure jwt authentication
}
}
Por otro lado, cuando nos logueamos, podemos generar el token y devolverlo al usuario, en base a los parámetros de configuración.
Para proteger ls rutas usamos la función athenticate. Cualquier ruta dentro de ella quedará protegida por la autenticación. Además si leemos en el Payload el usuario y administramos alguna política de permisos, podemos verificar que el usuario tiene permisos para acceder a la ruta.
routing {
authenticate("auth-jwt") {
get("/hello") {
val principal = call.principal<JWTPrincipal>()
val username = principal!!.payload.getClaim("username").asString()
val expiresAt = principal.expiresAt?.time?.minus(System.currentTimeMillis())
call.respondText("Hello, $username! Token is expired at $expiresAt ms.")
}
}
}
Referencia API REST
Recurso Customers
Get all customers
GET /rest/customers?limit={limit}
Get customer by id
GET /rest/customers/{id}
Update customer by id
PUT /rest/customers/{id}
Delete customer by id
DELETE /rest/customers/{id}
Recurso Orders
Get all orders
GET /rest/orders?limit={limit}
Get order by id
GET /rest/orders/{id}
Update order by id
PUT /rest/orders/{id}
Delete order by id
DELETE /rest/orders/{id}
Get contents by order id
GET /rest/orders/{id}
Get contents by order id
GET /rest/orders/{id}/contents
Get total by order id
GET /rest/orders/{id}/total
Get customer by order id
GET /rest/orders/{id}/customer
Subida/Bajada de archivos
Get/Download file by name
GET /rest/uploads/{fileName}
Post/Upload file
POST /rest/uploads/
Delete file
DELETE /rest/uploads/{fileName}
Recursos Autenticados
Login user.
POST /rest/auth/login
Register
POST /rest/auth/register
Me
GET /rest/auth/me
Get all Users
GET /rest/auth/users
PostMan
Puedes consumir el servicio REST con PostMan. Para ello solo debes importar la colección de ejemplo y ejecutar las mismas.
Autor
Codificado con
Contacto
Cualquier cosa que necesites házmelo saber por si puedo ayudarte
Licencia
Este proyecto está licenciado bajo licencia MIT, si desea saber más, visite el fichero LICENSE para su uso docente y educativo.