O padrão REST (Representational State Transfer) está cada vez mais se tornando um padrão para compartilhamento de informações nos serviços web. Criado em 2000 esse padrão utiliza o conceito de recursos que são identificados por um ids globais e utiliza os próprios métodos do protocolo HTTP para realizar operações sobre esses recursos em um servidor Web. Por exemplo, o método GET obtém um recurso ou uma lista deles, enquanto que o PUT insere, o POST atualiza e o DELETE remove. Outra característica interessante do REST é que podemos optar pelo tipo de retorno do serviço, ele pode ser: XML, JSON, ATOM, etc.
Para estabelecer um padrão para disponibilização de Web Services utilizando RESTful, foi criada a JSR-311, e sua implementação de referência é o Jersey, desenvolvido pela Sun (que saudade de você :) que utilizaremos nesse post.
Faça o download do Jersey (http://jersey.java.net/) e descompacte-o em algum lugar do seu computador (usei a versão 1.8). Para o nosso exemplo, além dele, você vai precisar do Tomcat (utilizei a versão 6.0), Eclipse for Java EE Developers ou o Eclipse comum com o plugin WTP (Web Tools Platform)
Inicie um novo Web/Dynamic Web Project no Eclipse chamado BlogWS_REST. Copie os JARs que estão na pasta lib do ZIP do Jersey para a pasta WebContent/WEB-INF/lib do seu projeto. Em seguida, adicione esses arquivos no Build Path do projeto. Clicando com o botão direito sobre eles com o botão direito, Build Path > Add to build path.
Agora, abra o arquivo Web Content/WEB-INF/web.xml e adicione as linhas abaixo dentro da tag <web-app>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>
com.sun.jersey.config.property.packages
</param-name>
<param-value>
ngvl.javaee.rest
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Quem já fez alguma coisa com Java Web já deve saber que estamos declarando o Servlet que responderá pelas requisições HTTP. As tags servlet-class está declarando o Servlet do Jersey. Passamos como parâmetros de inicialização o (init-param) o nome do pacote da nossa aplicação que terá os web services, no nosso caso, ngvl.javaee.rest. Depois informamos que esse servlet iniciará junto com o servidor (load-on-startup). E por último, fazemos o mapeamento de URL que esse Servlet responderá. No nosso caso, dizemos que qualquer requisição que venha pra http://servidor/BlogWS_REST/rest/ será tratado pelo Servlet do Jersey.
Agora vamos fazer o primeiro Web Service Rest. Crie a classe HelloResource e deixe-a conforme abaixo:
package ngvl.javaee.rest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/hello")
public class HelloResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayHello() {
return "Hello Jersey";
}
}
A anotação @Path informa o caminho para acessar esse serviço. O @GET diz o método HTTP que estamos utilizando. Já o @Produces determina o tipo de retorno que é dado, neste caso, texto plano.
Execute a aplicação (botão direito sobre o projeto Run as > Run on server) e depois execute no browser http://localhost:8080/BlogWS_REST/rest/hello. Você verá o texto "Hello Jersey" no navegador :)
Muito lindo! Mas na vida real não trabalhamos com "Olá mundo" :) Assim como no post que falei sobre o AXIS e Web Services com SOAP, vou mostrar como trabalhar com objetos. Crie a classe Pessoa e deixe-a conforme abaixo.
package ngvl.javaee.rest;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Pessoa {
private int id;
private String nome;
@DefaultValue("")
private String outro; // atributo opcional
// Contrutor padrão é obrigatório
public Pessoa(){
}
public Pessoa(int id, String nome, String outro) {
this.id = id;
this.nome = nome;
this.outro = outro;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getOutro() {
return outro;
}
public void setOutro(String outro) {
this.outro = outro;
}
}
A única observação sobre essa classe é a notação @XmlRootElement para que ela possa ser retornada como JSON, XML, whatever... :) Outro detalhe é o campo opcional "outro", isso fará que ele não seja obrigatório sua informação
Agora vamos a implementação do Web Service :)
package ngvl.javaee.rest;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/pessoas")
public class PessoaResource {
static List<Pessoa> pessoas;
static {
pessoas = new ArrayList<Pessoa>();
pessoas.add(new Pessoa(1, "Nelson", "info1"));
pessoas.add(new Pessoa(2, "Glauber", null));
pessoas.add(new Pessoa(3, "Vasconcelos", null));
pessoas.add(new Pessoa(4, "Leal", "info2"));
}
@GET
@Produces({ MediaType.APPLICATION_JSON })
public List<Pessoa> obterPessoas() {
return pessoas;
}
@GET
@Path("{pessoa}")
@Produces(MediaType.APPLICATION_JSON)
public Pessoa obterPessoa(@PathParam("pessoa") int id) {
return obterPessoas().get(id);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public String insert(Pessoa p){
if (p != null){
pessoas.add(p);
return "Funcionou!";
} else {
return "Erro ao adicionar pessoa.";
}
}
}
Quase todas as notações nós discutimos aqui. O método obterPessoas retorna um JSON com todas as pessoas da lista. Para acessar esse método, basta colocar no browser http://localhost:8080/BlogWS_REST/rest/pessoas o retorno será um arquivo nesse formato:
{"pessoa":[{"id":"1","nome":"Nelson"},{"id":"2","nome":"Glauber"},{"id":"3","nome":"Vasconcelos"},{"id":"4","nome":"Leal"}]}
Já o segundo método obtém apenas uma pessoa. Para acessa-lo, use a URL http://localhost:8080/BlogWS_REST/rest/pessoas/1 . Utilizar esse "1" no final é determinado pela notação @Path("{pessoa}") onde pessoa será o id do recurso desejado. O retorno dessa chamada será o arquivo abaixo:
{"id":"2","nome":"Glauber"}
O último método insere um objeto Pessoa na lista. Note que ele utiliza a notação @Consumes que indica que um parâmetro no formato JSON será enviado para esse método. Ou seja, um JSON será enviado e convertido "automagicamente" para um objeto Pessoa.
Bem pessoal, é isso. Esse post foi baseado nessa página aqui. Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber
44 comentários:
Glauber,
O parâmetro do método obterPassoa(int) requer a anotação @PathParam("pessoa"), caso contrário retornará um erro de tipo.
Segue o método já com a anotação:
@GET
@Path("{pessoa}")
@Produces( { MediaType.APPLICATION_JSON })
public Pessoa obterPessoa(@PathParam("pessoa") int id) {
return obterPessoas().get(id);
}
Após a modificação os valores são retornados corretamente.
Valeu Rafael!
Você tá completamente certo.
Depois de ler seu comentário, ajustei, testei e está ok.
4br4ç05,
nglauber
Olá, tenho uma dúvida, onde vc declarou a url http://localhost:8080/BlogWS_REST/rest/hello ?
Oi Mychelle,
localhost quer dize que é a sua máquina local. 8080 a porta padrão do Tomcat. BlogWS_REST é o nome da pasta do Tomcat. /rest é definido no arquivo de configuração do jersey na tag url-pattern. e o /hello é definido na anotação @Path("/hello").
4br4ç05,
nglauber
Obrigado, Nelson...
Nelson, obrigado e parabéns. O nivel de detalhamento das suas explicações facilita sobremaneira o entendimento de todo o artigo. Excelente.
Nelson, como faço um método POST consumir JSON? Não consegui achar isso de forma clara.
Oi Jhonathan,
Basta colocar na assinatura do método @POST ao invés de @GET. E no cliente fazer um request via post. No exemplo que mostrei com Android, é só chamar o método setRequestMethod("POST") no objeto HttpURLConnection.
4br4ç05,
nglauber
Grande Mestre! Existe algum motivo para o nome do Objeto ficar minusculo ({"pessoa":... e não {"Pessoa":... ) no JSon?
COmo posso passar 2 parametros pra o metodo de lerUmaPessoa por exemplo?
String result = getRESTFileContent("http://10.0.2.2:8080/BlogWS_REST/rest/pessoas/1parametro e 2parametro");
Oi Junior,
Basta você colocar na assinatura do método a anotação @QueryParam.
Ex.:
public Pessoa obterPessoa(@PathParam("pessoa") int id, @QueryParam("foo") String a) {
}
E na hora de chamar:
http://localhost:8080/ExemploRESTServer/rest/pessoas/1?foo=Glauber
4br4ç05,
nglauber
Testei mas sempre da erro de conexao recusada:
Iomar provavelmente você deve estar usando o IP errado. Você não deve usar nem localhost nem 127.0.0.1, pois, para o emulador esses endereços representam ele próprio. Use 10.0.2.2 para que o emulador se comunique com a máquina local.
4br4ç05,
nglauber
Apos fazer a alteração funcionou,
no me caso ficou assim:
Chamada
http://10.0.2.2:8080/ExemploRESTServer/rest/pessoas/ cliente
http://localhost:8080/ExemploRESTServer/server
obrigado Glauber, valeu pela ajuda!!
Boa tade Nelson, ótimo artigo. Só fiquei com uma dúvida: Como fica a Chamada para o método de inserção?
Oi Anônimo,
A resposta tá no outro post:
http://nglauber.blogspot.com/2011/06/android-restful-json.html
4br4ç05,
nglauber
Olá Glauber, estou com uma dúvida em relação a pergunta do Junior, ele perguntou como enviar dois parâmetros para o web service, eu entendi como o método fica no web service, mas Como ficaria a chamada no android, ficaria assim?
String result = getRESTFileContent("http://10.0.2.2:8080/BlogWS_REST/rest/pessoas/parametro1?parametro2");
Olá Glauber, já consegui fazer, mesmo assim obrigada. Boa noite.
Oi Mychelle,
Que bom que conseguiu. Qualqeur coisa me fala.
4br4ç05,
nglauber
Glauber como ficaria o meu metodo inserirPessoa caso eu adicione 2 campos editext, campoCod e campoNome e inserir atraves dos valores digitados pelo usuario. Obrigado e parabens!
Oi RH,
Você viu a parte editada do post? Eu mostro exatamente isso...
4br4ç05,
nglauber
profêêê... olha eu aqui de novo... hihihi... prometo que da proxima dou um tempo mais LONGO hihihi... veja bem.. como faço para consumir os dados de um webservice .NET no android... SEM TER NENHUMA CLASSE BÁSICA... :s preciso criar métodos ou so chamando os métodos da classe de comunicação já consigo esses dados? sem implementar nada de sql em android...?? :s HELP ME ME... mais uma vez novamente :) pleaseee :) vlw
Oi Gisele,
Não entendi sua dúvida. Pra acessa um webservice .net depende do que ele está retornando. Se for REST, segue a mesma linha desse post, se for SOAP, você pode usar esse post aqui http://nglauber.blogspot.com.br/2011/02/web-services-no-android.html.
Qualquer dúvida, manda email.
4br4ç05,
nglauber
Olá Glauber,
É possível consumir dois atributos de um Json, dessa forma abaixo? Mais o detalhe é que eu não queria que o login e a senha fosse passado na url, como o @PathParam obriga.
@POST
@Path("/login")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public User login(String login, String password) {
return facade.validateLogin(login, password);
}
Agradeço muito a ajuda, estou já a duas madrugadas procurando uma solução. Abraço
Oi Diego,
Nesse outro artigo (http://nglauber.blogspot.com.br/2011/06/android-restful-json.html), mostro como utilizar o webservice acima dando um POST no webservice. Consequentemente não passaria o login e senha na URL.
4br4ç05,
nglauber
Obrigado Nelson,
Consegui resolver me baseando no seu exemplo. Apenas utilizei JSONObject (Não tinha conhecimento da existência dessa classe) para recuperar as chaves passadas no meu Json e deu tudo certo!
Agora achei estranho o Package javax.ws.rs não ter nenhum método que receba de um Json um valor e salve esse valor em um atributo primitivo. Só vi formas de converter para um objeto.
Ola, vc teria algum tutorial sobre como consumir um WS REST, como o do Twitter?
Oi Larissa,
Dá uma olhada nesse post (http://nglauber.blogspot.com.br/2011/06/android-restful-json.html). Depois é só ajustar pra ler o JSON do Twitter.
4br4ç05,
nglauber
Muito bom o tutorial Glauber, ficou simples e bem explicativo sobre como fazer um web service!
Bom dia Nelson, primeiro quero agradecer pelo seu tutorial RESTful Web Services com Jersey, segui ele e funcionou perfeito.. queria tirar uma duvida, queria saber se tem como eu rodar o servidor em uma maquina onde não tem eclipse instalado, tipo criar um executável para não precisar instalar e ficar abrindo o eclipse, tentei criar o .jar, mas como não tem método principal não consegui fazer... Obrigado pela ajuda..
Cleber Alves
clebermcosmeticos@hotmail.com
Oi Anônimo,
É possível sim. Basta exportar seu projeto como um WAR (Web ARchive) ao invés de JAR e fazer o deploy no seu servidor.
4br4ç05,
nglauber
Obrigado pela dica, usei o tomcat, e esta rodando beleza, agora estou tentando tratar a busca quando o resultado é null ao importar..
Muito bom. Dentre todos exemplos que testei na Web, realmente este foi o melhor. Parabéns.
Minha estrutura ficou desta sabe me informar o porque?
{"pessoa":[{"id":"1","info":"info1","nome":"Nelson"},{"id":"2","info":"info2","nome":"Glauber"},{"id":"3","info":"info3","nome":"Vasconcelos"},{"id":"4","info":"info4","nome":"Leal"}]}
Oi mario,
É exatamente esse o formato esperado conforme eu descrevi no post.
4br4ç05,
nglauber
Olá Glauber, estou uma dúvida, você poderia me ajudar?
A dúvida é a seguinte: Quando um serviço REST precisa de vários método @GET, por exemplo:
@GET
@Path("{pessoa}") @Produces(MediaType.APPLICATION_JSON)
public Pessoa obterId(@PathParam("pessoa") int id) {
return obterPessoas().get(id);
}
@GET
@Path("{nome}") @Produces(MediaType.APPLICATION_JSON) public Pessoa obterNome(@PathParam("nome") String nome) {
return obterPessoas().get(nome);
}
No primeiro método a url de identificação seria: http://localhost:8080/BlogWS_REST/rest/pessoas/1
No segundo metodo a url de identificação seria:
http://localhost:8080/BlogWS_REST/rest/pessoas/maria
Mas quando executo o projeto, sempre o primeiro método é chamado independente do tipo de parâmetro passado na url. Sendo assim como ficaria o caminho da url e como seria a chamada no browser? Desde já agradeço.
Oi Raphael,
Quando queremos acessar um recurso específico (no nosso exemplo, uma pessoa) passamos o ID daquela pessoa na URL. Mas nesse caso que você tá falando, "maria" não é o ID da pessoa, e sim você está querendo fazer a busca por uma pessoa cujo nome é "maria". Então você teria uma URL do tipo http://seuservidor/pessoas?nome=maria.
4br4ç05,
nglauber
Boa noite Glauber.
Gostaria que você tirasse uma dúvida se for possível. Eu posso ter vários @Get em um único recurso, por exemplo. Um método @Get que busca uma pessoa pelo nome e um outro método @Get que busca uma pessoa pelo cpf? Eu criei em meu projeto dois métodos, que são os seguintes:
@Get
@Path("{nome}") @Produces(MediaType.APPLICATION_JSON)
public List getClienteByNome(@PathParam("nome") String nome) throws ClassNotFoundException{
ClienteBean cliente = new ClienteBean();
cliente.setCliNome(nome);
ClienteDao dao = new ClienteDao();
list = dao.searchByName(cliente);
return list;
}
O segundo método que criei foi:
@Get
@Path("{cpf}") @Produces(MediaType.APPLICATION_JSON)
public ClienteBean getClienteByCpf(@PathParam("cpf") String cpf) throws ClassNotFoundException{
ClienteBean cliente = new ClienteBean();
cliente.setCliCpf(cpf);
ClienteDao dao = new ClienteDao();
dao.searchByCpf(cliente);
return cliente;
}
Mas ocorre a seguinte exception quando executo o projeto pelo browser:
HTTP Status 500 - Internal Server Error
javax.servlet.ServletException: Servlet.init() for servlet helloWorld.ApplicationConfig threw exception
java.lang.IllegalStateException: The resource configuration is not modifiable in this context.
Uso a seguinte url para acessar os recursos:
para nome: http://localhost:8080/HelloWorldApplication/webresources/pessoas?nome=joana
para cpf:
http://localhost:8080/HelloWorldApplication/webresources/pessoas?cpf=12345
Quando eu retiro um dos métodos @Get, ficando apenas um método @Get, funciona legal o projeto. Desde já agradeço a atenção.
Obrigado, a pesquisa funcionou!
Oi Mychelle,
Vê a resposta que eu dei pro Raphael.
4br4ç05,
nglauber
Nelson, como consumir esse webservice pelo Android?
Oi Unknown,
O post é velhinho mas ainda serve...
http://www.nglauber.com.br/2011/06/android-restful-json.html
4br4ç05,
nglauber
Oi Glauber, boa tarde
estou com uma duvida! tenho um web service com dois parâmetros !! segue o codigo abaixo :
@Consumes({MediaType.APPLICATION_JSON, "text/plain"})
@Produces("text/plain")
@Path("/votacaoAta")
public String votarAta(TbDetalhesvotacaodeatape votAta, @QueryParam("url") String url) {
try {
FacesUtil.url = url;
hbutil = new HibernateUtil();
DetalhesVotacaoAtaPeRN rnAtaRN = new DetalhesVotacaoAtaPeRN(hbutil);
rnAtaRN.alterar(votAta);
hbutil.getSessionFactory().close();
return "S";
} catch (Exception e) {
return "N";
}
}
só que o objeto vem preenchido mais a url não!!! o que será? na minha ideia quero que url seja via url e o objeto um stream fora da url como posso fazer?
grato
OI André,
Esse post é bem velhinho (5 anos :) então faz tempo que não uso essa API, mas...
Você está passando a URL no formato:
http://seuservidor/votacaoAta?url="url_desejada"
Será que você não precisa dar um "escape" nos caracteres especiais?
4br4ç05,
nglauber
Postar um comentário