Mostrando postagens com marcador RESTful. Mostrar todas as postagens
Mostrando postagens com marcador RESTful. Mostrar todas as postagens

quinta-feira, 30 de junho de 2011

Android + RESTful + JSON

Olá pessoal,

No post anterior mostrei como criar um Web Service simples com o Jersey. Através dele podemos retornamos um arquivo JSON baseado no padrão RESTful.

Nesse pequeno POST, vou mostrar como ler os dados desse WS com o Android. O REST/JSON está tão forte que a Google já colocou nativamente no Android uma API para leitura desse tipo de arquivo. Então, dentro do seu projeto basta colocar os métodos abaixo:


private String toString(InputStream is)
throws IOException{

byte[] bytes = new byte[1024];
ByteArrayOutputStream baos =
new ByteArrayOutputStream();
int lidos;
while ((lidos = is.read(bytes)) > 0){
baos.write(bytes, 0, lidos);
}
return new String(baos.toByteArray());
}


Esse primeiro método é uma "receita de bolo". Ele será pelo método getRESTFileContent(String) (a seguir) e servirá para ler byte-a-byte (na verdade de 1024 em 1024 bytes) o conteúdo do arquivo JSON que é retornado pelo servidor, retornando-o em forma de String.

public String getRESTFileContent(String url) {
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(url);

try {
HttpResponse response =
httpclient.execute(httpget);

HttpEntity entity = response.getEntity();

if (entity != null) {
InputStream instream = entity.getContent();
String result = toString(instream);

instream.close();
return result;
}
} catch (Exception e) {
Log.e("NGVL", "Falha ao acessar Web service", e);
}
return null;
}

O método acima, utiliza o HttpClient (da Apache, que vem nativamente no Android) para estabelecer a conexão com o servidor, abrir o fluxo de leitura (InpuStream) e retornar o conteúdo do arquivo JSON em forma de String (feito pelo método toString(InputStream)).

private void lerTodasPessoas() {
String result = getRESTFileContent(
"http://10.0.2.2:8080/BlogWS_REST/rest/pessoas/");
if (result == null){
Log.e("NGVL", "Falha ao acessar WS");
return;
}

try {
JSONObject json = new JSONObject(result);
JSONArray pessoasArray =
json.getJSONArray("pessoa");

JSONObject pessoaJson;

for (int i = 0; i < pessoasArray.length(); i++) {
pessoaJson = new JSONObject(
pessoasArray.getString(i));

Log.i("NGVL",
"------------------------");
Log.i("NGVL",
"id="+ pessoaJson.getInt("id"));
Log.i("NGVL",
"nome="+ pessoaJson.getString("nome"));
}
} catch (JSONException e) {
Log.e("NGVL", "Erro no parsing do JSON", e);
}
}

private void lerUmaPessoa() {
// Observe o ID da pessoa no final da URL
String result = getRESTFileContent(
"http://10.0.2.2:8080/BlogWS_REST/rest/pessoas/1");
if (result == null){
Log.e("NGVL", "Falha ao acessar WS");
return;
}

try {
JSONObject pessoaJson = new JSONObject(result);

Log.i("NGVL",
"id="+ pessoaJson.getInt("id"));
Log.i("NGVL",
"nome="+ pessoaJson.getString("nome"));

} catch (JSONException e) {
Log.e("NGVL", "Erro ao fazer parsing do JSON", e);
}
}

Este é o método que deverá ser chamado diretamente e utilizará os demais. Inicialmente ele pega o conteúdo do arquivo JSON informando a URL do mesmo. Notem que foi usado o IP 10.0.2.2 estabelecido pelo Android para acessarmos um serviço da máquina local.
Em seguida, criamos o objeto JSON e a partir dele obtemos o array de pessoas. Esse array contém todos os objetos JSON do arquivo. Em seguida, iteramos sobre este array atribuindo à variável pessoaJson. E com essa variável pegamos a propriedade de cada objeto retornado.
Editado em 06/11/2011
Agora vou mostrar como passar um JSON para o nosso servidor inserir uma nova Pessoa.

private String inserirPessoa() {
try {
URL url = new URL(
"http://10.0.2.2:8080/BlogWS_REST/rest/pessoas/");
HttpURLConnection conexao =
(HttpURLConnection)url.openConnection();

conexao.setRequestMethod("POST");
conexao.addRequestProperty(
"Content-type", "application/json");

conexao.setDoOutput(true);

conexao.connect();

OutputStream os = conexao.getOutputStream();
os.write("{\"id\":\"1\",\"nome\":\"Nelson\",\"outro\":\"info1\"}".getBytes());
os.flush();

InputStream is = conexao.getInputStream();
return toString(is);

} catch (Exception e) {
e.printStackTrace();
return "ERRO!";
}
}

Este método abre a conexão com o servidor, mas desta vez para fazer um POST ao invés de um GET. Ele passará um objeto JSON que representa um objeto Pessoa no servidor através de um OutputStream. Aqui colocamos um JSON hard-coded, mas você poderia utilizar a bibilioteca GSON do Google para converter seus objetos em JSON.
[/Editado]

Dois detalhes: 1) toda comunicação deve ser feita na Thread separada da UI. Logo chame o método acima dentro de uma outra Thread ou use a abordagem da AsyncTask que eu mostrei nesse post. 2) Você deve adicionar a permissão android.permission.INTERNET no seu AndroidManifest.XML conforme abaixo:
<uses-permission android:name="android.permission.INTERNET"/>


Qualquer dúvida deixem seus comentários.

4br4ç05,
nglauber

RESTful Web Services com Jersey

Olá povo,

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