En esta página:
Demo
En la parte anteriror o la parte 5 de este tutorial, configuramos JWT (JSON Web Tokens), una tecnología que nos permite añadirle una capa de seguridad a nuestra API REST, existen usuarios maliciosos que pueden acceder a nuestros endpoints y por ello es importante portegerlos. Existen maneras avanzadas de implementar JWT, en este tutorial estamos aplicando una manera estandar, para que tengas una idea de como debes hacerlo, asimismo recuerda que hay otras capas de seguridad que debes agregar a tu API REST para que este protegida. En esta parte 6 continuamos con el tutorial Como Crear una API REST con Django 3.1.1 + Consumir Datos en una Aplicación Android, vamos con ello.
Partes
Antes de continuar te invito a escuchar el Podcast: “Porque Debes Acostumbrarte A Resolver Los Problemas De Código Por Tu Cuenta” y “Consejos Para Entrenar Tu Memoria de Programador” (Anchor Podcast):
Spotify: | Sound Cloud: | Apple Podcasts | Anchor Podcasts |
Bien ahora continuemos con el Post: Como Crear una API REST con Django 3.1.1 + Consumir Datos en una Aplicación Android – Parte 6.
Creación de Aplicación Android
Para crear la alicación usaré la versión Electric Eel de Android Studio, es la última versión de Android Studio, al menos hasta la fecha de este Post.
Abro Android Studio y presiono el botón New Project para crear un nuevo proyecto:
Elijo la opción Empty Activity, para crear un aplicación con una actividad vacia, presiono el botón Next para continuar:
En la siguiente ventana le debo poner un nombre a mi aplicación, le daré el nombre MyApplication, tu le puedes poner el nombre que desees, usaré el lenguaje de programación Kotlin. Presiono el botón Finish para que Android Studio inicie la creación del proyecto:
Abro el archivo MainActivity.kt y dentro de la función nativa onCreate, llamo a la función obtenerToken(), la cual crearé más abajo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package com.example.myapplication import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Obtenemos un token de de seguridad de la API REST obtenerToken() } } |
Ahora creo la función obtenerToken(), esta función envia a la API REST un token refresh y este nos devuelve un token access que la aplicación lo usa para autenticarse en la API REST y poder obtener los datos, ya sea de postres o jugos.
Dentro de la función obtenerToken() paso el token refresh el cual menciono casi al final de la Parte 5 de este tutorial. Haciendo uso de la herramienta Postman, lo que hago es autenticarme en la ruta http://localhost:8000/api/token/ enviando un email y password, la API REST me genera un par de tokens uno es refresh y otro es access, copio el token refresh:
Solo como conocimiento, mencionar que si voy a la ruta http://localhost:8000/api/token/refresh/ y pego y envio el token refresh, la API REST me genera un nuevo token access:
Para este ejemplo es suficiente que solo use el token refresh que generé en la ruta http://localhost:8000/api/token/ enviando un email y password.
Copio el token resfresh y en Android Studio, dentro de la función obtenerToken() creo una variable y alli pego el token refresh, exactamente lo coloco dentro de una variable llamada token, tu le puedes poner el nombre que desees a esta variable.
La idea es que cada ves que envie el token refresh, la API REST, me genere un token access y con este token access autenticarme siempre, asi evitamos escribir a cada rato un nuevo token access para obtener los datos. Este token access lo guardamos en Shared Preferences y luego lo leemos para enviarlo en la función obtenerDatos(), tu puedes elegir como y en que sistema de almacenamiento guardarlo, para este ejemplo usaré sencillamente Shared Preferences.
Nota: Recuerda que el token refresh tiene un periodo de vida de 24 horas, una ves que caduca, debes generar uno nuevo para poder obtener los datos de la API REST, ya que te puede dar error.
Creo la función obtenerToken() y agrego lo siguiente (He colocado comentarios para explicar que hace otras partes importantes del código):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
fun obtenerToken() { // Token val token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY3NjgwOTYwMSwiaWF0IjoxNjc2NzIzMjAxLCJqdGkiOiI5OWZjZGJmNjhmNzk0ODAwYTU5NWJkYjIzMjUzOWYyZiIsInVzZXJfaWQiOjF9.nGw0CHVI0BgqZu9mFkfxd7rCCdU_pdE-VAqHa59hjvI" Servicio.instancia.obtToken(token) .enqueue(object : Callback<Token> { override fun onResponse(call: Call<Token>, response: Response<Token>) { val message = response.body()?.access Log.e("respuesta", message.toString()) // Guardamos el token obtenido en Shared Preferences val sp = getSharedPreferences("token", Context.MODE_PRIVATE) val editor = sp.edit() editor.putString("access", message.toString()) editor.apply() // Obtenemos los datos de la API REST obtenerDatos() } // Si hay error al obtener los datos, mostramos un mensaje override fun onFailure(call: Call<Token>, t: Throwable) { Log.e("Estado", "Hubo Error al solicitar datos", t) } }) } |
Nota: Elimina los carateres extraños que no formen parte del token, para que no te salten errores al autenticarte.
En el código anterior, puedes ver que estoy llamando a la función obtenerDatos(), esta función la crearemos a continuación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private fun obtenerDatos() { // Leemos el token almacenado en Shared Preferences val sp = getSharedPreferences("token", Context.MODE_PRIVATE) val token = sp.getString("access", "") val datos = Servicio.instancia.listarDatos("Bearer $token", q = "all", nombre = String()) datos.enqueue(object : Callback<List<Datos>?> { override fun onResponse(call : Call<List<Datos>?>, response: Response<List<Datos>?>) { val data = response.body() Log.e("fresa1", data.toString()) } // Si hay error al obtener los datos, mostramos un mensaje override fun onFailure(call: Call<List<Datos>?>, t: Throwable) { Log.e("fresa2", "Hubo Error al solicitar datos", t) } }) } |
En el codigo anterior de la función obtenerDatos() solicito los datos a la API REST, el estoy pasando el token que guarde en SharedPreferences de Android.
Ahora creo un archivo llamado Api.kt en donde conecto la aplicación al servidor de Django. También accedo al endpoint api/token/refresh/ que es usado para la función obtenerToken() y al endpoint /jugos que es usado dentro la función obtenerDatos():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.example.myapplication import retrofit2.Call import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.http.* // Conexión local a Django const val URL = "http://10.0.2.2:8000/" interface Api { @FormUrlEncoded @POST("api/token/refresh/") fun obtToken( @Field("refresh") refresh: String ): Call<Token> @GET("jugos") fun listarDatos(@Header("Authorization") token : String, @Query("q") q: String, @Query("nombre") nombre : String) : Call<List<Datos>> } object Servicio { val instancia: Api init { val retrofit = Retrofit.Builder() .baseUrl(URL) .addConverterFactory(GsonConverterFactory.create()) .build() instancia = retrofit.create(Api::class.java) } } |
También creo un archivo lamado Datos.kt que me servirá de clase POJO (Plain Old Java Object), para obtener los datos de postres o jugos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.example.myapplication import com.google.gson.annotations.SerializedName data class Datos( @SerializedName("nombre") val nombre: String, @SerializedName("precio") val precio: String, @SerializedName("stock") val stock: String, @SerializedName("img") val img: String, ) |
Igualmente creo el archivo Token.kt que también me servirá de clase POJO (Plain Old Java Object), para obtener el token de seguridad para poder obtener los datos de la API REST:
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.example.myapplication import com.google.gson.annotations.SerializedName data class Token ( @SerializedName("access") val access: String, ) |
También instalaré los siguientes paquetes en mi archivo build.grade (Module :app), uno es Retrofit que me permitre leer APIs REST, también instalo Retrofit Converter GSON, que me permite obtener los datos de la API REST en JSON y el tercer paquete es Glide, que me permite mostrar las imágenes de los productos, de manera sencilla:
1 2 3 4 5 6 7 8 |
// Retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // Glide implementation 'com.github.bumptech.glide:glide:4.13.2' |
Por último abro el archivo AndrodManifest.xml y le agrego esta configuración:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:usesCleartextTraffic="true" android:theme="@style/Theme.MyApplication" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> |
La estructura de archivos y directorios de mi proyecto en Android Studio se ve así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/app ├── /manifest ├── /java ├── /com.example.myapplication ├── Api.kt ├── Datos.kt ├── MainActivity.kt ├── Token.kt ├── /com.example.myapplication (androidTest) ├── /com.example.myapplication (test) ├── /java (generated) ├── /res ├── /res (generated) ├── /Gradle Scripts ├── build.gradle (Project: My_Application) ├── build.gradle (Project: Module: app) ├── proguard-rules.pro (ProGuard Rules for ":app") ├── gradle.properties (Project Properties) ├── gradle-wrapper.properties (Gradle Version) ├── local.properties (SDK Location) ├── settings.gradle (Project Settings) |
Entonces si ejecuto la aplicación, puedo ver en la pestaña Run que la API REST me esta devolviendo los datos, especificamente datos de jugos:
También puedo obtener datos de postres, solo debo usar el endpoint /postres de la API REST, esto lo veremos más adelante.
Nota: En tu aplicación recuerda hacer el proceso completo de autenticación, es decir el usuario debe iniciar sesión en la aplicación con su usuario y contraseña, luego de ello ya puede internamente la app enviar el token para solicitar los datos. No quiero salirme del objetivo principal que es mostrar los datos en la aplicación.
Ten Paciencia, lo que quiero es que conozcas bien como se crea este proyecto y no llenarte el capitulo de mucho contenido porque te puedes marear y no tendrás un óptimo aprendizaje.
Nota (s)
- En la siguiente parte continuaremos con la creación de la aplicación Android, mostraremos de manera más agradable los datos de la API REST.
- No olvides que debemos usar la Tecnología para hacer cosas Buenas por el Mundo.
Síguenos en nuestras Redes Sociales para que no te pierdas nuestros próximos contenidos.