Android. Servicios Rest
Last updated
Last updated
Como ya hemos visto antes, en Android para descargar el contenido desde una URL podemos utilizar la clase HttpURLConnection
. Por defecto al realizar una conexión, si no indicamos nada más, se realizará por GET
. Si llamamos al método setDoOutput(true)
la petición se realizará por POST
. Y para realizar una petición de otro tipo, como PUT
o DELETE
, tenemos que indicarlo mediante el método setRequestMethod
.
A continuación se repasa la gestión de los códigos de estado, del uso de cabeceras y por ultimo se incluyen ejemplos de todos los tipos de peticiones.
Como ya vimos en la sesión anterior, a partir del método getResponseCode
de la clase HttpURLConnection
podemos obtener el códigos de estado que se envía en la cabecera de la respuesta. Si el codigo es igual a 200 indicará que la petición es correcta y que se puede descargar el contendio. En caso de que haya algún error devolverá un valor distinto a 200 correspondiente al código del error (Podéis consultar la lista completa de códigos en ). Los códigos de error usados dependerán de la API, no es obligatorio usarlos todos, y algunas usarán solo algunos de ellos para indicar un error genérico. Lo que sí que es estándar es devolver el código 200 si la petición es correcta. Para comprobar esto podemos hacer:
Para añadir cabeceras a una petición usamos el método setRequestProperty
.
Por ejemplo para indicar el user-agent, el formato de la codificación esperada y el formato en texto plano de los datos enviados usaríamos:
Para indicar que el formato de los datos enviados y de la respuesta esperada es JSON tenemos que añadir:
Para indicar que vamos a usar XML pondríamos:
O para desactivar la compresión Gzip de la comunicación:
Y para consultar las cabeceras de la respuesta podemos usar el método getHeaderFields
que devolverá toda la lista de cabeceras:
O para consultar una cabecera específica podemos usar:
Por ejemplo para realizar una petición tipo GET a una URI y obtener su contenido tendríamos que hacer:
Este método recibe como entrada una cadena con la dirección URL a la cual se quiere realizar la peticion GET y devuelve como respuesta el contenido de la misma. En caso de que hubiera algún error devolvería null.
En esta función también hemos especificado el tipo MIME de los datos de la peticion (Content-Type
) y el tipo de representación que queremos obtener como respuesta (mediante la cabecera Accept
).
Para realizar una petición por POST
tenemos que llamar al método setDoOutput(true)
y además indicar el tipo de petición POST mediante el metodo setRequestMethod
:
En este caso la función, además de la URL, recibe un segundo parámetro con los datos a enviar. En este ejemplo se reciben los datos como una cadena (que ya tendrá los datos almacenados) y se envían a servidor usando un output stream. A la clase PrinterWriter
se le puede pasar todo tipo de datos que queramos enviar, como por ejemplo un objecto JSON, o incluso podríamos establecer distintos parámetros a enviar de la forma:
En la función de ejemplo simplemente se lee el código de la respuesta y con eso ya podemos saber si el servidor ha procesado bien o no la petición. En caso de que queramos leer el cuerpo de la respuesta lo podemos hacer de la misma forma que en la petición tipo GET
. A veces, según la API usada, también nos interesará leer la cabecera Location
de respuesta, ya que en ella se suele devolver la URI del nuevo recurso generado.
Las peticiones PUT son muy similares a las POST, también tenemos que usar los métodos setDoOutput(true)
e indicar el tipo de petición PUT mediante el metodo setRequestMethod
:
Como se puede ver recibe como parámetros la URL y los datos a enviar, y devuelve el código de respuesta para que podamos comprobar si todo ha funcionado correctamente. Al igual que en las peticiones tipo POST también podemos enviar otro tipo de datos y leer el cuerpo de la respuesta si fuese necesario.
Y por último para realizar una petición tipo DELETE simplemente tendríamos que indicarlo con el método setRequestMethod
y por último llamar a getResponseCode
para lanzar la petición y comprobar el código de respuesta.
Como respuesta de esta función se devolverá el código de la petición, de esta forma podremos comprobar desde fuera si se ha eliminado correctamente o si ha habido algún error.
Los servicios REST estan fuertemente vinculados al protocolo HTTP, por lo que los mecanismos de seguridad utilizados también deberían ser los que define dicho protocolo. Pueden utilizar los diferentes tipos de autentificación definidos en HTTP: Basic, Digest y X.509. Sin embargo, cuando se trata de métodos que se dejan disponibles para que servicios externos puedan acceder a ellos, utilizar directamente estos mecanismos básicos de seguridad puede resultar peligroso. En estos casos la autentificación suele realizarse mediante el protocolo OAuth. Este último se sale de los contenidos del curso, en la siguiente sección nos centraremos en la seguridad HTTP básica.
Seguridad HTTP básica
Para acceder a servicios protegidos con seguridad HTTP estándar deberemos proporcionar en la llamada al servicio las cabeceras de autentificación con los credenciales que nos den acceso a las operaciones solicitadas.
Por ejemplo, desde un cliente Android en el que utilicemos la API de red estándar de Java SE deberemos definir un Authenticator
que proporcione estos datos:
Para quienes no estén muy familiarizados con la seguridad en HTTP, conviene mencionar el funcionamiento del protocolo a grandes rasgos. Cuando realizamos una petición HTTP a un recurso protegido con seguridad básica, HTTP nos devuelve una respuesta indicándonos que necesitamos autentificarnos para acceder. Es entonces cuando el cliente solicita al usuario las credenciales (usuario y password), y entonces se realiza una nueva petición con dichas credenciales incluidas en una cabecera Authorization. Si las credenciales son válidas, el servidor nos dará acceso al contenido solicitado.
Este es el funcionamiento habitual de la autentificación. En el caso del acceso mediante HttpURLConnection
que hemos visto anteriormente, el funcionamiento es el mismo, cuando el servidor nos pida autentificarnos la librería lanzará una nueva petición con las credenciales especificadas en el proveedor de credenciales.
Sin embargo, si sabemos de antemano que un recurso va a necesitar autentificación, podemos también autentificarnos de forma preventiva. La autentificación preventiva consiste en mandar las credenciales en la primera petición, antes de que el servidor nos las solicite. Con esto ahorramos una petición, pero podríamos estar mandando las credenciales en casos en los que no resulta necesario.
Con HttpURLConnection
podemos activar la autentificación preventiva añadiendo nosotros mismos la cabecera:
Es importante destacar que para que todos estos métodos sean seguros las conexiones se tendrían que realizar con HTTPS ya que en otro caso la conexion no sería segura.
En las comunicaciones por red es muy común transmitir información en formato XML, el ejemplo más conocido depués del HTML, son las noticias RSS. En este último caso, al delimitar cada campo de la noticia por tags de XML se permite a los diferentes clientes lectores de RSS obtener sólo aquellos campos que les interese mostrar.
Android nos ofrece dos maneras de trocear o "parsear" XML. El SAXParser
y el XmlPullParser
:
El parser SAX requiere la implementación de manejadores que reaccionan a eventos tales como encontrar la apertura o cierre de una etiqueta, o encontrar atributos.
Por el contrario XmlPullParser
necesita menos implementación ya que consiste en iterar sobre el árbol de XML (sin tenerlo completo en memoria) conforme el código lo va requiriendo, indicándole al parser que tome la siguiente etiqueta (método next()
) o texto (método nextText()
).
A continuación mostramos un ejemplo sencillo de uso del XmlPullParser
. Préstese atención a las sentencias y constantes resaltadas, para observar cómo se identifican los distintos tipos de etiqueta, y si son de apertura o cierre. También se puede ver cómo encontrar atributos y cómo obtener su valor.
Nota: el ejemplo anterior, igual que todas las operaciones que requieran acceso a Internet, tendrá que estar en un hilo.
El ejemplo anterior serviría para imprimir en el LogCat el título del siguiente fragmento de página web, que en este caso sería "Universidad de Alicante", y para encontrar el meta
cuyo atributo name
sea "description" y mostrar el valor de su atributo content
:
JSON es una representación muy utilizada para formatear los recursos solicitados a un servicio web RESTful. El formato sigue la misma notación de objetos de JavaScript, por lo tanto ocupa muy poco, es texto plano y puede ser manipulado muy fácilmente utilizando JavaScript, PHP u otros lenguajes.
Con la definición de agrupaciones anterior, podemos combinar múltiples conjuntos para crear cualquier tipo de estructura requerido. El siguiente ejemplo muestra una descipción JSON de un objeto con información sobre una lista de mensajes:
Antes de visualizar cualquiera de los valores de una respuesta JSON, necesitamos convertirla en otro tipo de estructura para poder procesarla. Dentro de la API de Android encontramos una serie de clases que nos permiten analizar y componer mensajes JSON. Las dos clases fundamentales son JSONArray
y JSONObject
. La primera de ellas representa una lista de elementos, mientras que la segunda representa un objeto con una serie de propiedades. Podemos combinar estos dos tipos de objetos para crear cualquier estructura JSON. Cuando en el JSON encontremos una lista de elementos ([ ... ]
) se representará mediante JSONArray
, mientras que cuando encontremos un conjunto de propiedades clave-valor encerrado entre llaves ({ ... }
) se representará con JSONObject
. Por ejemplo, para procesar el JSON del ejemplo anterior podríamos utilizar el siguiente código:
El objeto JSONArray
nos permite conocer el número de elementos que contiene (length
), y obtenerlos a partir de su índice, con una serie de métodos get-
. Los elementos pueden ser de tipos básicos (boolean
, double
, int
, long
, String
), o bien ser objetos o listas de objetos (JSONObject
, JSONArray
).
Los objetos (JSONObject
) tienen una serie de campos, a los que también se accede mediante una serie de métodos get-
que pueden ser de los mismos tipos que en el caso de las listas. En el ejemplo anterior son cadenas (texto, y usuario), pero podrían ser listas u otros objetos anidados.
Esta librería no sólo nos permite analizar JSON, sino que también podemos componer mensajes con este formato. Los objetos JSONObject
y JSONArray
tienen para cada método get-
, un método put-
asociado que nos permite añadir campos o elementos. Una vez añadida la información necesaria, podemos obtener el texto JSON mediante el método toString()
de los objetos anteriores. A continuación se incluye un ejemplo:
Además es importante que capturemos las excepciones al procesar cadenas en JSON ya que en caso de intentar obtener un tipo de elemento que no estuviera presente se lanzaría una excepción del tipo JSONException
.
En este ejercicio se pide realizar una aplicación Android que actúe de cliente de los servicios públicos tipo REST del videoclub. Para esto tenéis que crear una nueva aplicación, llamada Videoclub
, que va a tener solamente dos actividades: un listado con las películas y la vista detalle de una película (la apariencia de estas dos pantallas se deja a vuestra elección). En las plantillas se facilita la clase Movie.java
que podéis utilizar para almacenar los datos de las películas.
En la primera actividad, el listado de películas, se tendrá que:
El servidor podrá devolver:
El array de películas en JSON y el código de respuesta 200.
Los códigos de error 404 o 500.
Una vez completada la descarga tendréis que procesar el JSON (creando una lista de objetos tipo Movie).
A partir de la lista obtenida se creará un adaptador que irá asociado a la lista. Este adaptador realizará la descarga de las portadas usando la técnica lazy loading (el código de esta parte es muy parecido al del último ejercicio de la sección anterior).
Al pulsar sobre un elemento de esta lista se tendrá que abrir la vista detalle de la misma pasándole (en el intent de llamada) los datos de la película.
En la vista detalle primero tendréis que recoger el intent de llamada con todos los datos de la película, mostrar la información y crear un hilo para descargar la portada de la película.
La gramática de los objetos JSON es simple y requiere la agrupación de la definición de los datos y valores de los mismos. En primer lugar, los elementos están contenidos dentro de llaves {
y }
y separados por comas; los valores de los diferentes elementos se organizan en pares con la estructura "nombre":"valor"
; y finalmente, las secuencias de elementos (o arrays) están contenidas entre corchetes [
y ]
. Y esto es todo! Para una descripción detallada de la gramática, podéis consultar
Esta librería es sencilla y fácil de utilizar, pero puede generar demasiado código para parsear estructuras de complejidad media. Existen otras librerías que podemos utilizar como GSON () o Jackson () que nos facilitarán notablemente el trabajo, ya que nos permiten mapear el JSON directamente con nuestros objetos Java, con lo que podremos acceder al contenido JSON de forma similar a como se hace en Javascript.
Solicitar en primer lugar el listado al servidor realizando una petición tipo GET a la URL "" (recordad que tiene que ser asíncrona).