RESTful - Parte 2: Manejando un solo objeto
Hemos visto en el anterior post cómo hacer un servicio REST solo para producir y consumir un texto simple. Ahora bien, en la vida real no son textos simples, sino estructuras de datos algo complicadas. Pero para ir lentos pero seguros, aprenderemos cómo hacer un servicio REST pero para manejar un solo objeto.
Afortunadamente para nuestros proyectos, no debemos crear ningún XML, ni tener algún "parser" que convierta nuestros objetos en formato XML o algo parecido para enviar y recibir objetos por la red. Solo necesitamos crear nuestros JavaBeans... y ponerle algunos tags.
... con sus respectivos set y get.
Ahora, este JavaBean lo entendemos muy bien en Java, pero recordemos de que un servicio web debe ser compatible para otros lenguajes, y que la estructura de datos más "compatible" es el XML. Así que vamos hacer que este JavaBean se convierta en XML. Bastará con poner la anotación
Nota: Lo normal aquí es usar un manejador de persistencia (sea JPA, JDBC, etc..), pero como el objetivo de este tutorial es ver cómo funciona un REST, no gastaremos esfuerzo por conectarnos a una base de datos.
Este es el código fuente del recurso
Ahora, necesitamos implementar los métodos para registrar un objeto Persona desde el cliente, y leer el objeto desde el hacía hasta el cliente. Por tanto, crearemos dos métodos: registrar y leer.
Sí, nada más (por ahora)
Pero no es así. Porque toda petición que se hace a un recurso web (el que sea) siempre debe devolver algo.. así sea un error, pero debe devolver algo. En REST debe devolver un objeto
Ahora sí... a desplegarlo y a probar... pero si lo probamos en este momento, nos mostrará la siguiente ventana.
y.. qué pondremos en la caja de texto? Pues el objeto a enviar... pero ¿cómo? Pues un dato estándar, como XML o JSON. Por ejemplo, este código
Lo probamos y... error!!! ¿qué pasó?
Pues nuestro servicio REST no sabe si la data que va a recibir es un XML, o un JSON. Hay que decirle al método de ese recurso cómo va a recibir los datos. Por ahora, vamos a poner esta anotación en el método:
Ahora sí, desplegamos, ejecutamos el Test y... vemos que ahora el método POST dice qué tipo permitirá:
Escribimos nuevamente el texto y voila!!
"Bien, el XML funciona, pero el formato es un poco grande ¿se puede usar JSON?" Sí.. y lo mejor, es bien sencillo activar esa opción.
Listo, ahora nuestro método
... seleccionamos el tipo JSON...
... y listo.. funciona!
Si no están seguros de que guardó correctamente, ejecutemos el método GET para ver si lo guardó en el objeto
Esto también es fácil. Por cada método "get" que debemos cambiar el formato, le agregamos la anotación
Ahora, probemos el método "GET".
¿Y si, el
Ojo, cuando es atributo y se desea enviar en formato JSON, se debe considerar ql nombre del atributo antepuesto por un
"En ningún momento aparece el nombre del método
Pues por el mismo método HTTP utilizado: Si se hace "POST", ejecuta el método que está asociado a
¿"Y si quiero diferenciar un 'update' de un 'create' ? " Pues utilizar otro método. Como comenté en el anterior post, el POST debe estar asociado al "create",el GET asociado a la búsqueda, el "PUT" al "update" y el "DELETE" al borrar.
Si hay que hacer otro tipo de "POST" o de "DELETE", debemos utilizar otro objeto Recurso (con anotación
Por ello comenté en el anterior post de que este diseño asegura de que los métodos de mantenimiento están asociados a una sola entidad.. y nos evitará tener métodos en servicios web que no corresponde, por ejemplo, no habrá un manejo de proveedores en un servicio de clientes.
http://kenai.com/projects/apuntes/downloads/download/PersonaRESTWeb%252FPersonaRESTWeb.tar.gz
Aquí el código fuente de una aplicación Java Desktop:
http://kenai.com/projects/apuntes/downloads/download/PersonaRESTWeb%252FPersonaRESTClient.tar.gz
Afortunadamente para nuestros proyectos, no debemos crear ningún XML, ni tener algún "parser" que convierta nuestros objetos en formato XML o algo parecido para enviar y recibir objetos por la red. Solo necesitamos crear nuestros JavaBeans... y ponerle algunos tags.
Manos a la obra...
Supongamos que tenemos creado el proyecto web PersonaRESTWeb sobre GlassFish v3 y sin ningún framework adicional. Recordemos que nuestro proyecto se autoconfigura en REST cuando se aplica una anotación especial.Creando un javaBean
Una vez creado el proyecto, crearemos un JavaBean llamadoPersona
public class Persona {
private int idPersona;
private String nombre;
private java.util.Date fechaNacimiento;
private boolean trabajador;
private char sexo;
//...
... con sus respectivos set y get.
Ahora, este JavaBean lo entendemos muy bien en Java, pero recordemos de que un servicio web debe ser compatible para otros lenguajes, y que la estructura de datos más "compatible" es el XML. Así que vamos hacer que este JavaBean se convierta en XML. Bastará con poner la anotación
@XmlRootElement
al inicio de la declaración de la clase.
@XmlRootElement
public class Persona {
private int idPersona;
private String nombre;
private java.util.Date fechaNacimiento;
private boolean trabajador;
private char sexo;
//...
Creando recurso manejador de Persona
El diseño de este Servicio obliga a que exista un solo recurso manejador por cada Entidad. Por tanto, debemos crear la clasePersonaResource
. Y para que administre un JavaBean, declararemos un objeto static. Nota: Lo normal aquí es usar un manejador de persistencia (sea JPA, JDBC, etc..), pero como el objetivo de este tutorial es ver cómo funciona un REST, no gastaremos esfuerzo por conectarnos a una base de datos.
Este es el código fuente del recurso
PersonaResource
@Stateless
@Path("/personas")
public class PersonaResource {
static Persona persona;
}
Ahora, necesitamos implementar los métodos para registrar un objeto Persona desde el cliente, y leer el objeto desde el hacía hasta el cliente. Por tanto, crearemos dos métodos: registrar y leer.
Leer valor del objeto
Esto ya lo hemos visto. Es declarar un método y declararlo con la anotación@GET
//...
@GET
public Persona leer(){
return persona;
}
//...
Sí, nada más (por ahora)
Guardar valor al objeto
Esto debería ser sencillo. Bastaría con poner este código
//...
@POST
public void guardar(Persona p) {
persona = p;
}
//...
Pero no es así. Porque toda petición que se hace a un recurso web (el que sea) siempre debe devolver algo.. así sea un error, pero debe devolver algo. En REST debe devolver un objeto
javax.ws.rs.core.Response
que contiene el estado de la petición: si está OK, si hay error de restricción, si no responde, etc.. todos los errores que conocemos para HTTP están contenidos en ese objeto. Pero para nuestro caso, vamos a devolver el valor "ok" de la siguiente manera:
//...
@POST
public Response guardar(Persona p) {
persona = p;
return Response.ok(p).build();
}
//...
Ahora sí... a desplegarlo y a probar... pero si lo probamos en este momento, nos mostrará la siguiente ventana.
y.. qué pondremos en la caja de texto? Pues el objeto a enviar... pero ¿cómo? Pues un dato estándar, como XML o JSON. Por ejemplo, este código
<persona>
<idPersona>20</idPersona>
<nombre>Albert</nombre>
<trabajador>true</trabajador>
</persona>
Lo probamos y... error!!! ¿qué pasó?
Pues nuestro servicio REST no sabe si la data que va a recibir es un XML, o un JSON. Hay que decirle al método de ese recurso cómo va a recibir los datos. Por ahora, vamos a poner esta anotación en el método:
//...
@POST
@Consumes("application/xml")
public Response guardar(Persona p) {
persona = p;
return Response.ok(p).build();
}
//...
Ahora sí, desplegamos, ejecutamos el Test y... vemos que ahora el método POST dice qué tipo permitirá:
Escribimos nuevamente el texto y voila!!
Status: 200 (OK)
"Bien, el XML funciona, pero el formato es un poco grande ¿se puede usar JSON?" Sí.. y lo mejor, es bien sencillo activar esa opción.
//...
@POST
@Consumes({"application/xml","application/json"})
public Response guardar(Persona p) {
persona = p;
return Response.ok(p).build();
}
//...
Listo, ahora nuestro método
guardar()
permite recibir tanto JSON como XML. Probemos ahora colocando el siguiente valor en el módulo de prueba:
{ "idPersona":"20",
"nombre":"Bernard",
"trabajador":"true"
}
... seleccionamos el tipo JSON...
... y listo.. funciona!
Si no están seguros de que guardó correctamente, ejecutemos el método GET para ver si lo guardó en el objeto
Modificando el tipo de formato para leer el objeto
Ya vimos que se puede establecer el tipo que el Servicio recibirá por la red usando@Consumes
. Y cuando probamos la lectura, lo convierte siempre a XML ¿se puede cambiar para que sea JSON? Por su puesto, y es igual de simple:
//...
@GET
@Produces({"application/json","application/xml"})
public Persona leer() {
return persona;
}
//...
Por omisión usará el primer tipo especificado (en este caso "json"), o - dependiendo cómo se indique en el cliente - puede utilizar el formato XML.Modificando la estructura de los datos
Nuestro bean utiliza la propiedadidPersona
, y el REST lo procesa correctamente. Pero, si el estándar de los proyectos donde se va a utilizar, dice que debe ser id_persona
y el campo nombre sea nombre_persona
¿Cómo modificamos esto?Esto también es fácil. Por cada método "get" que debemos cambiar el formato, le agregamos la anotación
@XmlElement
seguido del nombre como deberá ser manejado. Vayamos al Bean Persona y pongamos esto:
//...
@XmlElement(name = "id_persona")
public int getIdPersona() {
return idPersona;
}
@XmlElement(name = "nombre_persona")
public String getNombre() {
return nombre;
}
//...
Ahora, probemos el método "GET".
¿Y si, el
id_persona
tiene que ser un atributo del XML? Cambiamos la notación @XmlElement
por @XmlAttribute
//...
@XmlElement(name = "id_persona")
public int getIdPersona() {
return idPersona;
}
//...
Ojo, cuando es atributo y se desea enviar en formato JSON, se debe considerar ql nombre del atributo antepuesto por un
@
.
{ "@id_persona":"20",
"nombre_persona":"Bernard",
"trabajador":"true"
}
"En ningún momento aparece el nombre del método guardar()
y leer()
¿Cómo sabe qué método utilizar?"
Pues por el mismo método HTTP utilizado: Si se hace "POST", ejecuta el método que está asociado a @POST
, si se hace "GET", utiliza el @GET
.¿"Y si quiero diferenciar un 'update' de un 'create' ? " Pues utilizar otro método. Como comenté en el anterior post, el POST debe estar asociado al "create",el GET asociado a la búsqueda, el "PUT" al "update" y el "DELETE" al borrar.
Si hay que hacer otro tipo de "POST" o de "DELETE", debemos utilizar otro objeto Recurso (con anotación
@Path
) ya que será accedido desde otra ruta.Por ello comenté en el anterior post de que este diseño asegura de que los métodos de mantenimiento están asociados a una sola entidad.. y nos evitará tener métodos en servicios web que no corresponde, por ejemplo, no habrá un manejo de proveedores en un servicio de clientes.
Código del proyecto
Aquí se encuentra el código fuente del proyecto web:http://kenai.com/projects/apuntes/downloads/download/PersonaRESTWeb%252FPersonaRESTWeb.tar.gz
Aquí el código fuente de una aplicación Java Desktop:
http://kenai.com/projects/apuntes/downloads/download/PersonaRESTWeb%252FPersonaRESTClient.tar.gz
Muchas gracias, por compartirnos tu conocimiento Diego, esto me aclara bastante las dudas que fueron surgiendo luego de leer el post anterior, ya estuve probando para consumir RESTfull desde dispositvos móviles y realmente es muy eficiente su respuesta.
ردحذفgenial esto esta muy itil gracias, pero sigo con el mismo problema del ejemplo anterior como lo uso en otra aplicacion porque desde algun otro proyecto sirven bien los servicios pero si yo tengo una pagina ASP (HTML, PHP) y con jquery quiero llamar mi service como lo utilizo??
ردحذفHola Ernesto
ردحذفaun no respongo tu anterior pregunta, me estoy dando un tiempito.
El jquery es javascript, no importa en qué tipo de servidor exista. Solo ponlo en un html,asp,php,cgi,etc con referencia al servicio REST (que en este caso debe ser en un servidor JavaEE) y funciona.
Hola, muchas gracias por compartir tus experiencias, para aquellos que estamos incursionando es de mucho valor.
ردحذفYo necesito implementar este tema para mi tesis pero requiero que el webservice me guarde un objeto complejo, es decir un objeto propio con otros objetos y con listas de objetos, me podrías indicar como o un link donde pueda documentarme en este aspecto, muchas gracias.
Hola Pablo
ردحذفEstoy elaborando el tutorial para objetos complejos y arreglos, pero toma su tiempo. Pero te recomiendo que utilices el mismo NetBeans para generar estos servicios REST desde entidades. Mira el siguiente link que es de un post anterior, hay un vídeo donde se muestra cómo se hace:
http://goo.gl/8LIoj
Al final ha creado dos objetos para manejar una entidad: la entidad misma y una clase "Converter" que es un "delegate". Este delegate es que el que se envía por REST.
saludos
Hola, quise usar un link a uno de tus tutoriales y no he podido acceder, agradeceria mucho que reparas el link ya que necesitaba consultar la info de ese curso, te dejo el link que me falla
ردحذفhttp://es.debugmodeon.com/articulo/mapeando-java-objects-a-xml-o-generar-xml-desde-java