Servicios web RESTful - Guía rápida

¿Qué es la arquitectura REST?

REST significa Transferencia de Estado REpresentacional. REST es una arquitectura basada en estándares web y utiliza el protocolo HTTP. Gira en torno a recursos donde cada componente es un recurso y se accede a un recurso mediante una interfaz común utilizando métodos estándar HTTP. REST fue introducido por primera vez por Roy Fielding en 2000.

En la arquitectura REST, un servidor REST simplemente proporciona acceso a los recursos y el cliente REST accede y modifica los recursos. Aquí, cada recurso se identifica mediante URI / ID globales. REST utiliza varias representaciones para representar un recurso como texto, JSON, XML. JSON es el más popular.

Métodos HTTP

Los siguientes cuatro métodos HTTP se utilizan comúnmente en la arquitectura basada en REST.

  • GET - Proporciona acceso de solo lectura a un recurso.

  • POST - Se utiliza para crear un nuevo recurso.

  • DELETE : Se utiliza para eliminar un recurso.

  • PUT : Se utiliza para actualizar un recurso existente o crear un nuevo recurso.

Introducción a los servicios web RESTFul

Un servicio web es una colección de protocolos y estándares abiertos que se utilizan para intercambiar datos entre aplicaciones o sistemas. Las aplicaciones de software escritas en varios lenguajes de programación y que se ejecutan en varias plataformas pueden utilizar servicios web para intercambiar datos a través de redes informáticas como Internet de una manera similar a la comunicación entre procesos en una sola computadora. Esta interoperabilidad (por ejemplo, entre Java y Python, o aplicaciones de Windows y Linux) se debe al uso de estándares abiertos.

Los servicios web basados ​​en la arquitectura REST se conocen como servicios web RESTful. Estos servicios web utilizan métodos HTTP para implementar el concepto de arquitectura REST. Un servicio web RESTful generalmente define un URI, un identificador uniforme de recursos, un servicio, proporciona representación de recursos como JSON y un conjunto de métodos HTTP.

Creación de un servicio web RESTFul

En los próximos capítulos, crearemos un servicio web, por ejemplo, gestión de usuarios con las siguientes funcionalidades:

No Señor. URI Método HTTP POST cuerpo Resultado
1 / UserService / usuarios OBTENER vacío Mostrar lista de todos los usuarios.
2 / UserService / addUser ENVIAR Cadena JSON Agregue detalles del nuevo usuario.
3 / UserService / getUser /: id OBTENER vacío Muestra los detalles de un usuario.

Este tutorial lo guiará sobre cómo preparar un entorno de desarrollo para comenzar a trabajar. Jersey Frameworkpara crear servicios web RESTful. Implementos de marco de JerseyJAX-RS 2.0API, que es una especificación estándar para crear servicios web RESTful. Este tutorial también le enseñará cómo configurarJDK, Tomcat y Eclipse en su máquina antes de configurar Jersey Framework.

Configurar el kit de desarrollo de Java (JDK)

Puede descargar la última versión del SDK del sitio Java de Oracle: Descargas de Java SE . Encontrará las instrucciones para instalar JDK en los archivos descargados. Siga las instrucciones dadas para instalar y configurar la instalación. Finalmente configure elPATH y JAVA_HOME variables de entorno para hacer referencia al directorio que contiene Java y Javac, normalmente java_install_dir / bin y java_install_dir respectivamente.

Si está ejecutando Windows e instaló el JDK en C: \ jdk1.7.0_75, tendría que poner la siguiente línea en su archivo C: \ autoexec.bat.

set PATH = C:\jdk1.7.0_75\bin;%PATH% 
set JAVA_HOME = C:\jdk1.7.0_75

Alternativamente, en Windows NT / 2000 / XP, también puede hacer clic con el botón derecho en Mi PC → seleccionar Propiedades → luego Avanzado → luego Variables de entorno. Luego, actualizaría el valor PATH y presionaría el botón OK.

En Unix (Solaris, Linux, etc.), si el SDK está instalado en /usr/local/jdk1.7.0_75 y usa C Shell, pondría lo siguiente en su archivo .cshrc.

setenv PATH /usr/local/jdk1.7.0_75/bin:$PATH 
setenv JAVA_HOME /usr/local/jdk1.7.0_75

Alternativamente, si usa un entorno de desarrollo integrado (IDE) como Borland JBuilder, Eclipse, IntelliJ IDEA o Sun ONE Studio, compile y ejecute un programa simple para confirmar que el IDE sabe dónde instaló Java; de lo contrario, realice la configuración adecuada según el documento proporcionado del IDE.

Configurar Eclipse IDE

Todos los ejemplos de este tutorial se han escrito utilizando el IDE de Eclipse. Por lo tanto, le sugiero que tenga instalada la última versión de Eclipse en su máquina.

Para instalar Eclipse IDE, descargue los últimos binarios de Eclipse desde https://www.eclipse.org/downloads/. Una vez que descargó la instalación, descomprima la distribución binaria en una ubicación conveniente. Por ejemplo, en C: \ eclipse en Windows, o / usr / local / eclipse en Linux / Unix y finalmente configure la variable PATH apropiadamente.

Eclipse se puede iniciar ejecutando los siguientes comandos en una máquina con Windows, o simplemente puede hacer doble clic en eclipse.exe.

%C:\eclipse\eclipse.exe

Eclipse se puede iniciar ejecutando los siguientes comandos en una máquina Unix (Solaris, Linux, etc.):

$/usr/local/eclipse/eclipse

Después de un inicio exitoso, si todo está bien, entonces su pantalla debería mostrar el siguiente resultado:

Configurar bibliotecas de Jersey Framework

Ahora, si todo está bien, puede proceder a configurar el marco de Jersey. A continuación se muestran algunos pasos sencillos para descargar e instalar el marco en su máquina.

  • Elija si desea instalar Jersey en Windows o Unix y luego continúe con el siguiente paso para descargar el archivo .zip para Windows y luego el archivo .tz para Unix.

  • Descargue la última versión de los binarios del marco de Jersey desde el siguiente enlace: https://jersey.java.net/download.html.

  • En el momento de escribir este tutorial, descargué jaxrs-ri-2.17.zip en mi máquina con Windows y cuando descomprima el archivo descargado, le dará la estructura de directorio dentro de E: \ jaxrs-ri-2.17 \ jaxrs-ri como se muestra en la siguiente captura de pantalla.

Encontrará todas las bibliotecas de Jersey en los directorios C:\jaxrs-ri-2.17\jaxrs-ri\lib y dependencias en C:\jaxrs-ri-2.17\jaxrs-ri\ext. Asegúrese de configurar su variable CLASSPATH en este directorio correctamente, de lo contrario, tendrá problemas al ejecutar su aplicación. Si está utilizando Eclipse, no es necesario que configure CLASSPATH porque todos los ajustes se realizarán a través de Eclipse.

Configurar Apache Tomcat

Puede descargar la última versión de Tomcat desde https://tomcat.apache.org/. Una vez que descargó la instalación, descomprima la distribución binaria en una ubicación conveniente. Por ejemplo, en C: \ apache-tomcat-7.0.59 en Windows, o /usr/local/apache-tomcat-7.0.59 en Linux / Unix y establezca la variable de entorno CATALINA_HOME apuntando a las ubicaciones de instalación.

Tomcat se puede iniciar ejecutando los siguientes comandos en una máquina con Windows, o simplemente puede hacer doble clic en startup.bat.

%CATALINA_HOME%\bin\startup.bat

o

C:\apache-tomcat-7.0.59\bin\startup.bat

Tomcat se puede iniciar ejecutando los siguientes comandos en una máquina Unix (Solaris, Linux, etc.):

$CATALINA_HOME/bin/startup.sh

o

/usr/local/apache-tomcat-7.0.59/bin/startup.sh

Después de un inicio exitoso, las aplicaciones web predeterminadas incluidas con Tomcat estarán disponibles visitando http://localhost:8080/. Si todo está bien, debería mostrar el siguiente resultado:

Puede encontrar más información sobre la configuración y ejecución de Tomcat en la documentación incluida en esta página. Esta información también se puede encontrar en el sitio web de Tomcat:https://tomcat.apache.org.

Tomcat se puede detener ejecutando los siguientes comandos en una máquina con Windows:

%CATALINA_HOME%\bin\shutdown

o

C:\apache-tomcat-7.0.59\bin\shutdown

Tomcat se puede detener ejecutando los siguientes comandos en una máquina Unix (Solaris, Linux, etc.):

$CATALINA_HOME/bin/shutdown.sh

o

/usr/local/apache-tomcat-7.0.59/bin/shutdown.sh

Una vez que haya terminado con este último paso, estará listo para continuar con su primer ejemplo de Jersey, que verá en el siguiente capítulo.

Comencemos a escribir los servicios web RESTful reales con Jersey Framework. Antes de comenzar a escribir su primer ejemplo usando Jersey Framework, debe asegurarse de haber configurado su entorno Jersey correctamente como se explica en el capítulo Servicios web RESTful - Configuración del entorno . Aquí, también supongo que tiene un poco de conocimiento práctico de Eclipse IDE.

Entonces, procedamos a escribir una aplicación Jersey simple que expondrá un método de servicio web para mostrar la lista de usuarios.

Crear un proyecto Java

El primer paso es crear un proyecto web dinámico utilizando Eclipse IDE. Sigue la opciónFile → New → Project y finalmente seleccione el Dynamic Web Projectasistente de la lista de asistentes. Ahora nombre su proyecto comoUserManagement usando la ventana del asistente como se muestra en la siguiente captura de pantalla:

Una vez que su proyecto se haya creado con éxito, tendrá el siguiente contenido en su Project Explorer -

Agregar las bibliotecas necesarias

Como segundo paso, agreguemos Jersey Framework y sus dependencias (bibliotecas) en nuestro proyecto. Copie todos los archivos jar de los siguientes directorios de la carpeta zip de descarga de jersey en el directorio WEB-INF / lib del proyecto.

  • \jaxrs-ri-2.17\jaxrs-ri\api
  • \jaxrs-ri-2.17\jaxrs-ri\ext
  • \jaxrs-ri-2.17\jaxrs-ri\lib

Ahora, haga clic derecho en el nombre de su proyecto UserManagement y luego siga la opción disponible en el menú contextual - Build Path → Configure Build Path para mostrar la ventana Java Build Path.

Ahora usa Add JARs botón disponible debajo Libraries pestaña para agregar los archivos JAR presentes en el directorio WEBINF / lib.

Creando los archivos fuente

Ahora creemos los archivos fuente reales bajo el UserManagementproyecto. Primero necesitamos crear un paquete llamadocom.tutorialspoint. Para hacer esto, haga clic derecho en src en la sección del explorador de paquetes y siga la opción -New → Package.

A continuación crearemos UserService.java, User.java,UserDao.java archivos en el paquete com.tutorialspoint.

User.java

package com.tutorialspoint;  

import java.io.Serializable;  
import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlRootElement; 
@XmlRootElement(name = "user") 

public class User implements Serializable {  
   private static final long serialVersionUID = 1L; 
   private int id; 
   private String name; 
   private String profession;  
   public User(){} 
    
   public User(int id, String name, String profession){  
      this.id = id; 
      this.name = name; 
      this.profession = profession; 
   }  
   public int getId() { 
      return id; 
   }  
   @XmlElement 
   public void setId(int id) { 
      this.id = id; 
   } 
   public String getName() { 
      return name; 
   } 
   @XmlElement
   public void setName(String name) { 
      this.name = name; 
   } 
   public String getProfession() { 
      return profession; 
   } 
   @XmlElement 
   public void setProfession(String profession) { 
      this.profession = profession; 
   }   
}

UserDao.java

package com.tutorialspoint;  

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException;  
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.util.ArrayList; 
import java.util.List;  

public class UserDao { 
   public List<User> getAllUsers(){ 
      
      List<User> userList = null; 
      try { 
         File file = new File("Users.dat"); 
         if (!file.exists()) { 
            User user = new User(1, "Mahesh", "Teacher"); 
            userList = new ArrayList<User>(); 
            userList.add(user); 
            saveUserList(userList); 
         } 
         else{ 
            FileInputStream fis = new FileInputStream(file); 
            ObjectInputStream ois = new ObjectInputStream(fis); 
            userList = (List<User>) ois.readObject(); 
            ois.close(); 
         } 
      } catch (IOException e) { 
         e.printStackTrace(); 
      } catch (ClassNotFoundException e) { 
         e.printStackTrace(); 
      }   
      return userList; 
   } 
   private void saveUserList(List<User> userList){ 
      try { 
         File file = new File("Users.dat"); 
         FileOutputStream fos;  
         fos = new FileOutputStream(file); 
         ObjectOutputStream oos = new ObjectOutputStream(fos); 
         oos.writeObject(userList); 
         oos.close(); 
      } catch (FileNotFoundException e) { 
         e.printStackTrace(); 
      } catch (IOException e) { 
         e.printStackTrace(); 
      } 
   }    
}

UserService.java

package com.tutorialspoint;  

import java.util.List; 
import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 
import javax.ws.rs.core.MediaType;  
@Path("/UserService") 

public class UserService {  
   UserDao userDao = new UserDao();  
   @GET 
   @Path("/users") 
   @Produces(MediaType.APPLICATION_XML) 
   public List<User> getUsers(){ 
      return userDao.getAllUsers(); 
   }  
}

Hay dos puntos importantes que deben tenerse en cuenta sobre el programa principal,

UserService.java

  • El primer paso es especificar una ruta para el servicio web usando la anotación @Path al UserService.

  • El segundo paso es especificar una ruta para el método de servicio web particular usando la anotación @Path al método de UserService.

Creación del archivo de configuración Web.xml

Debe crear un archivo de configuración Web xml, que es un archivo XML y se utiliza para especificar el servlet de marco de Jersey para nuestra aplicación.

web.xml

<?xml version = "1.0" encoding = "UTF-8"?> 
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"  
   xmlns = "http://java.sun.com/xml/ns/javaee"  
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
   id = "WebApp_ID" version = "3.0"> 
   <display-name>User Management</display-name> 
   <servlet> 
      <servlet-name>Jersey RESTful Application</servlet-name> 
      <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> 
      <init-param> 
         <param-name>jersey.config.server.provider.packages</param-name> 
         <param-value>com.tutorialspoint</param-value> 
      </init-param> 
   </servlet> 
   <servlet-mapping> 
      <servlet-name>Jersey RESTful Application</servlet-name> 
      <url-pattern>/rest/*</url-pattern> 
   </servlet-mapping>   
</web-app>

Implementar el programa

Una vez que haya terminado con la creación de los archivos de configuración web y de origen, estará listo para este paso que consiste en compilar y ejecutar su programa. Para hacer esto, usando Eclipse, exporte su aplicación como un archivo war e impleméntelo en tomcat.

Para crear un archivo WAR usando eclipse, siga la opción File → export → Web → War Filey finalmente seleccione el proyecto UserManagement y la carpeta de destino. Para implementar un archivo war en Tomcat, coloque UserManagement.war en elTomcat Installation Directory → webapps directory e inicie el Tomcat.

Ejecutando el programa

Estamos usando Postman , una extensión de Chrome, para probar nuestros servicios web.

Haga una solicitud a UserManagement para obtener una lista de todos los usuarios. Coloque http: // localhost: 8080 / UserManagement / rest / UserService / users en POSTMAN con la solicitud GET y vea el siguiente resultado.

Felicitaciones, ha creado su primera aplicación RESTful con éxito.

¿Qué es un recurso?

La arquitectura REST trata cada contenido como un recurso. Estos recursos pueden ser archivos de texto, páginas HTML, imágenes, videos o datos comerciales dinámicos. El servidor REST simplemente proporciona acceso a los recursos y el cliente REST accede y modifica los recursos. Aquí, cada recurso se identifica mediante URI / ID globales. REST usa varias representaciones para representar un recurso donde Text, JSON, XML. Las representaciones de recursos más populares son XML y JSON.

Representación de recursos

Un recurso en REST es un objeto similar en la programación orientada a objetos o es como una entidad en una base de datos. Una vez que se identifica un recurso, su representación debe decidirse utilizando un formato estándar para que el servidor pueda enviar el recurso en el formato mencionado anteriormente y el cliente pueda entender el mismo formato.

Por ejemplo, en el capítulo Servicios web RESTful - Primera aplicación , un usuario es un recurso que se representa mediante el siguiente formato XML:

<user> 
   <id>1</id> 
   <name>Mahesh</name>
   <profession>Teacher</profession> 
</user>

El mismo recurso se puede representar en formato JSON de la siguiente manera:

{ 
   "id":1, 
   "name":"Mahesh", 
   "profession":"Teacher" 
}

Buena representación de recursos

REST no impone ninguna restricción sobre el formato de la representación de un recurso. Un cliente puede solicitar una representación JSON, mientras que otro cliente puede solicitar una representación XML del mismo recurso al servidor, etc. Es responsabilidad del servidor REST pasar al cliente el recurso en el formato que el cliente comprenda.

A continuación se presentan algunos puntos importantes que deben tenerse en cuenta al diseñar un formato de representación de un recurso en los servicios web RESTful.

  • Understandability - Tanto el servidor como el cliente deben poder comprender y utilizar el formato de representación del recurso.

  • Completeness- El formato debe poder representar un recurso por completo. Por ejemplo, un recurso puede contener otro recurso. El formato debe poder representar estructuras de recursos simples y complejas.

  • Linkablity - Un recurso puede tener un vínculo con otro recurso, un formato debe poder manejar tales situaciones.

Sin embargo, en la actualidad, la mayoría de los servicios web representan recursos utilizando formato XML o JSON. Hay muchas bibliotecas y herramientas disponibles para comprender, analizar y modificar datos XML y JSON.

Los servicios web RESTful utilizan protocolos HTTP como medio de comunicación entre el cliente y el servidor. Un cliente envía un mensaje en forma de solicitud HTTP y el servidor responde en forma de respuesta HTTP. Esta técnica se denomina mensajería. Estos mensajes contienen datos y metadatos del mensaje, es decir, información sobre el mensaje en sí. Echemos un vistazo a los mensajes HTTP Request y HTTP Response para HTTP 1.1.

Solicitud HTTP

Una solicitud HTTP tiene cinco partes principales:

  • Verb - Indica los métodos HTTP como GET, POST, DELETE, PUT, etc.

  • URI - Identificador uniforme de recursos (URI) para identificar el recurso en el servidor.

  • HTTP Version- Indica la versión HTTP. Por ejemplo, HTTP v1.1.

  • Request Header- Contiene metadatos para el mensaje de solicitud HTTP como pares clave-valor. Por ejemplo, tipo de cliente (o navegador), formato admitido por el cliente, formato del cuerpo del mensaje, configuración de la caché, etc.

  • Request Body - Contenido del mensaje o representación de recursos.

Respuesta HTTP

Una respuesta HTTP tiene cuatro partes principales:

  • Status/Response Code- Indica el estado del servidor para el recurso solicitado. Por ejemplo, 404 significa recurso no encontrado y 200 significa que la respuesta es correcta.

  • HTTP Version- Indica la versión HTTP. Por ejemplo, HTTP v1.1.

  • Response Header- Contiene metadatos para el mensaje de respuesta HTTP como pares de valores clave. Por ejemplo, longitud del contenido, tipo de contenido, fecha de respuesta, tipo de servidor, etc.

  • Response Body - Contenido del mensaje de respuesta o representación del recurso.

Ejemplo

Como hemos explicado en el capítulo Servicios web RESTful - Primera aplicación , pongamos http: // localhost: 8080 / UserManagement / rest / UserService / users en el POSTMAN con una solicitud GET. Si hace clic en el botón Vista previa que está cerca del botón enviar de Postman y luego hace clic en el botón Enviar, es posible que vea la siguiente salida.

Aquí puede ver, el navegador envió una solicitud GET y recibió un cuerpo de respuesta como XML.

El direccionamiento se refiere a ubicar un recurso o varios recursos que se encuentran en el servidor. Es análogo localizar la dirección postal de una persona.

Cada recurso en la arquitectura REST se identifica por su URI (Identificador uniforme de recursos). Un URI tiene el siguiente formato:

<protocol>://<service-name>/<ResourceType>/<ResourceID>

El propósito de un URI es ubicar un recurso (s) en el servidor que aloja el servicio web. Otro atributo importante de una solicitud es VERBO que identifica la operación que se realizará en el recurso. Por ejemplo, en el capítulo Servicios web RESTful - Primera aplicación , el URI eshttp://localhost:8080/UserManagement/rest/UserService/users y el VERBO es OBTENER.

Construyendo un URI estándar

Los siguientes son puntos importantes que deben tenerse en cuenta al diseñar un URI:

  • Use Plural Noun- Usar sustantivo plural para definir recursos. Por ejemplo, hemos utilizado usuarios para identificar a los usuarios como un recurso.

  • Avoid using spaces- Utilice guión bajo (_) o guión (-) cuando utilice un nombre de recurso largo. Por ejemplo, utilice usuarios_autorizados en lugar de% 20usuarios autorizados.

  • Use lowercase letters - Aunque URI no distingue entre mayúsculas y minúsculas, es una buena práctica mantener la URL solo en minúsculas.

  • Maintain Backward Compatibility- Dado que el servicio web es un servicio público, una URI, una vez que se haga pública, siempre debería estar disponible. En caso de que el URI se actualice, redirija el URI anterior a un nuevo URI utilizando el código de estado HTTP, 300.

  • Use HTTP Verb- Utilice siempre verbos HTTP como GET, PUT y DELETE para realizar las operaciones en el recurso. No es bueno usar el nombre de las operaciones en el URI.

Ejemplo

A continuación, se muestra un ejemplo de un URI deficiente para recuperar un usuario.

http://localhost:8080/UserManagement/rest/UserService/getUser/1

A continuación se muestra un ejemplo de un buen URI para buscar un usuario.

http://localhost:8080/UserManagement/rest/UserService/users/1

Como hemos comentado en los capítulos anteriores, el servicio web RESTful utiliza muchos verbos HTTP para determinar la operación que se llevará a cabo en los recursos especificados. La siguiente tabla muestra los ejemplos de los verbos HTTP más utilizados.

No Señor. Método HTTP, URI y operación

1

GET

http: // localhost: 8080 / UserManagement / rest / UserService / users

Obtiene la lista de usuarios.

(Solo lectura)

2

GET

http: // localhost: 8080 / UserManagement / rest / UserService / users / 1

Obtiene el usuario de Id 1

(Solo lectura)

3

PUT

http: // localhost: 8080 / UserManagement / rest / UserService / users / 2

Inserta usuario con Id 2

(Idempotente)

4

POST

http: // localhost: 8080 / UserManagement / rest / UserService / users / 2

Actualiza al usuario con Id 2

(N / A)

5

DELETE

http: // localhost: 8080 / UserManagement / rest / UserService / users / 1

Elimina el usuario con Id 1

(Idempotente)

6

OPTIONS

http: // localhost: 8080 / UserManagement / rest / UserService / users

Muestra las operaciones admitidas en un servicio web.

(Solo lectura)

7

HEAD

http: // localhost: 8080 / UserManagement / rest / UserService / users

Devuelve solo el encabezado HTTP, no el cuerpo.

(Solo lectura)

Se deben considerar los siguientes puntos.

  • Las operaciones GET son de solo lectura y seguras.

  • Las operaciones PUT y DELETE son idempotentes, lo que significa que su resultado siempre será el mismo, sin importar cuántas veces se invoquen estas operaciones.

  • Las operaciones PUT y POST son casi iguales, con la diferencia solo en el resultado donde la operación PUT es idempotente y la operación POST puede causar un resultado diferente.

Ejemplo

Actualicemos un ejemplo creado en el capítulo Servicios web RESTful - Primera aplicación para crear un servicio web que pueda realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar). Para simplificar, hemos utilizado una E / S de archivo para reemplazar las operaciones de la base de datos.

Actualicemos el User.java, UserDao.java y UserService.java archivos en el paquete com.tutorialspoint.

User.java

package com.tutorialspoint; 

import java.io.Serializable;  
import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlRootElement; 
@XmlRootElement(name = "user") 

public class User implements Serializable {  
   private static final long serialVersionUID = 1L; 
   private int id; 
   private String name; 
   private String profession;  
   public User(){}  
   
   public User(int id, String name, String profession){ 
      this.id = id; 
      this.name = name; 
      this.profession = profession; 
   }  
    
   public int getId() {
      return id; 
   } 
   @XmlElement 
   public void setId(int id) { 
      this.id = id; 
   } 
   public String getName() { 
      return name; 
   } 
   @XmlElement 
      public void setName(String name) { 
      this.name = name; 
   } 
   public String getProfession() { 
      return profession; 
   } 
   @XmlElement 
   public void setProfession(String profession) { 
      this.profession = profession; 
   }   
   @Override 
   public boolean equals(Object object){ 
      if(object == null){ 
         return false; 
      }else if(!(object instanceof User)){ 
         return false; 
      }else { 
         User user = (User)object; 
         if(id == user.getId() 
            && name.equals(user.getName()) 
            && profession.equals(user.getProfession())){ 
               return true; 
         }
      } 
      return false; 
   }  
}

UserDao.java

package com.tutorialspoint;  

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.util.ArrayList; 
import java.util.List;  

public class UserDao { 
   public List<User> getAllUsers(){ 
      List<User> userList = null; 
      try { 
         File file = new File("Users.dat"); 
         if (!file.exists()) { 
            User user = new User(1, "Mahesh", "Teacher"); 
            userList = new ArrayList<User>(); 
            userList.add(user); 
            saveUserList(userList);   
         } 
         else{ 
            FileInputStream fis = new FileInputStream(file); 
            ObjectInputStream ois = new ObjectInputStream(fis); 
            userList = (List<User>) ois.readObject(); 
            ois.close(); 
         }
      } catch (IOException e) { 
         e.printStackTrace(); 
      } catch (ClassNotFoundException e) { 
         e.printStackTrace(); 
      }   
      return userList; 
   }  
   public User getUser(int id){ 
      List<User> users = getAllUsers();  
      for(User user: users){ 
         if(user.getId() == id){ 
            return user; 
         } 
      } 
      return null; 
   }  
   public int addUser(User pUser){ 
      List<User> userList = getAllUsers(); 
      boolean userExists = false; 
      for(User user: userList){ 
         if(user.getId() == pUser.getId()){ 
            userExists = true; 
            break; 
         } 
      }   
      if(!userExists){ 
         userList.add(pUser); 
         saveUserList(userList); 
         return 1; 
      } 
      return 0; 
   }
   public int updateUser(User pUser){ 
      List<User> userList = getAllUsers();  
      for(User user: userList){ 
         if(user.getId() == pUser.getId()){ 
            int index = userList.indexOf(user);    
            userList.set(index, pUser); 
            saveUserList(userList); 
            return 1; 
         } 
      }   
      return 0; 
   }  
   public int deleteUser(int id){ 
      List<User> userList = getAllUsers();  
      for(User user: userList){ 
         if(user.getId() == id){ 
            int index = userList.indexOf(user);    
            userList.remove(index); 
            saveUserList(userList); 
            return 1;    
         } 
      }   
      return 0; 
   }  
   private void saveUserList(List<User> userList){ 
      try { 
         File file = new File("Users.dat"); 
         FileOutputStream fos;  
         fos = new FileOutputStream(file);
         ObjectOutputStream oos = new ObjectOutputStream(fos);   
         oos.writeObject(userList); 
         oos.close(); 
      } catch (FileNotFoundException e) { 
         e.printStackTrace(); 
      } catch (IOException e) { 
         e.printStackTrace(); 
      } 
   } 
}

UserService.java

package com.tutorialspoint;  

import java.io.IOException; 
import java.util.List;  
import javax.servlet.http.HttpServletResponse; 
import javax.ws.rs.Consumes; 
import javax.ws.rs.DELETE; 
import javax.ws.rs.FormParam; 
import javax.ws.rs.GET; 
import javax.ws.rs.OPTIONS; 
import javax.ws.rs.POST; 
import javax.ws.rs.PUT; 
import javax.ws.rs.Path; 
import javax.ws.rs.PathParam; 
import javax.ws.rs.Produces; 
import javax.ws.rs.core.Context; 
import javax.ws.rs.core.MediaType;  
@Path("/UserService") 

public class UserService { 
  
   UserDao userDao = new UserDao(); 
   private static final String SUCCESS_RESULT = "<result>success</result>"; 
   private static final String FAILURE_RESULT = "<result>failure</result>";  
   @GET 
   @Path("/users") 
   @Produces(MediaType.APPLICATION_XML) 
   public List<User> getUsers(){ 
      return userDao.getAllUsers(); 
   }  
   @GET 
   @Path("/users/{userid}") 
   @Produces(MediaType.APPLICATION_XML) 
   public User getUser(@PathParam("userid") int userid){ 
      return userDao.getUser(userid); 
   }  
   @PUT 
   @Path("/users") 
   @Produces(MediaType.APPLICATION_XML) 
   @Consumes(MediaType.APPLICATION_FORM_URLENCODED) 
   public String createUser(@FormParam("id") int id, 
      @FormParam("name") String name, 
      @FormParam("profession") String profession, 
      @Context HttpServletResponse servletResponse) throws IOException{ 
      User user = new User(id, name, profession); 
      int result = userDao.addUser(user); 
      if(result == 1){ 
         return SUCCESS_RESULT; 
      } 
      return FAILURE_RESULT; 
   }  
   @POST 
   @Path("/users")  
   @Produces(MediaType.APPLICATION_XML)
   @Consumes(MediaType.APPLICATION_FORM_URLENCODED) 
   public String updateUser(@FormParam("id") int id, 
      @FormParam("name") String name, 
      @FormParam("profession") String profession, 
      @Context HttpServletResponse servletResponse) throws IOException{ 
      User user = new User(id, name, profession); 
      int result = userDao.updateUser(user); 
      if(result == 1){ 
         return SUCCESS_RESULT; 
      } 
      return FAILURE_RESULT; 
   }  
   @DELETE 
   @Path("/users/{userid}") 
   @Produces(MediaType.APPLICATION_XML) 
   public String deleteUser(@PathParam("userid") int userid){ 
      int result = userDao.deleteUser(userid); 
      if(result == 1){ 
         return SUCCESS_RESULT; 
      } 
      return FAILURE_RESULT; 
   }  
   @OPTIONS 
   @Path("/users") 
   @Produces(MediaType.APPLICATION_XML) 
   public String getSupportedOperations(){ 
      return "<operations>GET, PUT, POST, DELETE</operations>"; 
   } 
}

Ahora usando Eclipse, exporte su aplicación como WAR Filee implementar el mismo en Tomcat. Para crear un archivo WAR usando eclipse, siga esta ruta:File → export → Web → War Filey finalmente seleccione el proyecto UserManagement y la carpeta de destino. Para implementar un archivo WAR en Tomcat, coloque UserManagement.war en elTomcat Installation Directory → webapps directorio y el inicio Tomcat.

Prueba del servicio web

Jersey proporciona API para crear un cliente de servicio web para probar servicios web. Hemos creado una clase de prueba de muestra.WebServiceTester.java en el paquete com.tutorialspoint en el mismo proyecto.

WebServiceTester.java

package com.tutorialspoint;  

import java.util.List; 
import javax.ws.rs.client.Client; 
import javax.ws.rs.client.ClientBuilder; 
import javax.ws.rs.client.Entity; 
import javax.ws.rs.core.Form; 
import javax.ws.rs.core.GenericType; 
import javax.ws.rs.core.MediaType;  

public class WebServiceTester  {  
   private Client client; 
   private String REST_SERVICE_URL = "
   http://localhost:8080/UserManagement/rest/UserService/users"; 
   private static final String SUCCESS_RESULT = "<result>success</result>"; 
   private static final String PASS = "pass"; 
   private static final String FAIL = "fail";  
   private void init(){ 
      this.client = ClientBuilder.newClient(); 
   }  
   public static void main(String[] args){ 
      WebServiceTester tester = new WebServiceTester(); 
      //initialize the tester 
      tester.init(); 
      //test get all users Web Service Method 
      tester.testGetAllUsers(); 
      //test get user Web Service Method  
      tester.testGetUser();
      //test update user Web Service Method 
      tester.testUpdateUser(); 
      //test add user Web Service Method 
      tester.testAddUser(); 
      //test delete user Web Service Method 
      tester.testDeleteUser(); 
   } 
   //Test: Get list of all users 
   //Test: Check if list is not empty 
   private void testGetAllUsers(){ 
      GenericType<List<User>> list = new GenericType<List<User>>() {}; 
      List<User> users = client 
         .target(REST_SERVICE_URL) 
         .request(MediaType.APPLICATION_XML) 
         .get(list); 
      String result = PASS; 
      if(users.isEmpty()){ 
         result = FAIL; 
      } 
      System.out.println("Test case name: testGetAllUsers, Result: " + result ); 
   } 
   //Test: Get User of id 1 
   //Test: Check if user is same as sample user 
   private void testGetUser(){ 
      User sampleUser = new User(); 
      sampleUser.setId(1);  
      User user = client 
         .target(REST_SERVICE_URL) 
         .path("/{userid}") 
         .resolveTemplate("userid", 1) 
         .request(MediaType.APPLICATION_XML) 
         .get(User.class); 
      String result = FAIL; 
      if(sampleUser != null && sampleUser.getId() == user.getId()){
         result = PASS; 
      } 
      System.out.println("Test case name: testGetUser, Result: " + result ); 
   } 
   //Test: Update User of id 1 
   //Test: Check if result is success XML. 
   private void testUpdateUser(){ 
      Form form = new Form(); 
      form.param("id", "1"); 
      form.param("name", "suresh"); 
      form.param("profession", "clerk");  
      String callResult = client 
         .target(REST_SERVICE_URL) 
         .request(MediaType.APPLICATION_XML) 
         .post(Entity.entity(form, 
         MediaType.APPLICATION_FORM_URLENCODED_TYPE), 
         String.class); 
      String result = PASS; 
      if(!SUCCESS_RESULT.equals(callResult)){ 
         result = FAIL; 
      }  
      System.out.println("Test case name: testUpdateUser, Result: " + result); 
   } 
   //Test: Add User of id 2 
   //Test: Check if result is success XML. 
   private void testAddUser(){ 
      Form form = new Form(); 
      form.param("id", "2"); 
      form.param("name", "naresh"); 
      form.param("profession", "clerk");  
      String callResult = client 
         .target(REST_SERVICE_URL) 
         .request(MediaType.APPLICATION_XML) 
         .put(Entity.entity(form, 
         MediaType.APPLICATION_FORM_URLENCODED_TYPE), 
         String.class); 
    
      String result = PASS; 
      if(!SUCCESS_RESULT.equals(callResult)){ 
         result = FAIL; 
      }  
      System.out.println("Test case name: testAddUser, Result: " + result ); 
   } 
   //Test: Delete User of id 2 
   //Test: Check if result is success XML. 
   private void testDeleteUser(){ 
      String callResult = client 
         .target(REST_SERVICE_URL) 
         .path("/{userid}") 
         .resolveTemplate("userid", 2) 
         .request(MediaType.APPLICATION_XML) 
         .delete(String.class);  
      String result = PASS; 
      if(!SUCCESS_RESULT.equals(callResult)){ 
         result = FAIL; 
      } 
      System.out.println("Test case name: testDeleteUser, Result: " + result); 
   } 
}

Ahora ejecute el probador con Eclipse. Haga clic derecho en el archivo y siga la opciónRun as → Java Application. Verá el siguiente resultado en la consola de Eclipse:

Test case name: testGetAllUsers, Result: pass 
Test case name: testGetUser, Result: pass 
Test case name: testUpdateUser, Result: pass 
Test case name: testAddUser, Result: pass 
Test case name: testDeleteUser, Result: pass

Según la arquitectura REST, un servicio web RESTful no debe mantener un estado de cliente en el servidor. Esta restricción se llama apatridia. Es responsabilidad del cliente pasar su contexto al servidor y luego el servidor puede almacenar este contexto para procesar la solicitud adicional del cliente. Por ejemplo, la sesión mantenida por el servidor se identifica mediante el identificador de sesión pasado por el cliente.

Los servicios web RESTful deben cumplir con esta restricción. Hemos visto esto en el capítulo Servicios web RESTful - Métodos , que los métodos del servicio web no almacenan ninguna información del cliente desde el que se invocan.

Consider the following URL −

https: // localhost: 8080 / UserManagement / rest / UserService / users / 1

Si presiona la URL anterior usando su navegador o usando un cliente basado en java o usando Postman, el resultado siempre será el XML de usuario cuyo Id es 1 porque el servidor no almacena ninguna información sobre el cliente.

<user> 
   <id>1</id> 
   <name>mahesh</name> 
   <profession>1</profession> 
</user>

Ventajas de la apatridia

Los siguientes son los beneficios de la apatridia en los servicios web RESTful:

  • Los servicios web pueden tratar cada solicitud de método de forma independiente.

  • Los servicios web no necesitan mantener las interacciones previas del cliente. Simplifica el diseño de la aplicación.

  • Como HTTP es en sí mismo un protocolo de ausencia de estado, los servicios web RESTful funcionan a la perfección con los protocolos HTTP.

Desventajas de la apatridia

Las siguientes son las desventajas de la apatridia en los servicios web RESTful:

  • Los servicios web necesitan obtener información adicional en cada solicitud y luego interpretar para obtener el estado del cliente en caso de que se deban atender las interacciones del cliente.

El almacenamiento en caché se refiere al almacenamiento de la respuesta del servidor en el propio cliente, de modo que un cliente no necesita realizar una solicitud al servidor para el mismo recurso una y otra vez. Una respuesta del servidor debe tener información sobre cómo se debe realizar el almacenamiento en caché, de modo que un cliente almacene en caché la respuesta durante un período de tiempo o nunca almacene en caché la respuesta del servidor.

A continuación se muestran los encabezados que puede tener una respuesta del servidor para configurar el almacenamiento en caché de un cliente:

No Señor. Encabezado y descripción

1

Date

Fecha y hora del recurso cuando se creó.

2

Last Modified

Fecha y hora del recurso cuando se modificó por última vez.

3

Cache-Control

Encabezado principal para controlar el almacenamiento en caché.

4

Expires

Fecha de vencimiento y hora de almacenamiento en caché.

5

Age

Duración en segundos desde que se obtuvo el recurso del servidor.

Encabezado de control de caché

A continuación se muestran los detalles de un encabezado de Cache-Control:

No Señor. Directiva y descripción

1

Public

Indica que cualquier componente puede almacenar en caché el recurso.

2

Private

Indica que el recurso se puede almacenar en caché solo por el cliente y el servidor, ningún intermediario puede almacenar en caché el recurso.

3

no-cache/no-store

Indica que un recurso no se puede almacenar en caché.

4

max-age

Indica que el almacenamiento en caché es válido hasta la edad máxima en segundos. Después de esto, el cliente debe realizar otra solicitud.

5

must-revalidate

Indicación al servidor para revalidar el recurso si ha pasado la edad máxima.

Mejores prácticas

  • Mantenga siempre los contenidos estáticos como imágenes, CSS, JavaScript en caché, con una fecha de vencimiento de 2 a 3 días.

  • Nunca mantenga la fecha de caducidad demasiado alta.

  • El contenido dinámico debe almacenarse en caché solo durante unas horas.

Dado que los servicios web RESTful funcionan con rutas de URL HTTP, es muy importante proteger un servicio web RESTful de la misma manera que se protege un sitio web.

Las siguientes son las mejores prácticas que se deben seguir al diseñar un servicio web RESTful:

  • Validation- Validar todas las entradas en el servidor. Proteja su servidor contra ataques de inyección SQL o NoSQL.

  • Session Based Authentication - Utilice la autenticación basada en sesión para autenticar a un usuario siempre que se realice una solicitud a un método de servicio web.

  • No Sensitive Data in the URL - Nunca use nombre de usuario, contraseña o token de sesión en una URL, estos valores deben pasarse al Servicio Web a través del método POST.

  • Restriction on Method Execution- Permitir el uso restringido de métodos como los métodos GET, POST y DELETE. El método GET no debería poder eliminar datos.

  • Validate Malformed XML/JSON - Verifique que la entrada con el formato correcto se haya pasado a un método de servicio web.

  • Throw generic Error Messages - Un método de servicio web debe usar mensajes de error HTTP como 403 para mostrar acceso prohibido, etc.

Código HTTP

No Señor. Código HTTP y descripción

1

200

OK - muestra éxito.

2

201

CREATED- cuando un recurso se crea correctamente mediante la solicitud POST o PUT. Devuelve el enlace al recurso recién creado utilizando el encabezado de ubicación.

3

204

NO CONTENT- cuando el cuerpo de respuesta está vacío. Por ejemplo, una solicitud DELETE.

4

304

NOT MODIFIED: Se utiliza para reducir el uso del ancho de banda de la red en caso de solicitudes GET condicionales. El cuerpo de respuesta debe estar vacío. Los encabezados deben tener fecha, ubicación, etc.

5

400

BAD REQUEST- indica que se proporciona una entrada no válida. Por ejemplo, error de validación, datos faltantes.

6

401

UNAUTHORIZED : Indica que el usuario está utilizando un token de autenticación incorrecto o no válido.

7

403

FORBIDDEN- indica que el usuario no tiene acceso al método que se está utilizando. Por ejemplo, Eliminar acceso sin derechos de administrador.

8

404

NOT FOUND - declara que el método no está disponible.

9

409

CONFLICT- declara una situación de conflicto al ejecutar el método. Por ejemplo, agregar una entrada duplicada.

10

500

INTERNAL SERVER ERROR - indica que el servidor ha lanzado alguna excepción al ejecutar el método.

JAX-RSson las siglas de JAVA API para RESTful Web Services. JAX-RS es una especificación y API de lenguaje de programación basada en JAVA para brindar soporte a los servicios web RESTful creados. Su versión 2.0 fue lanzada el 24 de mayo de 2013. JAX-RS utiliza anotaciones disponibles en Java SE 5 para simplificar el desarrollo de la creación e implementación de servicios web basados ​​en JAVA. También proporciona soporte para la creación de clientes para servicios web RESTful.

Especificaciones

A continuación se muestran las anotaciones más utilizadas para mapear un recurso como recurso de servicio web.

No Señor. Anotación y descripción

1

@Path

Ruta relativa de la clase / método de recurso.

2

@GET

Solicitud HTTP Get, utilizada para recuperar recursos.

3

@PUT

Solicitud HTTP PUT, utilizada para actualizar el recurso.

4

@POST

Solicitud HTTP POST, utilizada para crear un nuevo recurso.

5

@DELETE

Solicitud HTTP DELETE, utilizada para eliminar un recurso.

6

@HEAD

Solicitud HTTP HEAD, utilizada para obtener el estado de disponibilidad del método.

7

@Produces

Indica la respuesta HTTP generada por el servicio web. Por ejemplo, APLICACIÓN / XML, TEXTO / HTML, APLICACIÓN / JSON, etc.

8

@Consumes

Indica el tipo de solicitud HTTP. Por ejemplo, application / x-www-formurlencoded para aceptar datos de formulario en el cuerpo HTTP durante la solicitud POST.

9

@PathParam

Vincula el parámetro pasado al método a un valor en la ruta.

10

@QueryParam

Vincula el parámetro pasado al método a un parámetro de consulta en la ruta.

11

@MatrixParam

Vincula el parámetro pasado al método a un parámetro de matriz HTTP en la ruta.

12

@HeaderParam

Vincula el parámetro pasado al método a un encabezado HTTP.

13

@CookieParam

Vincula el parámetro pasado al método a una cookie.

14

@FormParam

Vincula el parámetro pasado al método a un valor de formulario.

15

@DefaultValue

Asigna un valor predeterminado a un parámetro pasado al método.

dieciséis

@Context

Contexto del recurso. Por ejemplo, HTTPRequest como contexto.

Note- Hemos utilizado Jersey, una implementación de referencia de JAX-RS 2.0 de Oracle, en los capítulos Servicios web RESTful - Primera aplicación y Servicios web RESTful - Métodos .