4Developers 2015: Dlaczego 99% firm, które tworzą API RESTowe kłamie? - Bartek Andrzejczak,...

Post on 15-Jul-2015

56 views 1 download

Transcript of 4Developers 2015: Dlaczego 99% firm, które tworzą API RESTowe kłamie? - Bartek Andrzejczak,...

DLACZEGO 99% FIRM, KTÓRE TWORZĄAPI RESTOWE KŁAMIE?

/ Bartek Andrzejczak @baandrzejczak

O MNIEdev @ twitter github blog @

BMS@baandrzejczak@bandrzejczakbandrzejczak.com

AGENDAGeneza kłamstwaCo to jest HATEOAS?Jak ważny jest HATEOAS?Czy HATEOAS jest mi potrzebny?HATEOAS od strony serweraHATEOAS od strony klienta

MOŻE POWTÓRZYMY ANGIELSKI?

CZYM WŁAŚCIWIE JEST REST?This chapter introduces and elaborates the

Representational State Transfer (REST)architectural style for distributed hypermedia

systems (...)

(...) a style is a named set of constraints onarchitectural elements that induces the set of

properties desired of the architecture.

"Architectural Styles and the Design ofNetwork-based Software Architectures"

Roy T. Fielding

OGRANICZENIA NARZUCANE PRZEZ RESTPodział klient-serwerBezstanowość serweraCacheJednolity interfejs

Identyfikacja zasobówManipulacja zasobami przez reprezentacjeSamoopisujące się wiadomościHATEOAS

Podział na warstwyCode-on-demand (Opcjonalne)

HYPERMEDIA AS THE ENGINE OFAPPLICATION STATE

Role, artefakty, zdarzenia oraz reguły Scrumasą niezmienne i choć możliwe jest

wykorzystanie tylko wybranych jegoelementów, wynikiem takiego postępowania

nie będzie Scrum. Scrum istnieje tylko wswojej pełnej postaci i sprawdza się doskonale

w roli ramy dla innych technik, metodyk czypraktyk.

Scrum Guide

JAK WAŻNY JEST HATEOAS?In order to obtain a uniform interface,

multiple architectural constraints are neededto guide the behavior of components. REST is

defined by four interface constraints:identification of resources; manipulation of

resources through representations; self-descriptive messages; and, hypermedia as theengine of application state. These constraints

will be discussed in Section 5.2.

"Architectural Styles and the Design ofNetwork-based Software Architectures"

Roy T. Fielding

The name “Representational State Transfer” is intended toevoke an image of how a well-designed Web application

behaves:a network of web pages (a virtual state-machine)where the user progresses through the application byselecting links (state transitions)resulting in the next page (representing the next state of theapplication) being transferred to the user and rendered fortheir use

CO DAJE NAM HATEOAS?

KIEDY HATEOAS JEST STRATĄ CZASU?Brak wyraźnego flow aplikacjiCRUDMałe API

ZALETYSterowanie przepływem danych w aplikacjiSterowanie dostępnymi podzasobamiLuźniejsze związanie serwera i klientaDodatkowa dokumentacja API (jednak niewystarczająca)

WADYWięcej pracyWięcej transferuBrak ekspertów w temacieTworzenie API dla nieistniejącego klienta

DLA JAKIEGO KLIENTA NADAJE SIĘ HATEOAS?

FORMAT LINKÓW

HTML<a href="http://swapi.co/api/films/1/">A New Hope</a>

JSON API "name": "Luke Skywalker", "height": "1.72 m", "mass": "77 Kg", "hair_color": "Blond", "links": "species": "self": "http://swapi.co/api/species/1/", "all": "http://swapi.co/api/species/"

PAYPAL API "name": "Luke Skywalker", "height": "1.72 m", "links": [ "href": "http://swapi.co/api/people/1/", "rel": "self", "method": "GET" , "href": "http://swapi.co/api/species/1/", "rel": "species", "method": "GET" ]

VERTICALRESPONSE API "name": "Luke Skywalker", "height": "1.72 m", "mass": "77 Kg", "hair_color": "Blond", "links": "self": "url": "http://swapi.co/api/people/1/" , "homeworld": "url": "http://swapi.co/api/planets/1/"

HYPERTEXT APPLICATION LANGUAGE (HAL) "name": "Luke Skywalker", "height": "1.72 m", "mass": "77 Kg", "hair_color": "Blond", "_links": "self": "href": "http://swapi.co/api/people/1/", "title": "Luke Skywalker" , "http://swapi.co/api/rels/species": "href": "http://swapi.co/api/species/1/", "title": "Human"

HYPERTEXT APPLICATION LANGUAGE (HAL) "_links": "self": "href": "/" , "curies": [ "name": "ht", "href": "http://haltalk.herokuapp.com/rels/rel", "templated": true ], "ht:users": "href": "/users"

JSON-LD "name": "Luke Skywalker", "height": "1.72 m", "mass": "77 Kg", "hair_color": "Blond", "@self": "http://swapi.co/api/people/1/", "@species": "http://swapi.co/api/species/1/",

NAGŁÓWKI HTTPLink: <http://swapi.co/api/species/1/>; rel="species"

HATEOAS NA SERWERZE

JERSEY DECLARATIVE LINKING

NAJPROSTSZY PRZYKŁAD@Path("/people")public class PeopleResource @GET public List<Person> list() ... @GET @Path("/id") public Person get(@PathParam("id") String personId) return new PeopleRepository().find(personId);

public class Person public String name; @InjectLink(resource=PeopleResource.class) public URI self;

"name": "Luke Skywalker", "self": "http://swapi.co/api/people"

PARAMETRY ŚCIEŻEK@Path("/people/id")public class PersonResource ...public class Person public String name; @InjectLink( resource=PlanetResource.class, bindings= @Binding("$resource.homeworldId") ) public URI homeworld; @JsonIgnore public String homeworldId;

"name": "Luke Skywalker", "homeworld": "http://swapi.co/api/planets/1"

STAŁE ŚCIEŻKIpublic class Root @InjectLink("/films", rel="films") public URI films;

"films": "http://swapi.co/api/films"

LINKI W NAGŁÓWKACH@InjectLinks( value=@InjectLink("planets/$resource.homeworldId"), rel="homeworld")public class Person public String name; @JsonIgnore public String homeworldId;

HTTP 200 OKAllow: GET, HEAD, OPTIONSContent­Type: application/jsonVary: AcceptLink: <http://swapi.co/api/planets/1>; rel="homeworld"

"name": "Luke Skywalker"

PLUSYWybór pomiędzy linkami w nagłówkach i wreprezentacjach

MINUSYBrak możliwości przesyłania tylko niektórychlinkówSilne połączenie reprezentacji z zasobamiAnnotation hell

JAX-RS 2.0

Tylko imperatywna konstrukcja linków.@Path("/people/id")public class PersonResource @GET public Response get(@PathParam("id") String personId) return Response .ok(new PeopleRepository().find(personId)) .links(Link.fromResource(PersonResource.class) .rel("self").build(personId)) .build();

HTTP 200 OKContent­Type: application/jsonLink: <http://swapi.co/api/people/1>; rel="self"

Ścieżki można pobierać z metod...@Path("/people/id")public class PersonResource @GET public Response get(@PathParam("id") String personId) return Response .ok(new PeopleRepository().find(personId)) .links(Link .fromMethod(PersonResource.class, "films") .rel("films").build(personId)) .build();

@GET @Path("/films") public Response films() ...

HTTP 200 OKContent­Type: application/jsonLink: <http://swapi.co/api/people/1/films>; rel="films"

...lub hardcodować@GETpublic Response get(@PathParam("id") String personId) Person person = new PeopleRepository().find(personId); String fatherId = person.findFather(); return Response .ok() .links(Link .fromPath("/people/id") .rel("father").build(fatherId)) .build();

HTTP 200 OKContent­Type: application/jsonLink: <http://swapi.co/api/people/darthVader>; rel="father"

"name": "Luke Skywalker"

PLUSYBrak ingerencji w klasy reprezentacjiImplementacja przez wiele różnych frameworków

MINUSYPłaska struktura linkówNagłówek trudniej jest analizować niżreprezentację

SPRING-HATEOAS

Aby dodawać linki do klasy, musi ona rozszerzać klasęResourceSupport - linki staną się częścią reprezentacji.

public class Person extends ResourceSupport public String name; public String height; public String mass; public String hair_color;

Linki dodajemy bezpośrednio do reprezentacjiimport static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;

@Controllerpublic class PersonController @RequestMapping("/people/id") @ResponseBody public HttpEntity<Person> get(@PathVariable("id") String personId) Person person = new PeopleRepository().find(personId); person.add( linkTo(methodOn(PersonController.class).get(personId)) .withSelfRel() ); return new ResponseEntity<Person>(person, HttpStatus.OK);

Wynik: "name": "Luke Skywalker", "height": "1.72 m", "mass": "77 Kg", "hair_color": "Blond", "_links": "self": "href": "http://swapi.co/api/people/1"

PLUSYMinimalna ingerencja w klasy reprezentacjiMożliwość zagnieżdżania linkówObsługa różnych formatów linkówWsparcie JAX-RS

MINUSYNarastająca logika w kontrolerach, w przypadkulinkowania bardziej skomplikowanych struktur(np. kolekcji)

HATEOAS W KLIENCIE

JAVA (JAX-RS)

Linki w reprezentacjach "name": "Luke Skywalker", "links": "homeworld": "uri": "http://swapi.co/api/planets/1"

final Person table = target.request().get(Person.class);

URI homeworldURI = table.getLinks().getHomeworld().getUri();WebTarget homeworldTarget = client.target(homeworldURI);

Linki w nagłówkachHTTP 200 OKContent­Type: application/jsonLink: <http://swapi.co/api/planets/1>; rel="homeworld"Link: <http://swapi.co/api/species/1>; rel="species"

"name": "Luke Skywalker"

final Response response = target.request().get();

URI homeworldURI = response.getLink("homeworld").getUri();URI speciesURI = response.getLink("species").getUri();

JAVASCRIPT (ANGULARJS)

Linki w reprezentacjach "name": "Luke Skywalker", "links": "father": "uri": "http://swapi.co/api/people/2"

var personResource = $resource("/api/people/:personId");

var luke = personResource.query(personId: 1, function () var lukesFather = $resource(luke.links.father) .query(null, function () console.log(lukesFather); ););

Linki w nagłówkachHTTP 200 OKContent­Type: application/jsonLink: <http://swapi.co/api/people/2>; rel="father"

"name": "Luke Skywalker"

var personResource = $resource("/api/people/:personId");

var luke = personResource.query(personId: 1, function () var lukesFather = firstPerson.resource("father") .query(null, function () console.log(lukesFather); ););

Link: </people>; rel="people"; actions="[ 'name':'add','method':'POST', 'name':'list','method':'GET' ]"

$http.get("/").success( function (root) var peopleList = root.links.people.list(); root.links.people.add( "name": "Darth Vader", "height": "1.8" ); );

https://github.com/bandrzejczak/bpm-console-gui

https://github.com/bandrzejczak/bpm-console-rest

PODSUMOWANIEHATEOASMaszyna stanówRosnące wsparcie bibliotek

SWAPI.CO

PYTANIA?

DZIĘKI!