RESTful Web Services - Guia rápido

O que é arquitetura REST?

REST significa Transferência de Estado Representacional. REST é uma arquitetura baseada em padrões da web e usa protocolo HTTP. Ele gira em torno de recursos em que cada componente é um recurso e um recurso é acessado por uma interface comum usando métodos padrão HTTP. O REST foi introduzido pela primeira vez por Roy Fielding em 2000.

Na arquitetura REST, um servidor REST simplesmente fornece acesso aos recursos e o cliente REST acessa e modifica os recursos. Aqui, cada recurso é identificado por URIs / IDs globais. REST usa várias representações para representar um recurso como texto, JSON, XML. JSON é o mais popular.

Métodos HTTP

Os quatro métodos HTTP a seguir são comumente usados ​​na arquitetura baseada em REST.

  • GET - Fornece acesso somente leitura a um recurso.

  • POST - Usado para criar um novo recurso.

  • DELETE - Usado para remover um recurso.

  • PUT - Usado para atualizar um recurso existente ou criar um novo recurso.

Introdução aos serviços da web RESTFul

Um serviço da web é uma coleção de protocolos e padrões abertos usados ​​para trocar dados entre aplicativos ou sistemas. Os aplicativos de software escritos em várias linguagens de programação e em execução em várias plataformas podem usar serviços da web para trocar dados em redes de computadores como a Internet de maneira semelhante à comunicação entre processos em um único computador. Essa interoperabilidade (por exemplo, entre aplicativos Java e Python ou Windows e Linux) se deve ao uso de padrões abertos.

Os serviços da Web baseados na arquitetura REST são conhecidos como serviços da Web RESTful. Esses serviços da web usam métodos HTTP para implementar o conceito de arquitetura REST. Um serviço da web RESTful geralmente define um URI, Uniform Resource Identifier um serviço, fornece representação de recursos como JSON e um conjunto de métodos HTTP.

Criação de serviço da Web RESTFul

Nos próximos capítulos, criaremos um serviço da web, digamos, gerenciamento de usuários com as seguintes funcionalidades -

Sr. Não. URI Método HTTP POST body Resultado
1 / UserService / users PEGUE vazio Mostra a lista de todos os usuários.
2 / UserService / addUser POSTAR String JSON Adicione detalhes do novo usuário.
3 / UserService / getUser /: id PEGUE vazio Mostra os detalhes de um usuário.

Este tutorial irá guiá-lo sobre como preparar um ambiente de desenvolvimento para começar seu trabalho com Jersey Frameworkpara criar serviços da Web RESTful. Implementos de estrutura de JerseyJAX-RS 2.0API, que é uma especificação padrão para criar serviços da Web RESTful. Este tutorial também lhe ensinará como configurarJDK, Tomcat e Eclipse em sua máquina antes de configurar o Jersey Framework.

Configure o Java Development Kit (JDK)

Você pode baixar a versão mais recente do SDK no site Java da Oracle - Java SE Downloads . Você encontrará as instruções para instalar o JDK nos arquivos baixados. Siga as instruções fornecidas para instalar e configurar a configuração. Finalmente defina oPATH e JAVA_HOME variáveis ​​de ambiente para se referir ao diretório que contém Java e Javac, normalmente java_install_dir / bin e java_install_dir respectivamente.

Se você estiver executando o Windows e instalou o JDK em C: \ jdk1.7.0_75, deverá inserir a seguinte linha em seu arquivo C: \ autoexec.bat.

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

Como alternativa, no Windows NT / 2000 / XP, você também pode clicar com o botão direito do mouse em Meu Computador → selecionar Propriedades → Avançado → Variáveis ​​de Ambiente. Em seguida, você atualizaria o valor de PATH e pressionaria o botão OK.

No Unix (Solaris, Linux, etc.), se o SDK estiver instalado em /usr/local/jdk1.7.0_75 e você usar o C Shell, você deve colocar o seguinte em seu arquivo .cshrc.

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

Alternativamente, se você usar um Ambiente de Desenvolvimento Integrado (IDE) como Borland JBuilder, Eclipse, IntelliJ IDEA ou Sun ONE Studio, compile e execute um programa simples para confirmar que o IDE sabe onde você instalou o Java, caso contrário, faça a configuração adequada conforme o documento fornecido do IDE.

Configurar IDE Eclipse

Todos os exemplos neste tutorial foram escritos usando o IDE Eclipse. Portanto, sugiro que você tenha a versão mais recente do Eclipse instalada em sua máquina.

Para instalar o Eclipse IDE, baixe os binários mais recentes do Eclipse em https://www.eclipse.org/downloads/. Depois de fazer o download da instalação, descompacte a distribuição binária em um local conveniente. Por exemplo, em C: \ eclipse no windows, ou / usr / local / eclipse no Linux / Unix e finalmente defina a variável PATH apropriadamente.

O Eclipse pode ser iniciado executando os seguintes comandos em uma máquina Windows ou você pode simplesmente clicar duas vezes em eclipse.exe.

%C:\eclipse\eclipse.exe

O Eclipse pode ser iniciado executando os seguintes comandos na máquina Unix (Solaris, Linux, etc.) -

$/usr/local/eclipse/eclipse

Após uma inicialização bem-sucedida, se tudo estiver bem, sua tela deverá exibir o seguinte resultado -

Configurar bibliotecas do Jersey Framework

Agora, se tudo estiver bem, você pode prosseguir com a configuração da estrutura Jersey. A seguir estão algumas etapas simples para baixar e instalar a estrutura em sua máquina.

  • Escolha se deseja instalar o Jersey no Windows ou Unix e prossiga para a próxima etapa para baixar o arquivo .zip para Windows e, em seguida, o arquivo .tz para Unix.

  • Baixe a versão mais recente dos binários do framework Jersey a partir do seguinte link - https://jersey.java.net/download.html.

  • No momento em que escrevi este tutorial, eu baixei jaxrs-ri-2.17.zip na minha máquina Windows e quando você descompacta o arquivo baixado, ele mostra a estrutura de diretórios dentro de E: \ jaxrs-ri-2.17 \ jaxrs-ri como mostrado na imagem a seguir.

Você encontrará todas as bibliotecas de Jersey nos diretórios C:\jaxrs-ri-2.17\jaxrs-ri\lib e dependências em C:\jaxrs-ri-2.17\jaxrs-ri\ext. Certifique-se de definir sua variável CLASSPATH neste diretório corretamente, caso contrário, você terá problemas ao executar seu aplicativo. Se você estiver usando o Eclipse, não é necessário definir o CLASSPATH porque todas as configurações serão feitas por meio do Eclipse.

Configurar Apache Tomcat

Você pode baixar a última versão do Tomcat em https://tomcat.apache.org/. Depois de baixar a instalação, descompacte a distribuição binária em um local conveniente. Por exemplo, em C: \ apache-tomcat-7.0.59 no Windows ou /usr/local/apache-tomcat-7.0.59 no Linux / Unix e defina a variável de ambiente CATALINA_HOME apontando para os locais de instalação.

O Tomcat pode ser iniciado executando os seguintes comandos em uma máquina Windows, ou você pode simplesmente clicar duas vezes em startup.bat.

%CATALINA_HOME%\bin\startup.bat

ou

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

O Tomcat pode ser iniciado executando os seguintes comandos em uma máquina Unix (Solaris, Linux, etc.) -

$CATALINA_HOME/bin/startup.sh

ou

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

Após uma inicialização bem-sucedida, os aplicativos da web padrão incluídos no Tomcat estarão disponíveis visitando http://localhost:8080/. Se tudo estiver bem, ele deve exibir o seguinte resultado -

Mais informações sobre como configurar e executar o Tomcat podem ser encontradas na documentação incluída nesta página. Essas informações também podem ser encontradas no site do Tomcat -https://tomcat.apache.org.

O Tomcat pode ser interrompido executando os seguintes comandos em uma máquina Windows -

%CATALINA_HOME%\bin\shutdown

ou

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

O Tomcat pode ser interrompido executando os seguintes comandos na máquina Unix (Solaris, Linux, etc.) -

$CATALINA_HOME/bin/shutdown.sh

ou

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

Depois de concluir esta última etapa, você está pronto para prosseguir para seu primeiro exemplo de Jersey, que verá no próximo capítulo.

Vamos começar a escrever os serviços da web RESTful reais com o Jersey Framework. Antes de começar a escrever seu primeiro exemplo usando o Jersey Framework, você deve certificar-se de que configurou seu ambiente Jersey corretamente, conforme explicado no capítulo RESTful Web Services - Configuração do ambiente . Aqui, também estou assumindo que você tem algum conhecimento prático do Eclipse IDE.

Portanto, vamos continuar a escrever um aplicativo Jersey simples que exporá um método de serviço da web para exibir a lista de usuários.

Criando um Projeto Java

A primeira etapa é criar um Projeto da Web Dinâmico usando Eclipse IDE. Siga a opçãoFile → New → Project e finalmente selecione o Dynamic Web Projectassistente da lista de assistentes. Agora nomeie seu projeto comoUserManagement usando a janela do assistente, conforme mostrado na imagem a seguir -

Assim que seu projeto for criado com sucesso, você terá o seguinte conteúdo em seu Project Explorer -

Adicionando as Bibliotecas Necessárias

Como uma segunda etapa, vamos adicionar Jersey Framework e suas dependências (bibliotecas) em nosso projeto. Copie todos os jars dos seguintes diretórios da pasta zip de download do jersey no diretório WEB-INF / lib do projeto.

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

Agora, clique com o botão direito no nome do seu projeto UserManagement e depois siga a opção disponível no menu de contexto - Build Path → Configure Build Path para exibir a janela Java Build Path.

Agora usa Add JARs botão disponível em Libraries para adicionar os JARs presentes no diretório WEBINF / lib.

Criando os arquivos de origem

Agora vamos criar os arquivos de origem reais sob o UserManagementprojeto. Primeiro, precisamos criar um pacote chamadocom.tutorialspoint. Para fazer isso, clique com o botão direito em src na seção Package Explorer e siga a opção -New → Package.

Em seguida, vamos criar UserService.java, User.java,UserDao.java arquivos no pacote 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(); 
   }  
}

Existem dois pontos importantes a serem observados sobre o programa principal,

UserService.java

  • A primeira etapa é especificar um caminho para o serviço da web usando a anotação @Path para o UserService.

  • A segunda etapa é especificar um caminho para o método de serviço da web específico usando a anotação @Path para o método de UserService.

Criação do arquivo de configuração Web.xml

Você precisa criar um arquivo de configuração xml da Web que é um arquivo XML e é usado para especificar o servlet da estrutura Jersey para nosso aplicativo.

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>

Implementando o Programa

Quando terminar de criar os arquivos de origem e de configuração da web, você estará pronto para esta etapa que consiste em compilar e executar seu programa. Para fazer isso, usando o Eclipse, exporte seu aplicativo como um arquivo war e implante o mesmo no tomcat.

Para criar um arquivo WAR usando eclipse, siga a opção File → export → Web → War Filee, por fim, selecione o gerenciamento do usuário do projeto e a pasta de destino. Para implantar um arquivo war no Tomcat, coloque o UserManagement.war noTomcat Installation Directory → webapps directory e inicie o Tomcat.

Executando o Programa

Estamos usando Postman , uma extensão do Chrome, para testar nossos serviços da web.

Faça uma solicitação ao UserManagement para obter a lista de todos os usuários. Coloque http: // localhost: 8080 / UserManagement / rest / UserService / users no POSTMAN com solicitação GET e veja o seguinte resultado.

Parabéns, você criou seu primeiro aplicativo RESTful com sucesso.

O que é um recurso?

A arquitetura REST trata cada conteúdo como um recurso. Esses recursos podem ser arquivos de texto, páginas html, imagens, vídeos ou dados dinâmicos de negócios. O REST Server simplesmente fornece acesso aos recursos e o cliente REST acessa e modifica os recursos. Aqui, cada recurso é identificado por URIs / IDs globais. REST usa várias representações para representar um recurso onde Texto, JSON, XML. As representações de recursos mais populares são XML e JSON.

Representação de Recursos

Um recurso em REST é um Objeto semelhante em Programação Orientada a Objetos ou é como uma Entidade em um Banco de Dados. Uma vez identificado um recurso, então sua representação deve ser decidida utilizando um formato padrão para que o servidor possa enviar o recurso no formato acima mencionado e o cliente possa entender o mesmo formato.

Por exemplo, no capítulo RESTful Web Services - Primeiro aplicativo , um usuário é um recurso representado usando o seguinte formato XML -

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

O mesmo recurso pode ser representado no formato JSON da seguinte forma -

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

Boa Representação de Recursos

O REST não impõe nenhuma restrição ao formato de uma representação de recurso. Um cliente pode solicitar representação JSON, enquanto outro cliente pode solicitar representação XML do mesmo recurso para o servidor e assim por diante. É responsabilidade do servidor REST passar ao cliente o recurso no formato que o cliente entende.

A seguir estão alguns pontos importantes a serem considerados ao projetar um formato de representação de um recurso em RESTful Web Services.

  • Understandability - Tanto o servidor quanto o cliente devem ser capazes de entender e utilizar o formato de representação do recurso.

  • Completeness- O formato deve ser capaz de representar um recurso completamente. Por exemplo, um recurso pode conter outro recurso. O formato deve ser capaz de representar estruturas de recursos simples e complexas.

  • Linkablity - Um recurso pode ter uma ligação a outro recurso, um formato deve ser capaz de lidar com tais situações.

No entanto, no momento, a maioria dos serviços da web está representando recursos usando o formato XML ou JSON. Existem muitas bibliotecas e ferramentas disponíveis para entender, analisar e modificar dados XML e JSON.

Os RESTful Web Services usam protocolos HTTP como meio de comunicação entre o cliente e o servidor. Um cliente envia uma mensagem na forma de uma solicitação HTTP e o servidor responde na forma de uma resposta HTTP. Esta técnica é denominada Messaging. Essas mensagens contêm dados e metadados da mensagem, ou seja, informações sobre a própria mensagem. Vamos dar uma olhada nas mensagens de solicitação HTTP e resposta HTTP para HTTP 1.1.

Pedido HTTP

Uma solicitação HTTP tem cinco partes principais -

  • Verb - indica os métodos HTTP como GET, POST, DELETE, PUT, etc.

  • URI - Uniform Resource Identifier (URI) para identificar o recurso no servidor.

  • HTTP Version- indica a versão HTTP. Por exemplo, HTTP v1.1.

  • Request Header- Contém metadados para a mensagem de solicitação HTTP como pares chave-valor. Por exemplo, tipo de cliente (ou navegador), formato suportado pelo cliente, formato do corpo da mensagem, configurações de cache, etc.

  • Request Body - Conteúdo da mensagem ou representação de recursos.

Resposta HTTP

Uma resposta HTTP tem quatro partes principais -

  • Status/Response Code- indica o status do servidor para o recurso solicitado. Por exemplo, 404 significa recurso não encontrado e 200 significa que a resposta está ok.

  • HTTP Version- indica a versão HTTP. Por exemplo, HTTP v1.1.

  • Response Header- Contém metadados para a mensagem de resposta HTTP como pares chave-valor. Por exemplo, comprimento do conteúdo, tipo de conteúdo, data de resposta, tipo de servidor, etc.

  • Response Body - Conteúdo da mensagem de resposta ou representação de recursos.

Exemplo

Conforme explicado no capítulo RESTful Web Services - Primeiro aplicativo , vamos colocar http: // localhost: 8080 / UserManagement / rest / UserService / users no POSTMAN com uma solicitação GET. Se você clicar no botão Visualizar, próximo ao botão enviar do Postman, e clicar no botão Enviar, poderá ver o seguinte resultado.

Aqui você pode ver que o navegador enviou uma solicitação GET e recebeu um corpo de resposta como XML.

O endereçamento refere-se à localização de um recurso ou vários recursos no servidor. É análogo localizar o endereço postal de uma pessoa.

Cada recurso na arquitetura REST é identificado por seu URI (Uniform Resource Identifier). Um URI tem o seguinte formato -

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

O objetivo de um URI é localizar um ou mais recursos no servidor que hospeda o serviço da web. Outro atributo importante de uma solicitação é VERB que identifica a operação a ser executada no recurso. Por exemplo, no capítulo RESTful Web Services - Primeiro aplicativo , o URI éhttp://localhost:8080/UserManagement/rest/UserService/users e o VERBO é GET.

Construindo um URI padrão

A seguir estão os pontos importantes a serem considerados ao projetar um URI -

  • Use Plural Noun- Use o substantivo plural para definir recursos. Por exemplo, usamos usuários para identificar usuários como um recurso.

  • Avoid using spaces- Use sublinhado (_) ou hífen (-) ao usar um nome de recurso longo. Por exemplo, use authorized_users em vez de% 20users autorizados.

  • Use lowercase letters - Embora o URI não faça distinção entre maiúsculas e minúsculas, é uma boa prática manter o url apenas em letras minúsculas.

  • Maintain Backward Compatibility- Como o Web Service é um serviço público, um URI, uma vez tornado público, deve estar sempre disponível. Caso o URI seja atualizado, redirecione o URI mais antigo para um novo URI usando o código de status HTTP, 300.

  • Use HTTP Verb- Sempre use HTTP Verb como GET, PUT e DELETE para fazer as operações no recurso. Não é bom usar o nome de operações no URI.

Exemplo

A seguir está um exemplo de um URI ruim para buscar um usuário.

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

A seguir está um exemplo de um bom URI para buscar um usuário.

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

Como discutimos nos capítulos anteriores, o serviço da Web RESTful usa muitos verbos HTTP para determinar a operação a ser realizada no (s) recurso (s) especificado (s). A tabela a seguir apresenta os exemplos dos verbos HTTP mais comumente usados.

Sr. Não. Método HTTP, URI e operação

1

GET

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

Obtém a lista de usuários.

(Somente leitura)

2

GET

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

Obtém o usuário de Id 1

(Somente leitura)

3

PUT

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

Insere usuário com Id 2

(Idempotente)

4

POST

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

Atualiza o usuário com Id 2

(N / D)

5

DELETE

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

Exclui o usuário com ID 1

(Idempotente)

6

OPTIONS

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

Lista as operações com suporte em um serviço da web.

(Somente leitura)

7

HEAD

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

Retorna apenas o cabeçalho HTTP, sem corpo.

(Somente leitura)

Os seguintes pontos devem ser considerados.

  • As operações GET são somente leitura e são seguras.

  • As operações PUT e DELETE são idempotentes, o que significa que seu resultado sempre será o mesmo, não importa quantas vezes essas operações sejam chamadas.

  • As operações PUT e POST são quase iguais, com a diferença apenas no resultado em que a operação PUT é idempotente e a operação POST pode causar um resultado diferente.

Exemplo

Vamos atualizar um exemplo criado no capítulo RESTful Web Services - Primeiro aplicativo para criar um serviço da Web que pode executar operações CRUD (Criar, Ler, Atualizar, Excluir). Para simplificar, usamos um arquivo de E / S para substituir as operações do banco de dados.

Deixe-nos atualizar o User.java, UserDao.java e UserService.java arquivos no pacote 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>"; 
   } 
}

Agora usando o Eclipse, exporte seu aplicativo como um WAR Filee implantar o mesmo no Tomcat. Para criar um arquivo WAR usando eclipse, siga este caminho -File → export → Web → War Filee, finalmente, selecione o projeto UserManagement e a pasta de destino. Para implementar um arquivo WAR no Tomcat, coloque o UserManagement.war noTomcat Installation Directory → webapps diretório e o Tomcat inicial.

Testando o serviço da Web

Jersey fornece APIs para criar um cliente de serviço da web para testar serviços da web. Nós criamos uma amostra de classe de testeWebServiceTester.java sob o pacote com.tutorialspoint no mesmo projeto.

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); 
   } 
}

Agora execute o testador usando Eclipse. Clique com o botão direito no arquivo e siga a opçãoRun as → Java Application. Você verá o seguinte resultado no console do 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

De acordo com a arquitetura REST, um serviço da Web RESTful não deve manter um estado de cliente no servidor. Essa restrição é chamada de apatridia. É responsabilidade do cliente passar seu contexto para o servidor e, em seguida, o servidor pode armazenar esse contexto para processar a solicitação posterior do cliente. Por exemplo, a sessão mantida pelo servidor é identificada pelo identificador de sessão passado pelo cliente.

Os serviços da Web RESTful devem aderir a essa restrição. Vimos isso no capítulo Serviços da Web RESTful - Métodos , que os métodos de serviço da Web não armazenam nenhuma informação do cliente a partir do qual são chamados.

Consider the following URL −

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

Se você acessar a url acima usando seu navegador ou um cliente baseado em java ou usando Postman, o resultado será sempre o XML do usuário cujo Id é 1 porque o servidor não armazena nenhuma informação sobre o cliente.

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

Vantagens da apatridia

A seguir estão os benefícios da apatridia em RESTful Web Services -

  • Os serviços da Web podem tratar cada solicitação de método independentemente.

  • Os serviços da Web não precisam manter as interações anteriores do cliente. Isso simplifica o design do aplicativo.

  • Como o HTTP é um protocolo sem estado, os serviços da Web RESTful funcionam perfeitamente com os protocolos HTTP.

Desvantagens da apatridia

A seguir estão as desvantagens da ausência de estado em RESTful Web Services -

  • Os serviços da Web precisam obter informações extras em cada solicitação e, em seguida, interpretar para obter o estado do cliente, caso as interações com o cliente precisem ser cuidadas.

O armazenamento em cache refere-se ao armazenamento da resposta do servidor no próprio cliente, para que o cliente não precise fazer uma solicitação do servidor para o mesmo recurso repetidamente. Uma resposta do servidor deve ter informações sobre como o armazenamento em cache deve ser feito, para que um cliente armazene em cache a resposta por um período de tempo ou nunca armazene em cache a resposta do servidor.

A seguir estão os cabeçalhos que uma resposta do servidor pode ter para configurar o cache de um cliente -

Sr. Não. Cabeçalho e Descrição

1

Date

Data e hora do recurso quando foi criado.

2

Last Modified

Data e hora do recurso quando foi modificado pela última vez.

3

Cache-Control

Cabeçalho primário para controlar o cache.

4

Expires

Data de expiração e hora do armazenamento em cache.

5

Age

Duração em segundos a partir de quando o recurso foi buscado no servidor.

Cabeçalho Cache-Control

A seguir estão os detalhes de um cabeçalho Cache-Control -

Sr. Não. Diretriz e descrição

1

Public

Indica que o recurso pode ser armazenado em cache por qualquer componente.

2

Private

Indica que o recurso pode ser armazenado em cache apenas pelo cliente e pelo servidor, nenhum intermediário pode armazenar o recurso em cache.

3

no-cache/no-store

Indica que um recurso não pode ser armazenado em cache.

4

max-age

Indica que o armazenamento em cache é válido até a idade máxima em segundos. Depois disso, o cliente deve fazer outro pedido.

5

must-revalidate

Indicação ao servidor para revalidar o recurso se max-age tiver passado.

Melhores Práticas

  • Sempre mantenha conteúdos estáticos como imagens, CSS, JavaScript armazenáveis ​​em cache, com data de validade de 2 a 3 dias.

  • Nunca mantenha a data de validade muito alta.

  • O conteúdo dinâmico deve ser armazenado em cache por apenas algumas horas.

Como os serviços da Web RESTful funcionam com caminhos de URL HTTP, é muito importante proteger um serviço da Web RESTful da mesma maneira que um site é protegido.

A seguir estão as melhores práticas a serem seguidas ao projetar um serviço da Web RESTful -

  • Validation- Valide todas as entradas no servidor. Proteja seu servidor contra ataques de injeção de SQL ou NoSQL.

  • Session Based Authentication - Use a autenticação baseada em sessão para autenticar um usuário sempre que uma solicitação for feita a um método de serviço da web.

  • No Sensitive Data in the URL - Nunca use nome de usuário, senha ou token de sessão em uma URL, esses valores devem ser passados ​​para o serviço da Web através do método POST.

  • Restriction on Method Execution- Permitir o uso restrito de métodos como métodos GET, POST e DELETE. O método GET não deve ser capaz de excluir dados.

  • Validate Malformed XML/JSON - Verifique a entrada bem formada passada para um método de serviço da web.

  • Throw generic Error Messages - Um método de serviço da web deve usar mensagens de erro HTTP como 403 para mostrar o acesso proibido, etc.

Código HTTP

Sr. Não. Código HTTP e descrição

1

200

OK - mostra sucesso.

2

201

CREATED- quando um recurso é criado com sucesso usando a solicitação POST ou PUT. Retorna o link para o recurso recém-criado usando o cabeçalho do local.

3

204

NO CONTENT- quando o corpo de resposta está vazio. Por exemplo, uma solicitação DELETE.

4

304

NOT MODIFIED- usado para reduzir o uso da largura de banda da rede no caso de solicitações GET condicionais. O corpo da resposta deve estar vazio. Os cabeçalhos devem ter data, localização, etc.

5

400

BAD REQUEST- afirma que uma entrada inválida foi fornecida. Por exemplo, erro de validação, dados ausentes.

6

401

UNAUTHORIZED - afirma que o usuário está usando um token de autenticação inválido ou incorreto.

7

403

FORBIDDEN- informa que o usuário não está tendo acesso ao método que está sendo usado. Por exemplo, Excluir acesso sem direitos de administrador.

8

404

NOT FOUND - afirma que o método não está disponível.

9

409

CONFLICT- indica a situação de conflito durante a execução do método. Por exemplo, adicionar uma entrada duplicada.

10

500

INTERNAL SERVER ERROR - afirma que o servidor lançou alguma exceção ao executar o método.

JAX-RSsignifica JAVA API para RESTful Web Services. JAX-RS é uma API e especificação de linguagem de programação baseada em JAVA para fornecer suporte para serviços da Web RESTful criados. Sua versão 2.0 foi lançada em 24 de maio de 2013. JAX-RS usa anotações disponíveis no Java SE 5 para simplificar o desenvolvimento da criação e implantação de serviços da web baseados em JAVA. Ele também fornece suporte para a criação de clientes para RESTful Web Services.

Especificações

A seguir estão as anotações mais comumente usadas para mapear um recurso como um recurso de serviço da web.

Sr. Não. Anotação e descrição

1

@Path

Caminho relativo da classe / método de recurso.

2

@GET

Solicitação HTTP Get, usada para buscar recursos.

3

@PUT

Solicitação HTTP PUT, usada para atualizar o recurso.

4

@POST

Solicitação HTTP POST, usada para criar um novo recurso.

5

@DELETE

Solicitação HTTP DELETE, usada para excluir o recurso.

6

@HEAD

Solicitação HTTP HEAD, usada para obter o status de disponibilidade do método.

7

@Produces

Afirma a resposta HTTP gerada pelo serviço da web. Por exemplo, APPLICATION / XML, TEXT / HTML, APPLICATION / JSON etc.

8

@Consumes

Indica o tipo de solicitação HTTP. Por exemplo, application / x-www-formurlencoded para aceitar dados de formulário no corpo HTTP durante a solicitação POST.

9

@PathParam

Vincula o parâmetro passado ao método a um valor no caminho.

10

@QueryParam

Vincula o parâmetro passado ao método a um parâmetro de consulta no caminho.

11

@MatrixParam

Vincula o parâmetro passado ao método a um parâmetro de matriz HTTP no caminho.

12

@HeaderParam

Vincula o parâmetro passado ao método a um cabeçalho HTTP.

13

@CookieParam

Vincula o parâmetro passado ao método a um Cookie.

14

@FormParam

Vincula o parâmetro passado ao método a um valor de formulário.

15

@DefaultValue

Atribui um valor padrão a um parâmetro passado ao método.

16

@Context

Contexto do recurso. Por exemplo, HTTPRequest como um contexto.

Note- Usamos Jersey, uma implementação de referência de JAX-RS 2.0 da Oracle, nos capítulos RESTful Web Services - First Application e RESTful Web Services - Methods .