Aula 25: Web Services (III)
Diego PassosUniversidade Federal Fluminense
Última Aula
A API JAX-WS.
I Como escrever Big Web Services.
I Montagem e disponibilizaçao.
I Geração dos artefatos.
I Como escrever um cliente. I Mapeamento de tipos de dados.
Aula de Hoje
RESTful Web Services: I Visão geral.
I A API JAX-RS.
I Como escrever RESTful Web Services.
I Tipos de anotações utilizadas.
I Provedores de Entidades.
I Recebendo parâmetros.
RESTful Web Services: Visão Geral
Até agora, discutimos um tipo específico de Web Services: os Big Web Services.
I Padrão do W3C.
I Baseado em SOAP, XML.
I Relativamente complexos.
Uma alternativa em voga atualmente são os RESTful Web Services. I Baseados no padrão arquitetural REST (Representational State Transfer ).
I Abstração da arquitetura da Web.
I O padrão REST determina uma série de restrições na iteração entre os componentes do sistema.
RESTful Web Services: Visão Geral (II)
Exemplos de restrições impostas pelo padrão REST: I Modelo cliente-servidor.
I Comunicação stateless.
I Respostas podem ser cacheadas.
I Interface uniformizada dos serviços.
F Uso de URIs (identificam funcionalidades e recursos).
F Mensagens auto-descritivas (contém toda a informação necessária para entendê-las; não sendo necessário estado).
RESTful Web Services: Visão Geral (III)
Em termos práticos, a diferença entre Big Web Services e RESTful Web Services está na padronização.
Os Big Web Services são padronizados e por isso demandam a geração de inúmeros artefatos.
I Como o arquivo WSDL, por exemplo.
O conceito de RESTful Web Service é bem mais flexível.
I Qualquer serviço ofertado pela Web que esteja de acordo com as restrições arquiteturais REST.
Esta liberdade faz com que seja mais fácil desenvolver um Web Service do tipo REST. I Por outro lado, é difícil elaborar uma API genérica, especialmente do lado do cliente.
RESTful Web Services: JAX-RS
O J2EE inclui uma API para auxiliar na criação de RESTful Web Services.
I JAX-RS.
I Análoga à JAX-WS.
Utiliza bastante o conceito de anotações.
I Desenvolvedor anota classes e métodos definindo recursos e ações que podem ser executadas.
As anotações do JAX-WS são de tempo de execução.
I São interpretadas por um servidor de aplicação para gerar as classes auxiliares necessárias para implantar o Web Service.
JAX-RS: Anotações
Há um grande número de anotações na API. Alguns exemplos:
@Path:
I Identifica a URL para a qual a classe será mapeada.
I O atributo especificado para esta anotação será usado como o sufixo da URI.
F Considerando o endereço base do servidor.
@GET:
I Um método anotado com esta anotação processará requisições HTTP do tipo GET. I Há anotações para outros métodos como @POST, @PUT, @DELETE, @HEAD.
JAX-RS: Anotações (II)
@Produces:
I Define o tipo MIME da resposta gerada pelo serviço.
I e.g., “plain/text”, “application/json”.
@Consumes:
I Define o tipo MIME da entrada provida pelo cliente.
JAX-RS: Exemplo de Código
importjavax.ws.rs.GET; importjavax.ws.rs.Produces; importjavax.ws.rs.Path;
// The Java class will be hosted at the URI path "/helloworld" @Path("/helloworld")
public classHelloWorldResource{
// The Java method will process HTTP GET requests @GET
// The Java method will produce content identified by the MIME Media // type "text/plain"
@Produces("text/plain")
publicStringgetClichedMessage() { // Return some cliched textual content return"Hello World";
} }
Web Service servido na URL (relativa) /helloworld.
Requisições HTTP do tipo GET resultam em chamadas ao método getClinchedMessage().
JAX-RS: Exemplo de Código (II)
E se precisarmos receber conteúdo do usuário?
@POST
@Consumes("text/plain")
publicvoidpostClichedMessage(String message) { // Store the message
}
JAX-RS: A Anotação @Path e Templates
Vimos um uso bastante simplificado da anotação @Path.
I Ela recebia como atributo uma String especificando uma URL estática (/helloworld). Esta anotação, no entanto, é bem mais poderosa.
Ela permite que especifiquemos templates.
Estes templates podem ser usados, por exemplo, para usarmos parte de uma URL como uma variável/parâmetro.
JAX-RS: A Anotação @Path e Templates (II)
Considere o seguinte exemplo:I Queremos criar um Web Service para consultar informações sobre usuários de um sistema.
I O Web Service será mapeado para a URL /users/.
I O cliente deve especificar o nome do usuário (do qual quer obter informações).
I Uma opção seria colocar o nome do usuário no corpo da requisição.
I Ao invés disso, podemos usar uma solução baseada em templates:
@Path("/users/{username}") public classUserResource {
@GET
@Produces("text/xml")
publicStringgetUser(@PathParam("username")String userName) { ...
} }
A sintaxe {username} cria uma variável username.
Se o cliente acessa a URL /users/diego esta variável terá o valor diego. Esta variável pode ser acessada pelos métodos.
JAX-RS: A Anotação @Path e Templates (III)
É possível ainda especificar o formato das variáveis através de expressões regulares. Suponha no exemplo anterior que nomes de usuário só podem ser compostos por letras e números.
Podemos restringir isso com a seguinte declaração:
@Path("/users/{username: [a-zA-Z][a-zA-Z0-9]*}") public classUserResource {
@GET
@Produces("text/xml")
publicStringgetUser(@PathParam("username")String userName) { ...
} }
Se tentamos acessar a URL /users/diego3, o acesso é bem sucedido. Se tentamos acessar a URL /users/diego_passos, recebemos um erro 404.
JAX-RS: A Anotação @Path e Templates (IV)
Também podemos definir um template com mais de uma variável. Exemplo: queremos nome e sobrenome do usuário.
@Path("/users/{nome}/{sobrenome}") public classUserResource {
@GET
@Produces("text/plain")
publicStringgetUser(@PathParam("nome")String nome,@PathParam("sobrenome")String sobrenome) { return"Nome completo: "nome+" "+sobrenome;
} }
JAX-RS: A Anotação @Path e Templates (V)
Algumas outras observações:
I Uma mesma variável pode ser especificada múltiplas vezes em uma anotação @Path. F Exemplo: @Path("/users/{nome}/{nome}").
F Neste caso, só serão aceitas requisições a URLs cujos dois últimos componentes sejam iguais.
I Variáveis podem ser vazias. Exemplo:
F @Path("/users/{nome}/home").
F Cliente acessa URL /users//home.
F Variável nome ganha o valor “” (String vazia).
I Note ainda que a anotação @Path pode ser usada para métodos específicos. F Aquele método será mapeado para aquela URL.
JAX-RS: Anotações Designadoras de Métodos de Requisição
São anotações utilizadas para associar um método HTTP a um método da classe. Vimos exemplos de uso de duas: @GET e @POST.
São também suportadas: I @PUT: incluir um recurso. I @DELETE: remover um recurso. I @HEAD: informações sobre um recurso.
Os métodos HTTP HEAD e OPTIONS são suportados por padrão, mesmo se não implementados explicitamente.
I Implementação padrão do HEAD retorna o mesmo que a implementação do GET, sem o conteúdo.
JAX-RS: Anotações Designadoras de Métodos de Requisição (II)
A implementação do OPTIONS é mais interessante.Ela retorna (como esperado) o conjunto de requisições que são suportadas. Mas também retorna um documento WADL:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <applicationxmlns="http://wadl.dev.java.net/2009/02">
<docxmlns:jersey="http://jersey.java.net/"jersey:generatedBy="Jersey: 2.10.4 2014-08-08 15:09:00"/> <grammars/>
<resourcesbase="http://172.16.10.75:8080/WebService2/services/"> <resourcepath="BasicPost">
<methodid="postClinchedMessage"name="POST"> <request>
<representationmediaType="text/plain"/> </request>
<response>
<representationmediaType="text/plain"/> </response>
</method> </resource> </resources> </application>
JAX-RS: Anotações Designadoras de Métodos de Requisição (III)
O WALD é um formato de documento utilizado para descrever aplicações Web em geral. I Hoje, o exemplo mais comum de uso é com os RESTful Web Services.
Provê informação sobre as operações oferecidas. I Parâmetros esperados.
I Formato da entrada.
I Formato da resposta.
JAX-RS: Anotações Designadoras de Métodos de Requisição (IV)
Não há uma assinatura definida para os métodos de requisição.
I Eles podem receber inúmeros parâmetros (e.g., extraídos do template da anotação @Path). I Eles podem ter vários tipos de retorno diferentes.
Mas há algumas restrições:
I O tipo de retorno deve ser void, um tipo básico de Java, ou um
javax.ws.rs.core.Response.
JAX-RS: Incluindo Metadados nas Respostas
Isso é feito com as classes Response e ResponseBuilder. Exemplo de funcionalidades:
I Definição de cookies.
I Definição de headers.
I Definição de código de status. I Definição do media type.
Exemplo de código:
@Path("/users/{nome}/{sobrenome}") public classUserResource {
@GET
@Produces("text/plain")
publicResponsegetUser(@PathParam("nome")String nome,@PathParam("sobrenome") { returnResponse.status(404).entity("User not found").build();
} }
JAX-RS: Provedores de Entidades
Suponha que desejemos escrever um Web Service que adiciona um usuário a uma base de dados.
Usuários têm as seguintes propriedades: I Nome completo.
I Apelido.
I Data de nascimento.
I CPF.
I Senha.
Temos uma classe User com esta representação.
O Web Service recebe estes dados no corpo de uma requisição POST. Como obter, a partir da requisição, um objeto do tipo User?
JAX-RS: Provedores de Entidades (II)
Primeira abordagem: receber o corpo da requisição como texto e fazer o parse. Código resultante:
@POST
@Consumes("text/plain") @Produces("text/plain")
publicStringpostClichedMessage(String message) { User u=newUser();
// Parse da mensagem e preenchimento do objeto. ...
// Adição do usuário à base. ...
// Envio da resposta ao usuário. ...
}
Há muita coisa sendo feita neste método.
Note também que a assinatura do método é enganosa:
I Implicitamente, o método espera que o corpo da mensagem seja um User. I Ou melhor, sua representação em texto.
JAX-RS: Provedores de Entidades (III)
Podemos implementar isso de outra forma: usando um provedor de entidade. Exemplo de código:
@Provider
@Consumes("text/user")
public classUserUnmarshaller implementsMessageBodyReader{ @Override
publicbooleanisReadable(Class aClass,Type type,Annotation[]annotations,MediaType mediaType) { return true;
} @Override
publicObjectreadFrom(Class aClass,Type type,Annotation[]annotations,MediaType mediaType,
MultivaluedMap multivaluedMap,InputStream inputStream)throwsIOException,WebApplicationException{ User result =null;
try{
// Parse da mensagem (lida do inputStream). // Resultado deve ser colocado no objeto result. // ... }catch(Exception e) { e.printStackTrace(); } returnresult; } }
JAX-RS: Provedores de Entidades (IV)
A classe do slide anterior implementa um decodificador para o tipo de mídia text/user. I Fictício, criado para este exemplo.
Tendo a classe UserUnmarshaller carregada, podemos usar este tipo de mídia para tornar transparente o parse dos dados:
@POST
@Consumes("text/user") @Produces("text/plain")
publicStringpostClichedMessage(User u) {
// Já recebemos automaticamente uma instância de User preenchida com os dados da requisição. // Basta fazer a inserção no banco.
...
// Envio da resposta ao usuário. ...
JAX-RS: Provedores de Entidades (V)
Podemos fazer também o processo inverso. I Na resposta, enviamos um User.
I Ou melhor, sua representação textual.
Novamente, ao invés de fazer o próprio método, escrevemos um provedor de entidade. Neste caso, no entanto, como se trata de uma resposta, o provedor precisa fazer a operação contrária.
JAX-RS: Provedores de Entidades (VI)
Para tipos mais comuns, a JAX-RS já provê provedores de entidades padrão. Estes provedores mapeiam tipos comuns do Java a tipos de mídia comuns. Por exemplo:
I byte[] ↔ */*.
I String ↔ text/*.
I File ↔ */*.
I MultivaluedMap<String,String> ↔ application/x-www-form-urlencoded. Para usá-los, basta definir o método com os tipos de parâmetro/retorno adequados aos tipos de mídia.
JAX-RS: Anotação @Produces
Como visto nos exemplos anteriores, esta anotação declara o tipo de conteúdo que é produzido por um dado método.
A anotação também pode ser usada na declaração da classe.
I Neste caso, todos os métodos podem produzir o tipo especificado de conteúdo.
I Se um método específico declara a anotação, esta se sobrepõe à anotação da classe.
Note que a anotação define os tipos de dados que podem ser gerados pelos métodos. I Por isso, a anotação pode conter mais de um tipo.
JAX-RS: Anotação @Produces
Quando um cliente envia uma requisição, ele pode especificar um conjunto de formatos aceitáveis.
Neste caso, o JAX-RS procura entre todos os métodos que atendem àquele tipo de requisição o que se encaixa melhor ao que o cliente requisitou.
I Se nenhum método se encaixa, servidor retorna um erro 406.
Exemplo:
@Path("/myResource") @Produces("text/plain") public classSomeResource {
@GET
publicStringdoGetAsPlainText() { ...
} @GET
@Produces("text/html") publicStringdoGetAsHtml() {
... } }
JAX-RS: Anotação @Consumes
Também já vista em exemplos anteriores.
Equivalente à @Produces para dados de entrada. Segue as mesmas convenções da anotação @Produces:
I Pode ser aplicada a uma classe inteira.
I Pode receber mais de uma especificação de formato de dados.
I JAX-RS utiliza para identificar qual o método (mais) adequado para tratar uma requisição.
De forma similar à anotação @Produces, se nenhum método declara consumir um dado formato, é retornado um erro 415.
JAX-RS: Recebendo Parâmetros
Vimos anteriormente um exemplo de obtenção de parâmetros através da URL. I Definimos um template usando a anotação @Path.
I Declaramos parâmetros de métodos usando a anotação @PathParam.
Mas existem muitas maneiras de um cliente codificar seus parâmetros em uma requisição HTTP: I Query. I Form. I Cookies. I Headers. I Matrix.
JAX-RS: Query Parameters
São parâmetros passados em uma URL explicitamente.
I Exemplo: http://servidor/webservice/user?username=diego&action=remove Podemos acessar estes parâmetros com a anotação @QueryParam.
I Podemos ainda definir valores padrão com a anotação @DefaultValue. F Usados em caso de omissão.
@Path("smooth") @GET
publicResponsesmooth(
@DefaultValue("2")@QueryParam("step")intstep, @DefaultValue("true")@QueryParam("min-m")booleanhasMin, @DefaultValue("true")@QueryParam("max-m")booleanhasMax, @DefaultValue("true")@QueryParam("last-m")booleanhasLast, @DefaultValue("blue")@QueryParam("min-color")ColorParam minColor, @DefaultValue("green")@QueryParam("max-color")ColorParam maxColor, @DefaultValue("red")@QueryParam("last-color")ColorParam lastColor ) { ... }
JAX-RS: Query Parameters
Há restrições em relação aos tipos dos parâmetros. Só se pode usar tipos com as seguintes características:
I Tipos primitivos, exceto char.
I Classes correspondentes a tipos primitivos, exceto Character.
I Qualquer classe com construtor que recebe um único argumento do tipo String.
I Qualquer classe com um método estático valueOf(String).
I List<T>, Set<T>, SortedSet<T>, onde T cai em um dos casos anteriores. F Usado para atributos multi-valorados.
Caso não seja possível mapear o valor presente na URL para o tipo do parâmetro, é gerado um erro 400.
JAX-RS: Outros Tipos de Parâmetros
Há suporte para as seguintes outras anotações relacionadas a parâmetros: I @CookieParam: associa um parâmetro a um cookie enviado pelo cliente.
I @HeaderParam: associa um parâmetro a um header da requisição.
I @FormParam: associa um parâmetro a um campo de formulário enviado pelo cliente.
I @MatrixParam: associa um parâmetro ao matrix parameter correspondente.
F Matrix parameters são similares aos query parameters.
F Mas permitem diferenciar parâmetros por nível da URL.
F Exemplo: http://servidor/webservice/categoria;nome=frutas/objetos;nome=abacaxi
I Em todos os casos, a correspondência é feita pelo nome.
F Nome do parâmetro declarado no método deve bater com o nome do cookie/header/...
Note que, no caso do @FormParam, o tipo de conteúdo deve ser application/x-www-form-urlencoded.
JAX-RS: A Anotação @Context
Anotação especial que pode ser usada em certos parâmetros de um método. Permite obter “informações contextuais”.
I Referências a objetos específicos que contem informações sobre a requisição.
Exemplo:
I Objeto UriInfo: permite obtenção de informações sobre a URL. I Objeto HttpHeaders: permite acesso mais direto aos headers.
JAX-RS: A Anotação @Context (II)
Exemplo de código:
@GET
publicStringget(@ContextUriInfo ui,@ContextHttpHeaders hh) { MultivaluedMap<String,String>queryParams=ui.getQueryParameters(); MultivaluedMap<String,String>pathParams=ui.getPathParameters(); MultivaluedMap<String,String>headerParams=hh.getRequestHeaders(); Map<String,Cookie>pathParams=hh.getCookies();
...
returnui.getAbsolutePath().toString(); }
JAX-RS vs. Servlets
De certa forma, a JAX-RS é bem parecida com os Servlets HTTP. I Ambos permitem criar aplicações Web.
I Escrevemos uma classe que é mapeada para uma ou mais URLs.
I Métodos específicos nesta classe são chamados pelo container quando tipos específicos de requisição são recebidas.
I Podemos manipular parâmetros, cookies, headers... Qual a diferença, então?
JAX-RS vs. Servlets (II)
A resposta é não.
Servlets são componentes capazes de tratar requisições (tipicamente HTTP) em um nível bastante baixo.
I É possível manipular todos os aspectos das requisições e respostas, desde que obedecidas as
regras do protocolo HTTP.
I Podemos, por exemplo, criar um Web Service RESTful.
A JAX-RS é uma API especificamente dedicada a criação de serviços RESTful. I Temos controle sobre muita coisa, mas dentro das limitações arquiteturais REST.
I Por exemplo, não podemos (ou não deveríamos) guardar estado interno sobre os clientes através de sessões.
Em suma:
I É mais fácil implementarmos serviços RESTful com a JAX-RS.
Referências
Conteúdo e exemplos baseados em: I Tutorial da Oracle sobre Web Services.