rest java one
DESCRIPTION
por Eder Ignatowicz - Senior Architect na Dextra SistemasTRANSCRIPT
Then Jesus said, "Come to me, all of you who are weary and carry heavy burdens,
and I will give you REST."Matthew 11:28
Approaching Pure REST in Java:
HATEOAS and HTTP TuningEder Ignatowicz
Senior Architect, Dextra
JavaOne LATAM 2012
Software Craftsman @ Dextra(Arquitetura, NoSQL, Devops, QA)
Doutorando na Unicamp (Polyglot Persistence em Cidades Digitais)
Professor na Faccamp e Unisal
Editor líder no InfoQ Brasil
@ederign
DextraGalera Ponta Firme e que Manda bem
Ambiente de Melhoria ContínuaProjet
os De
safia
dores
Quali
dade
de Vi
da
DextraGalera Ponta Firme e que Manda bem
Ambiente de Melhoria ContínuaProjet
os De
safia
dores
Quali
dade
de Vi
da
WE’RE HIRING!!!
http://geekswithblogs.net/images/geekswithblogs_net/ugandadotnet/eai-spaghetti.jpg
TEMPOS DIFÍCEIS...
MUITOS “PADRÕES”RMI, Corba, DCOM
MUITOS FORNECEDORESSun, Microsoft, OASIS, OMG
MUITAS LÁGRIMASNão existia interoperabilidade
Reinvenção da rodaVendor “lock-in”
http://geekswithblogs.net/images/geekswithblogs_net/ugandadotnet/eai-spaghetti.jpg
http://geekswithblogs.net/images/geekswithblogs_net/ugandadotnet/eai-spaghetti.jpg
RESTREpresentational State Transfer
Roy F
ieldin
g Diss
erta
tion
ESTILO DE ARQUITETURA DE SOFTWARE PARA SISTEMASDISTRIBUÍDOS HYPERMEDIA SEMELHANTES A WORLDWIDE WEB
PRINCÍPIOS E RESTRIÇÕES REST
CLIENTE-SERVIDORSERVIDOR STATELESS
CACHEINTERFACE UNIFORME
IDENTIFICAÇÃO DE RECURSOSMANIPULAÇÃO DESTES RECURSOS ATRAVÉS DE REPRESENTAÇÕES
MENSAGENS AUTO-DESCRITIVASHYPERMEDIA COMO ENGINE DO ESTADO DA APLICAÇÃO
ARQUITETURA EM CAMADASCÓDIGO SOB DEMANDA
(OPCIONAL)
PUT
Buscar recursos, cache
Criar um novo recurso
Atualizar (todo o) recurso existente
Remover um recurso
Atualizar (parte de) um recurso
DELETE
PATCH
POST
GET
VERBO URI (Substantivo) AçãoPOST /bookmarks/create CriarGET /bookmarks/show/1 Visualizar
POST /bookmarks/update/1 AlterarGET/POST /bookmarks/delete/1 Apagar
NÃO RESTful
VERBO URI (Substantivo) AçãoPOST /bookmarks/ CriarGET /bookmarks/1 VisualizarPUT /bookmarks/1 Alterar
DELETE /bookmarks/1 Apagar
RESTful
NOSSOS SONHOS NA INTEGRAÇÃO DE SISTEMASESCALABILIDADE
SEGURANÇA
BAIXO ACOPLAMENTO
TOLERÂNCIA A FALHAS
“O que precisa ser feito para que entendam que no estilo arquitetural REST o hypertext é um
pré-requisito?Em outras palavras, se a engine do estado da
aplicação (e consequentemente sua API) não é guiada por hypertext, então sua aplicação não
pode ser RESTful e nem ter uma API REST. PONTO. Existe por ai algum manual que necessite ser
consertado?”
“
”Roy Thomas Fielding
Richardson’s Maturity Model
Nível 0: O pântano do POX
Nível1: Recursos
Nível 2: Verbos HTTP
Nível 3: Controles Hypermedia
REST Sagrado
Nível 0: O pântano do POX
Uma URI, um método HTTPXML-RPC / SOAP / POX
HTTP usado como transporte
POST /agendamentoService HTTP/1.1[headers...]
<appointmentRequest> <slot doctor = "rcmito" start = "1400" end = "1450"/> <patient id = "ederi"/></appointmentRequest>
Nível 1: RecursosCada recurso tem uma única URI
URI tunnelingUm único verbo HTTP (POST ou GET)
HTTP usado como transporte
POST /slots/1234 HTTP/1.1[headers...]
<appointmentRequest> <patient id = "ederi" /></appointmentRequest>
Level 2: HTTP VerbsMuitas URIs, utilizando corretamente
os verbos HTTPUso correto dos códigos de respostaExpõe estado e não comportamento
CRUDGET /doctors/rcmito/slots?date=20121010?status=open HTTP/1.1[headers...]HTTP/1.1 200 OK
<openSlotList> <slot id = “1234” start=”1400” end=”1450” /> <slot id = “1234” start=”1600” end=”1650”/></openSlotList>
Nível 3: Controles Hypermedia
Hypermedia As The Engine of Application State (HATEOAS)
Recursos auto descritivos
Clientes só precisam saber a URI root (home page) de uma API e os media types utilizados
O resto é HTTP e links
O nome Representational State Transfer foi escolhido com a intenção de criar uma imagem de
como uma aplicação Web bem desenvolvida se comporta: uma rede de páginas web (máquina de
estados), onde o usuário navega selecionando links (transições de estados), resultando na
próxima página (próximo estado da aplicação).
“
”Roy Thomas Fielding
ESTADO
ESTADOESTADO
ESTADOESTADO
http://uri
Transição
Transição
Transição
Transição
HTTP/1.1 201 CreatedLocation: http://jogano10.com/slots/1234/appointment[various headers]
<appointment> <slot id = "1234" doctor = "rcmito" start = "1400" end = "1450"/> <patient id = "ederi"/> <link rel = "/linkrels/appointment/cancel" uri = "/slots/1234/appointment"/> <link rel = "/linkrels/appointment/addTest" uri = "/slots/1234/appointment/tests"/> <link rel = "self" uri = "/slots/1234/appointment"/> <link rel = "/linkrels/appointment/updateContactInfo" uri = "/patients/ederi/contactInfo"/></appointment>
@Path("/cartao/{cardId}")public class Cartao {
@GET @Path("/saldo") @Produces("text/plain") public Saldo saldo(@PathParam("cardId") String card) { return getSaldo(card); }...}
HTTP Method Binding Serialização
automática
Recursos
Injeção dos parâmetros
JAX-RS API
// Server APIResponse res = Response.ok(order) .link("http://.../orders/1/ship", "ship") .build(); // Client APIResponse order = client.target(…) .request("application/xml").get();
if (order.getLink(“ship”) != null) { Response shippedOrder = client .target(order.getLink("ship")) .request("application/xml").post(null); }
HypermediaSuporte a HATEOAS
Mas, eu preciso mais do que a semântica
GET/POST/PUTDELETE/PATCH "But my model is different!".
You are not a special or unique snowflake.- should I build another protocol? - should I add a new HTTP method?- answer 1: tough shit. you're wrong, it can be modeled with the uniform interface, it's sufficiently general.
Concorrência?If you do two PUTs at once, one wins. The solution depends on what you need. For a lot of applications, "last update wins" is a fine strategy. If not, Etags are the solution.
Transações... ?!?I want to debit one guy's account at the same time, and only if, I can credit another guy's account. I could do two PUTs in a row, but that's begging for inconsistencies to creep into the system.answer 1: YAGNI. Answer 2: what you probably want is a new resource to encapsulate
Não existe a necessidade de API nem de
versionamento de APIs!!!- So Iʼll wrap up where we started. Youʼll find that the closer you move to REST, the less of a distinct API youʼll have at all. The “regular” application and its API will become one.- The rub, and itʼs a big rub, is not a technical one but a social one. Building on a API requires trust of stability, and “traditional” websites donʼt provide for
http://2.bp.blogspot.com/-JmDsZ1ESAWg/TyiHY8MMdzI/AAAAAAAAEDs/lFZxR0z5fGk/s1600/escalada1.jpg
Escalabilidade HTTP + JAX-RS
HTTPCaching features
allowed (or not)expiration
intermediary caches allowed (or not)
validationstorable(or not)
HTTP Quando “cachear”?
Expires headerExpires: Sun, 04 Aug 2012 16:00 GMT
Cache-control headerCache-Control: no-cache
Cache-Control: public, max-age=3000
Validation HeaderLast-Modified: Mon, 29 Jun 2012 02:28:12 GMT
ETag: "3e86-410-3596fbbc"
JAX-RS e Cache Control
@Path("/doutores")public class DoutoresService { @Path("/{id}") @GET @Produces("application/xml") public Response getDoutores(@PathParam("id") int id) { List<Doutor> doutores = //getDoutores CacheControl cc = new CacheControl(); cc.setMaxAge(3000); return Response.ok(doutores).cacheControl(cc).build(); }}
JAX-RS e Gets Condicionais
Last-Modified: Mon, 29 Jun 2012 02:28:12 GMTETag: "3e86-410-3596fbbc"
O cache está valido?304 “Not Modified”
Não está?200 “OK” + recurso válido
@Path("/doutores")public class DoutoresService { @Path("/{id}") @GET @Produces("application/xml") public Response getDoutores(@PathParam("id") int id, @Context Request request) { EntityTag tag = // get tag mais atualizada ResponseBuilder builder = null;
builder = request.evaluatePreconditions(tag); if (builder != null){ return builder.cacheControl(cc).build(); } Object doutores = // getDoutores return Response.ok(doutores).cacheControl(cc).build(); }}
JAX-RS e Cache-Control
@Path("/doutores")public class DoutoresService { @Path("/{id}") @GET @Produces("application/xml") public Response getDoutores(@PathParam("id") int id) { List<Doutor> doutores = //getDoutores CacheControl cc = new CacheControl(); cc.setMaxAge(3000); return Response.ok(doutores).cacheControl(cc).build(); }}
O mesmo princípio se aplica para PUTs e POSTs
condicionais(updates concorrentes)
Client API @Before public void setUp() throws Exception { // start the server server = Main.startServer(); // create the client Client c = ClientFactory.newClient(); target = c.target(Main.BASE_URI); }
@After public void tearDown() throws Exception { server.stop(); }
@Test public void testExtratoSemHATEOAS() { String responseMsg = target.path("cartao/1/saldo").request() .get(String.class); assertEquals(value, responseMsg); }
Interceptors/Handlers
Pontos de extensão: Logging, Compression, Security, etc.
@Providerclass LoggingFilter implements RequestFilter, ResponseFilter {
@Override public FilterAction preFilter(FilterContext ctx) throws IOException { logRequest(ctx.getRequest()); return FilterAction.NEXT; } @Override public FilterAction postFilter(FilterContext ctx) throws IOException { logResponse(ctx.getResponse()); return FilterAction.NEXT; }
Async
Suporte na API Client
// Acesso URITarget target = client.target("http://.../atm/balance")… // Chamada async e callbackFuture<?> handle = target.request().async().get( new InvocationCallback<String>() { public void complete(String balance) { … } public void failed(InvocationException e) { … } });