Web Services REST
Web Services REST
Web Services REST
Introduo
Este artigo fala sobre web services REST. Esta linha de web services vem se tornando cada vez mais popular e uma excelente opo para integrao este sistemas de diferentes plataformas. Neste artigo abordarei os principais conceitos deste tipo de servios, utilizarei um exemplo de leilo do Mercado Livre para ilustrar o problema, e mostrarei detalhes da arquitetura e implementao.
Definio
REpresentation State Transfer Estilo de arquitetura de software para sistemas distribudos Termo proposto por Roy Fielding em sua tese de doutorado Web services com a arquitetura da internet Explorao extensa dos recursos do HTTP
Surgimento
Roy Fielding um dos principais autores do protocolo HTTP, e ele props em sua tese de doutorado um estilo de arquitetura que faz extenso uso dos recursos oferecidos por este protocolo. Enquanto nos servios WS-I os recursos do HTTP so muito pouco explorados (inclusive porque o SOAP independente de transporte), nos servios REST umas das principais
caractersticas a utilizao de muitos recursos do HTTP para elaborar um protocolo de comunicao conciso e claro.
Arquitetura
A arquitetura dos web services WS-* se baseia em um protocolo bem definido, com regras precisas quanto ao formato dos dados trafegados e seguindo padres acordados em consrcios de grandes corporaes. Contrastando com isso, arquitetura dos web services REST radicalmente diferente. Poderamos ilustrar as filosofias de projeto de servios WS-* e REST com as sentenas a seguir. WS-*: J temos o protocolo e os padres, devemos definir os servios que vamos oferecer e os documentos que desejamos trocar entre as partes. REST: Vamos identificar os recursos envolvidos e utilizar extensamente os recursos do HTTP para definir um bom protocolo de interao com estes recursos.
REST: Clientes interagem com os Recursos atravs de requisies HTTP GET, PUT, POST e DELETE WS-*: Clientes invocam diferentes operaes, com conjuntos variados de parmetros de entrada e sada
A URI deve indicar o que voc est manipulando e o mtodo (ou verbo) HTTP indicar como voc est manipulando. Neste exemplo, a URI /usuario/123456 nos indica que estamos manipulando um usurio especfico. Sabendo que estamos usando o mtodo HTTP GET, temos a clara indicao de que estamos buscando os dados deste usurio. Este estilo de invocao de servios pode ser considerado Declarativo. Em bons protocolos REST, a juno do mtodo HTTP com a URI do recurso j nos indica na maioria dos casos qual a operao sendo realizada. Assim, com um pequeno trecho do cabealho HTTP j somos capazes de compreender a comunicao.
Nos web services WS-*, a informao da operao que est sendo realizada fica encapsulada no corpo da requisio. Mesmo quando a camada de transporte das mensagens SOAP HTTP, a URI no esclarece de forma alguma a operao envolvida. A informao dos servios disponveis fica descrita por elementos operation de um documento WSDL, geralmente em um formato fazerEssaOperacao. Esta maneira de desenvolver web services classificada como Imperativa.
Nesta seo falaremos da modelagem e desenvolvimento dos servios utilizando REST. Para ilustrar bem os cenrios abordados, trabalharemos em cima de um problema proposto: um processo de leilo do Mercado Livre.
Apresentao do problema
O problema que buscaremos resolver envolve servios referentes a um processo de leilo do Mercado Livre. Num leilo tpico, um usurio cadastrado no site coloca para venda um produto (novo ou usado), definindo um valor para o lance inicial e ento aguarda pelas ofertas de compra por parte de outros usurios interessados no produto. Aps receber algumas ofertas pelo produto, em um determinado momento o vendedor decide aceitar a melhor oferta recebida, e ento vende o produto para o comprador que fez esta oferta, encerrando neste momento o leilo. Em seguida venda, ocorrem os trmites de pagamento e entrega do produto (que no trataremos aqui) e no final, vendedor e comprador avaliam um ao outro, o que essencial para os usurios sentirem maior segurana ao realizar negociaes futuras deste gnero. Na modelagem desta aplicao, quatro entidades sero utilizadas: Usurio, Item, Oferta e Avaliao. Modelagem com Recursos O ponto de partida do desenvolvimento com REST definir quais so os recursos envolvidos, com base nos requisitos do sistema e nos servios que se deseja oferecer. No nosso exemplo, esta etapa no complexa. Os recursos que manipularemos so: Usuario, Item, Oferta e Avaliacao. Embora esta identificao tenha sido trivial no domnio que definimos, em alguns casos este processo pode ser um dos mais complexos na modelagem da aplicao. De uma maneira geral, quanto mais a aplicao se aproxima de um CRUD, mais fcil a identificao dos recursos. Para este processo de leilo, os servios que disponibilizaremos sero os da Tabela 1: Servio Anunciar item Buscar itens do vendedor Cadastrar usurio Realizar oferta Retirar oferta Buscar ofertas do item Descrio Permite que um usurio coloque um produto venda. Pesquisa os itens venda de um vendedor.
Realiza o cadastro de um novo usurio no site. Permite que um comprador faa uma oferta por um produto. Permite a remoo de uma oferta por parte do comprador. Pesquisa por todas as ofertas feitas sobre um produto.
Buscar melhor oferta Aceitar melhor oferta Avaliar usurio Buscar avaliaes do usurio
Busca a melhor oferta feita at o momento sobre um produto. Permite que um vendedor aceite a melhor oferta feita sobre o seu produto, e com isso encerre o leilo do mesmo. Realiza a avaliao de um usurio por parte de outro usurio, aps o trmino do processo de compra. Pesquisa por todas as avaliaes recebidas por um usurio.
Oferta Usuario Usuario Usuario Coleo de avaliaes Coleo de tens Item Avaliao Avaliao Coleo de URIs
Atualiza uma oferta. Remove uma oferta. Cadastra um usurio. Busca um usurio. Atualiza um usurio. Busca as avaliaes recebidas por um usurio. Busca os tens anunciados por um determinado usurio. Usurio coloca novo item venda. Busca uma determinada avaliao. Realizao da avaliao de um usurio sobre outro. Consulta URIs e mtodos HTTP disponveis para acesso.
/usuario/{id}/avaliacoes
GET
/services
GET
Tabela 2. URIs de acesso a recursos e mtodos HTTP aceitos por cada uma delas
@PathParam. Estas anotaes realizam injeo de dependncias relativas a servios REST, e so apresentadas na Tabela 3. Anotao Descrio
@Context
Injeta uma instncia de recursos como UriInfo, HttpHeaders, ServletConfig, ServletContext, HttpServletRequest e HttpServletResponse. Outros recursos de Java EE podem ser opcionalmente oferecidos por uma implementao desta JSR.
@HeaderParam
@CookieParam
@MatrixParam
Extrai o valor de parmetros enviados no formato chave=valor dentro de um segmento da URI. Exemplo: /usurio/123/itens;categoria=eletronicos;limitePreco=1000
@QueryParam
@PathParam
Tabela 3. Anotaes da JAX-RS para injeo de dependncias Com exceo do Context, estes parmetros so enviados dentro de URIs, query strings, cabealhos HTTP e cookies. Sendo assim, sua representao na camada de transporte como String. Entretanto, podemos colocar estas anotaes sobre parmetros que no so String, para j recebermos os dados convertidos em um formato mais adequado para nossa manipulao. Tipos de parmetros que podem ser marcados com estas anotaes so: Tipos primitivos Classes que possuam um construtor tendo uma nica String como parmetro Classes que possuam um mtodo esttico valueOf() recebendo uma String como parmetro; List<T>, Set<T> ou SortedSet<T>, onde T satisfaz a condio 2 ou a 3. A seguir um exemplo de uso destas anotaes: @GET @Path("{usuarioId}") public Response buscarUsuario(@PathParam("usuarioId") String usuarioId) { Usuario usuario = usuarioService.buscar(usuarioId); Response resposta = Response.ok(usuario).build(); return resposta; } Este exemplo mostra como poderia ser um mtodo de busca de usurio recebendo uma URI /usuario/{usuarioId}, como /usuario/123. O parmetro usuarioId do mtodo poderia ser int ou Integer, caso o ID do usurio fosse um nmero inteiro. A implementao da API faria a converso do parmetro enviado na URI para o tipo especificado no mtodo.
resolver este problema a JSR-311 permite que a resposta seja personalizada em caso de excees. Podemos fazer este controle de duas formas. A primeira forma com uma exceo especial. Basta disparar uma unchecked exception javax.ws.rs.WebApplicationException. Criando esta exceo podemos passar o status HTTP e um objeto Response, permitindo total controle da resposta que ser enviada para o usurio. Porm, em uma aplicao grande muito comum termos o lanamento de outras excees em camadas inferiores. Para esta situao a JSR-311 permite que seja criada uma classe para mapear a resposta correspondente a cada exceo. Este mapeamento seria conhecido apenas pela camada de servios REST. A classe de mapeamento deve implementar a interface javax.ws.rs.ext.ExceptionMapper e ser anotada com @Provider. A partir da, quando a exceo especificada for disparada o controle vai passar para o mtodo toResponse() desta classe. Este mtodo poder construir a resposta de acordo com a exceo, que passada como parmetro. Sobre a segunda forma de tratar excees vale citar que ela ainda est sendo implementada na verso 0.8 da especificao, e est sujeita a modificaes at a finalizao da JSR. O trecho a seguir apresenta um exemplo de mapeamento de exceo para uma resposta com status HTTP customizado. @Provider public class ItemJaVendidoExceptionMapper implements ExceptionMapper { public Response toResponse(ItemJaVendidoException e) { return Response.status(Response.Status.GONE).build(); } }
O trecho a seguir apresenta um exemplo de uso destas anotaes na classe UsuarioResource. Neste exemplo, declaramos que a classe aceita e gera contedo nos formatos text/xml e application/json. @ConsumeMime( { "text/xml", "application/json" }) @ProduceMime( { "text/xml", "application/json" }) public class UsuarioResource {} Neste exemplo, colocamos as anotaes sobre a classe. Estas anotaes tambm podem ser colocadas sobre mtodos. Caso as anotaes sejam colocadas sobre a classe e tambm sobre um mtodo, a anotao sobre o mtodo a que vale. O trecho abaixo mostra um exemplo de anotaes sobre a classe e sobre um mtodo. Neste exemplo, declaramos que a classe aceita os tipos de contedo text/xml e application/json em todos os seus mtodos, com exceo do mtodo buscarUsuario(). Neste mtodo declaramos que produzimos apenas contedo em formato text/xml. O mtodo atualizarUsuario() herda as declaraes feitas na classe, portanto aceita contedo em text/xml e application/json. @Path("usuario") @ConsumeMime( { "text/xml", "application/json" }) @ProduceMime( { "text/xml", "application/json" }) public class UsuarioResource { @GET @Path("{usuarioId}") @ProduceMime("text/xml") public Response buscarUsuario(@PathParam("usuarioId") String usuarioId) { Usuario usuario = usuarioService.buscar(usuarioId); Response resposta = Response.ok(usuario).build(); return resposta; } @PUT @Path("{usuarioId}") public Response atualizarUsuario(Usuario usuario) { usuarioService.atualizar(usuario); return Response.ok().build(); } } importante mencionar como funciona este tratamento dos tipos de contedo. Quando um cliente faz uma requisio HTTP, ele pode especificar o cabealho Accept. Este cabealho informa ao servidor quais so os tipos de contedo que o cliente aceita receber. Se o cliente no especificar este cabealho, o valor assumido do mesmo */*, o que indica que o cliente aceita qualquer tipo de contedo. Ao receber uma requisio HTTP, o runtime JAX-RS ir comparar a lista de tipos enviados no cabealho Accept com a lista de tipos de contedo registrados para o mtodo invocado. Nesta comparao, as seguintes regras sero aplicadas: Caso exista apenas um tipo de contedo em comum entre as duas listas, este ser o tipo de contedo enviado; Caso exista mais de um tipo de contedo em comum entre as listas, o
contedo ser enviado no formato que aparecer primeiro na lista registrada no servidor. Se o mtodo declarou gerao de text/xml e application/json (nesta ordem) e o cliente aceita ambos os tipos, o cliente receber text/xml; Se a lista de tipos de contedo oferecida pelo servidor no contiver nenhum dos tipos que o cliente afirmou aceitar, o runtime JAX-RS envia uma resposta de falha com status HTTP 415 (Unsupported Media Type). Com estes exemplos, mostramos a capacidade de gerar diferentes formatos em nossos servios, sem que seja necessrio tratar isso explicitamente pela nossa aplicao. A declarao dos tipos de contedo atravs das anotaes @ConsumeMime e @ProduceMime suficiente para que o runtime JAX-RS faa o tratamento correto. Uma ltima questo que precisamos abordar neste contexto como indicar para a JAX-RS como deve ser feito o mapeamento de nossas classes em XMLs. As implementaes da JSR so obrigadas a suportar o uso de JAXB (Java Architecture for XML Binding) na converso de Java para XML e de XML para Java. O JAXB um dos componentes do Java EE 5 e foi includo tambm na verso 6 do Java SE. Ele a forma padro de mapeamento entre classes Java e documentos XML na JSR-311. Para utilizar o JAXB no mapeamento de nossas classes, a opo mais simples utilizar a anotao @XmlRootElement sobre as mesmas. Ao fazer isso, o JAXB far a converso da classe e dos seus atributos em um documento XML cujo elemento root ser o nome da classe (comeando por minscula). Os elementos filhos sero os atributos da classe, seguindo a nomenclatura da mesma. Caso seja desejado, possvel especificar na anotao @XmlRootElement um nome de elemento XML diferente do nome da classe. Podemos tambm modificar os elementos XML dos atributos da classe usando a anotao @XmlElement. O JAXB nos permite customizar bastante os mapeamentos realizados, se assim quisermos. O trecho abaixo apresenta um exemplo no qual mapeamos a classe Avaliacao para um XML com elementos em ingls. No entraremos em mais detalhes sobre o JAXB neste momento, pois isto fugiria do foco do artigo. @XmlRootElement(name = "feedback") public class Avaliacao {XmlElement(name = "feedbackCode") private String codAvaliacao; @XmlElement(name = "rater") private Usuario avaliador; @XmlElement(name = "positive") private boolean positiva; @XmlElement(name = "comment") private String comentario; }
antes de invocar os servios em questo, precisamos extrair das URIs os parmetros que tenham sido enviados nas mesmas. A anotao @Path pode ser colocada em classes e mtodos. Quando a colocamos sobre uma classe, estamos associando a classe a um prefixo de URI. Esta anotao foi utilizada anteriormente no artigo para associar o prefixo /usuario da nossa aplicao classe UsuarioResource. Colocamos a anotao @Path sobre mtodos para formar o caminho completo dos nossos servios. Juntando os valores da anotao @Path sobre os mtodos com o valor da anotao sobre a classe, temos a lista de URIs disponveis na classe Recurso em questo. A colocao das anotaes @GET, @POST, @PUT, @DELETE, @HEAD e @OPTIONS complementa o mapeamento de URIs. Com estas duas informaes sobre cada mtodo, conseguimos mapear precisamente cada solicitao na classe e no mtodo que devem trat-la. Mostraremos na prxima seo como fica o uso da JSR-311 no desenvolvimento dos servios de leilo.
</servlet> <servlet-mapping> <servlet-name>Jersey Web Application</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> Alm da configurao do Servlet, precisamos adicionar algumas bibliotecas para utilizar o Jersey. O conjunto mnimo de bibliotecas que devem ser colocadas na aplicao inclui o jersey.jar, jsr311-api.jar e asm.jar. Estas bibliotecas e mais algumas dependncias esto presentes no diretrio /lib da distribuio binria do Jersey. Se no estiver usando Java SE 6 ou Java EE 5, voc tambm precisar adicionar o JAXB ao projeto. Utilizando um servidor de aplicaes Java EE 5 e as bibliotecas presentes na distribuio binria do projeto, voc tem a garantia de ter todas as dependncias necessrias.
/usuario
POST
Usuario
Cadastra um usurio.
Usuario
Busca um usurio.
Usuario
Atualiza um usurio.
/usuario/{id}/avaliacoes
GET
Coleo de avaliaes
Coleo de itens
Item
/avaliacao/{id}
GET
Avaliao
/avaliacao/de/{id}/para/{id}
POST
Avaliao
A listagem abaixo apresenta a classe UsuarioResource. Esta uma das classes Recurso da nossa aplicao e nela esto todos os servios do prefixo /usuario. A classe foi anotada com @Path(usuario), o que faz a associao da mesma com o prefixo citado. Alm disso, a classe possui as anotaes @ConsumeMime e @ProduceMime, que neste caso declaram que os servios da mesma so capazes de consumir e gerar contedo nos formatos text/xml e application/json. @Path("usuario") @ConsumeMime( { "text/xml", "application/json" }) @ProduceMime( { "text/xml", "application/json" }) public class UsuarioResource { private ItemService itemService; private UsuarioService usuarioService; private AvaliacaoService avaliacaoService; public UsuarioResource() { this.itemService = ServiceFactory.getItemService(); this.usuarioService = ServiceFactory.getUsuarioService(); this.avaliacaoService = ServiceFactory.getAvaliacaoService(); } @GET @Path("{usuarioId}") public Response buscarUsuario(@PathParam("usuarioId") String usuarioId) { Usuario usuario = usuarioService.buscar(usuarioId); if(usuario == null){ return Response.status(HttpServletResponse.SC_NOT_FOUND).build(); } Response resposta = Response.ok(usuario).build(); return resposta; } @POST public Response cadastrarUsuario(Usuario usuario) { usuario = usuarioService.cadastrar(usuario); try { return Response.created(new URI(usuario.getCodUsuario())).build(); } catch (URISyntaxException e) { throw new RuntimeException(e); } } @PUT
@Path("{usuarioId}") public Response atualizarUsuario(Usuario usuario) { usuarioService.atualizar(usuario); return Response.ok().build(); } @POST @Path("{usuarioId}/itens") public Response cadastrarItem(@Context UriInfo uriInfo, @PathParam("usuarioId") String usuarioId, Item item) throws URISyntaxException { Usuario usuario = new Usuario(usuarioId); item = itemService.cadastrar(item, usuario); URI uriItem = new URI(uriInfo.getBaseUri() + "item/" + item.getCodItem()); return Response.created(uriItem).build(); } @GET @Path("{usuarioId}/itens") public Response buscarItensDoUsuario(@PathParam("usuarioId") String usuarioId) { // Verifica se o usurio existe if (this.usuarioService.buscar(usuarioId) == null) { return Response.status(Status.NOT_FOUND).build(); } List itens = itemService.buscarPorVendedor(new Usuario(usuarioId)); return Response.ok(new ItensUsuario(itens)).build(); } @GET @Path("{usuarioId}/avaliacoes") public AvaliacoesUsuario buscarAvaliacoesDoUsuario(@PathParam("usuarioId")String usuarioId){ Usuario usuario = new Usuario(usuarioId); List avaliacoes = avaliacaoService.buscarPorUsuario(usuario); return new AvaliacoesUsuario(avaliacoes); } } O primeiro servio o de busca de usurio. Este mtodo foi anotado com @Path({usuarioId}). O casamento da anotao sobre o mtodo com a anotao sobre a classe especifica que este mtodo responde a requisies para a URI /usuario/{usuarioId}. Como o mtodo tambm foi anotado com @GET, sabemos que as solicitaes HTTP GET para /usuario/{usuarioId} sero tratadas por este mtodo. Importante reparar no uso da anotao @PathParam para injetar no parmetro usuarioId o valor que veio na URI.
Na resposta a esta solicitao, retornamos o status HTTP 200 (OK) e os dados do usurio no corpo da resposta. Caso o usurio no tenha sido encontrado, retornamos status 404 (Not Found). A listagem abaixo mostra a classe Usuario, que manipulada por alguns servios na classe UsuarioResource. Por simplicidade mostramos apenas a declarao da classe com os atributos. @XmlRootElement public class Usuario { private private private private String String String String codUsuario; nome; login; email;
private Item[] items; private Oferta[] ofertas; private Avaliacao[] avaliacoes; } O segundo servio presente em UsuarioResource o de cadastro de usurio. Como no colocamos nenhuma anotao @Path sobre este mtodo, ele est associado URI da classe (/usuario). O mtodo foi anotado com @POST, ento ele responde s solicitaes POST na URI citada. O parmetro contendo os dados do usurio no recebeu nenhuma anotao, o que significa que ele obtido do corpo da solicitao. Como o mtodo de cadastro de usurio no tem as anotaes @ConsumeMime e @ProduceMime, ele herda as declaraes feitas sobre a classe. Sendo assim, podemos cadastrar usurios usando text/xml ou application/json. Na resposta criao do usurio ns enviamos o status HTTP 201 (Created), colocando no header Location a URI do novo usurio. O mtodo de atualizao de usurio recebe solicitaes PUT em /usuario/{usuarioId}. Os dados do usurio tambm so consumidos do corpo da solicitao, e as operaes com sucesso resultam no envio do status HTTP 200. No mtodo de cadastrar itens, usamos a anotao @Path para associar o servio URI /usuario/{usuarioId}/itens. Usamos a anotao @PathParam para extrair da URI o ID do usurio envolvido. Usamos tambm a anotao @Context para injetar a classe UriInfo, que nos fornece informaes sobre a URI de acesso aos servios. No final, usamos a UriInfo para colocar no header Location o caminho absoluto de acesso ao item recm-criado. A listagem abaixo mostra a classe Item, manipulada neste servio. @XmlRootElement public class Item { private String codItem; private String nome; private private private private String descricao; BigDecimal valorInicial; boolean novo; boolean vendido;
} No mtodo de buscar itens do usurio temos o primeiro servio que manipula colees. Este mtodo trata de solicitaes GET URI /usuario/{usuarioId}/itens. Para retornar a lista de itens do usurio foi criada a classe ItensUsuario, que simplesmente contm a lista. A listagem a seguir apresenta a declarao desta classe. @XmlRootElement public class ItensUsuario {
private List item; public ItensUsuario() {} public ItensUsuario(List itens) { this.item = itens; } } O mtodo de buscar avaliaes do usurio estruturalmente semelhante ao de buscar itens. Foi criada a classe AvaliacoesUsuario para retornar a lista de avaliaes. Como esta muito semelhante ItensUsuario, ela ser omitida. Para implementar os servios do prefixo /avaliacao foi criada a classe AvaliacaoResource. Esta classe pode ser vista na listagem a seguir. Temos a anotao @Path registrando a URI desejada e tambm as anotaes @ConsumeMime e @ProduceMime declarando que manipulamos text/xml e application/json. @Path("avaliacao") @ConsumeMime( { "text/xml", "application/json" }) @ProduceMime( { "text/xml", "application/json" }) public class AvaliacaoResource { private AvaliacaoService avaliacaoService; public AvaliacaoResource() { this.avaliacaoService = ServiceFactory.getAvaliacaoService(); } @GET @Path("{avaliacaoId}") public Response buscarAvaliacao(@PathParam("avaliacaoId") String avaliacaoId) { Avaliacao avaliacao = avaliacaoService.buscar(avaliacaoId); if(avaliacao == null){ return Response.status(HttpServletResponse.SC_NOT_FOUND).build(); } return Response.ok(avaliacao).build(); } @POST @Path("de/{avaliador}/para/{avaliado}") public Response avaliarUsuario(@Context UriInfo uriInfo, @PathParam("avaliador") String avaliador, @PathParam("avaliado") String avaliado, Avaliacao avaliacao) throws URISyntaxException { Usuario usuarioAvaliado = new Usuario(avaliado); avaliacao = avaliacaoService.cadastrar(avaliacao, usuarioAvaliado); URI uri = new URI(uriInfo.getBaseUri() + "avaliacao/" + avaliacao.getCodAvaliacao()); return Response.created(uri).build(); } }
O primeiro servio desta classe o de busca de avaliao, que muito semelhante ao servio de busca de usurio que vimos anteriormente. Este servio ficou mapeado em /avaliacao/{avaliacaoId}, recebendo solicitaes GET. O servio de avaliar usurio mais interessante. Extramos dois parmetros da URI e consumimos um recurso do corpo da solicitao. A URI /avaliacao/de/{avaliador}/para/ {avaliado} um bom exemplo da liberdade que temos na definio das URIs. Podemos mold-las para aumentar a clareza das operaes. Isto facilita a aproximao dos servios com o nosso domnio da aplicao.
// Montando requisio com o commons-http-client HttpClient client = new HttpClient(); PostMethod method = new PostMethod("http://localhost:8080/item/" + item.getCodItem() + "/ofertas"); // Gerao de XMLs com o XStream XStream xstream = new XStream(); xstream.alias("oferta", Oferta.class); xstream.alias("ofertante", Usuario.class); xstream.alias("item", Item.class); String ofertaXml = xstream.toXML(oferta); System.out.println(ofertaXml); // Definindo corpo da requisio StringRequestEntity requestEntity = new StringRequestEntity(ofertaXml, "text/xml", "UTF-8"); method.setRequestEntity(requestEntity); // Here it goes... int statusCode = client.executeMethod(method); // Status HTTP deve ser 201 - Created assertEquals(HttpServletResponse.SC_CREATED, statusCode); System.out.println("\n#### REQUISIO ####\n"); System.out.println(method.getName() + " " + method.getPath()); Header[] headersRequest = method.getRequestHeaders(); for(Header header : headersRequest){ System.out.println(header.getName() + " : " + header.getValue()); } method.getRequestEntity().writeRequest(System.out); System.out.println("\n\n#### RESPOSTA ####\n"); System.out.println(method.getStatusLine().getHttpVersion() + " " + method.getStatusLine().getStatusCode()); Header[] headersResponse = method.getResponseHeaders(); for(Header header : headersResponse){ System.out.println(header.getName() + " : " + header.getValue()); } System.out.println(method.getResponseBodyAsString()); } }
O formato da requisio HTTP gerada por esta classe pode ser visto na primeira listagem abaixo. Na listagem seguinte pode ser conferido o formato da resposta HTTP a esta solicitao. A terceira listagem desta sequncia mostra o formato da resposta busca de ofertas de um determinado item. #### REQUISIO #### POST /item/13c017ba-7c01-44aa-9a0b-b815a9ea298f/ofertas User-Agent : Jakarta Commons-HttpClient/3.0.1 Host : localhost:8080 Content-Length : 596 Content-Type : text/xml; charset=UTF-8 <oferta> <valor>111.22</valor> <dataModificacao>2008-09-28 10:36:54.642 BRT</dataModificacao> <item> <codItem>13c017ba-7c01-44aa-9a0b-b815a9ea298f</codItem> <nome>Iphone</nome> <descricao>Item teste Tech Talk 1222609014628</descricao> <valorInicial>0</valorInicial> <novo>true</novo> <vendido>false</vendido> </item> <ofertante> <codUsuario>fc6104ad-b9a5-4b2d-9085-a186083b9c2d</codUsuario> <nome>Usuario Tech Talk</nome> <login>techtalk1222609014475</login> <email>[email protected]</email> </ofertante> <vencedora>false</vencedora> </oferta> #### RESPOSTA #### HTTP/1.1 201 Server : Apache-Coyote/1.1 Location : http://localhost:8080/item/13c017ba-7c01-44aa-9a0bb815a9ea298f/ofertas Content-Length : 0 Date : Sun, 28 Sep 2008 13:36:55 GMT A implementao das solicitaes HTTP em Java pode ser feita com uso da biblioteca commons-http-client. Esta API permite que montemos requisies HTTP e recebamos suas respostas correspondentes, da mesma forma que ocorreria com um browser simples. No existe a capacidade de executar cdigo javascript e tambm no existem equivalentes para os plugins dos browsers completos. Mesmo sem estes recursos, o commons-http-client nos d o poder de fazer praticamente qualquer operao padro HTTP do lado cliente. Isto torna esta biblioteca um componente de fundamental importncia para implementaes RESTful feitas em Java.
Clientes Ajax/JSON
Quando falamos em web services a primeira idia sobre o formato para troca das informaes o XML. Porm, outros formatos para troca de dados existem e podem atender melhor a alguns casos particulares. Um destes formatos o JSON. Ele tem se difundido bastante, especialmente como uma alternativa ao XML em AJAX, pois um formato nativamente suportado por qualquer interpretador javascript. Outro motivo que um formato mais enxuto, gerando documentos menores e mais fceis consumir. Por estas vantagens, tem sido comum o uso de JSON para criar clientes AJAX de servios REST. Na listagem abaixo apresentamos um exemplo de funo javascript que busca itens de um usurio num servio REST e apresenta os mesmos em uma tabela. Mostramos apenas os trechos mais relevantes. O leitor pode obter o cdigo completo dos exemplos de clientes ajax no site da revista. A listagem seguinte apresenta a definio de uma funo que faz uma requisio HTTP GET assncrona a um servio REST. <script type="text/javascript"> function mostrarItens() { // Faz a chamada rest var url = "/usuario/" + document.getElementById("cod_usuario").value + "/itens"; var tabela = document.getElementById("itens"); var response = RESTFul.get(url); if (response.status != 200) { tabela.innerHTML = "<b>Usurio no encontrado!</b>"; return; } var itensUsuario = response.getObject(); var stringitens = "<table border="1">"; stringitens += "<thead><td>codigo</td><td>descrio</td><td>nome</td><td>novo</td>" +"<td>valor inicial</td><td>vendido</td></thead>"; for (i in itensUsuario.itensUsuario.item) { var item = itensUsuario.itensUsuario.item[i]; stringitens += "<tr><td> // requisio Ajax para HTTP GET get: function (url, request) { // cria um XmlHttpRequest xmlhttp = getXTR(); // constri um request default if (!request) {request = new RESTFul.Request();} xmlhttp.open("GET",url,false); // requisio GET assncrona xmlhttp.setRequestHeader("Accept","application/json"); xmlhttp.send(null); // requisio sem corpo while (xmlhttp.readyState != 4); var response = new RESTFul.Response(xmlhttp.status, xmlhttp.statusText, xmlhttp.getAllResponseHeaders(), xmlhttp.responseText);
return response; }
Suporte de ferramentas
Um dos pontos positivos dos web services WS-* que j existe um amplo conjunto de ferramentas para facilitar o trabalho com esta linha de servios. Geradores de clientes e esqueletos de servio esto disponveis para vrias plataformas e linguagens. Os web services REST tiveram que esperar muito mais tempo para terem as primeiras ferramentas de desenvolvimento. No devemos ver isso como um ponto negativo. Se existem muitos produtos para melhorar o trabalho com WS-* porque as tecnologias envolvidas exigem isso. Os servios REST so essencialmente mais simples e concentram seus detalhes principais em torno de HTTP. Como as tecnologias utilizadas com REST j so muito maduras e conhecidas, indiretamente j havia um timo suporte a este desenvolvimento. Recentemente o NetBeans introduziu plugins focados em servios REST, e estes contribuem com um ganho de produtividade no comeo do desenvolvimento. Somos capazes de gerar classes Recurso a partir de entidades JPA e tambm gerar clientes Java e Javascript para nossos servios. Estas funcionalidades ajudam na rpida criao de prottipos, e oferecem um bom ponto de partida na implementao. Detalhes sobre o suporte do NetBeans ficam fora do escopo deste artigo, mas recomendamos que os leitores avaliem os benefcios que este IDE traz.
Concluso
O objetivo deste artigo foi partir de um exemplo rico o suficiente de arquitetura orientada a servios e ento fazer a modelagem e desenvolvimento dos mesmos com uso de uma
abordagem RESTful. Atravs do exemplo do leilo do Mercado Livre, fomos capazes de percorrer todas as etapas envolvidas na implementao de web services REST. Entre estas etapas podemos destacar a identificao dos recursos, mapeamento de URIs, definio do protocolo de comunicao e formas de mapeamento de Java para XML e XML para Java. Foi possvel ilustrar bem como o bom uso dos recursos do HTTP podem ajudar na definio de um protocolo de comunicao conciso e claro. Elementos j conhecidos como os status e mtodos HTTP, URIs e Content-Types passam a ser utilizados para comunicaes bem mais diversas do que a transferncia simples de HTML. Com o amadurecimento das implementaes REST, estamos caminhando na direo de solues poderosas e interoperveis. At alguns anos atrs, a adoo de web services representava uma troca de performance por interoperabilidade. Piorar a performance na comunicao com a mesma plataforma e linguagem para ser capaz de falar com qualquer outro servio. Isto felizmente est deixando de ser verdade. Estamos conquistando poder suficiente para ter ao mesmo tempo alta performance e interoperabilidade. A JSR-311 e o Jersey trazem benefcios interessantes, e no nos tiram o poder do REST. A introduo destes componentes simplifica o desenvolvimento. Alm disso, ganhamos funcionalidades que seriam muito trabalhosas de implementar de forma customizada. A capacidade de manipular mltiplos formatos e a gerao do WADL so bons exemplos disso. Esperamos que este artigo tenha contribudo com novas idias para os leitores no desenvolvimento de servios REST. Esta linha de servios vem amadurecendo progressivamente e j se apresenta como uma opo poderosa para integrao entre aplicaes. Pretendo publicar vrios outros artigos na rea de web services, integrao e tecnologias relacionadas, ento se voc gostou deste artigo, acompanhe sempre as novidades por aqui
Engenheiro eletrnico e de computao de 26 anos que se frustrou com a eletrnica e se apaixonou por software. Trabalha com software desde 2003 (sem contar o perodo acadmico) e com Java desde o comeo de 2004. Se interessa bastante por diversas reas distintas, como linguagens de programao, open source, sistemas operacionais, processo de desenvolvimento, computao distribuda, SOA e Java em geral. Trabalha h 2 anos e meio na Concrete Solutions, atuando na Globo.com (mais precisamente na rea do ISP) desde minha chegada empresa. Atualmente tambm colunista da Java Magazine, com foco principal em Java EE e Web Services em geral.