Upgrade to Pro — share decks privately, control downloads, hide ads and more …

HATEOAS dla ludzi!

Matt Stasch
September 08, 2018

HATEOAS dla ludzi!

W swojej prezentacji opowiem o dziwnym stworzeniu, które nazywa się HATEOAS i jest częstym tematem prezentacji, jednak w praktyce widuje się je znacznie rzadziej. Postaram się podzielić doświadczeniami z moich poprzednich projektów, gdzie miałem okazję implementować i konsumować API z HATEOASem. Opowiem, dlaczego warto i dlaczego czasem jednak nie warto, jakie wyzwania stawia(ł) przed nami HATEOAS oraz jak dzięki niemu zmieniło się moje patrzenie na oprogramowanie. Postaram się pokazać praktyczne przykłady.

Matt Stasch

September 08, 2018
Tweet

More Decks by Matt Stasch

Other Decks in Programming

Transcript

  1. Wikipedia ORP Kom. Piłsudzki Barycz (Wyszanów) Antylopowiec modry Deklaracja niepodległości

    Polska Odra Sułów Wdrażanie Ramowej Dyrektywy Wodnej w zlewni Baryczy
  2. Wikipedia ORP Kom. Piłsudzki Barycz (Wyszanów) Antylopowiec modry Deklaracja niepodległości

    Polska Odra Sułów Wdrażanie Ramowej Dyrektywy Wodnej w zlewni Baryczy Mieszko I Józef Stalin Andrzej Duda
  3. Wikipedia ORP Kom. Piłsudzki Barycz (Wyszanów) Antylopowiec modry Deklaracja niepodległości

    Polska Odra Sułów Wdrażanie Ramowej Dyrektywy Wodnej w zlewni Baryczy Mieszko I Józef Stalin Andrzej Duda
  4. GET /notes/3381 HTTP/1.1 Host: api.service.com Authorization: bearer <token> --- {

    data: {...}, links: [ { "rel": "self", "href": "https://api.service.com/note/3381", "method": "GET" }, { "rel": "followers", "href": "https://api.service.com/note/3381/followers", "method": "GET" }, ... ] }
  5. GET /notes/3381 HTTP/1.1 Host: api.service.com Authorization: bearer <token> --- {

    data: {...}, links: [ { "rel": "self", "href": "https://api.service.com/notes/3381", "method": "GET" }, { "rel": "follow", "href": "https://api.service.com/notes/3381/followers", "method": "POST" }, ... ] }
  6. GET /notes/3381 HTTP/1.1 Host: api.service.com Authorization: bearer <token> --- {

    data: {...}, links: [ { "rel": "self", "href": "https://api.service.com/notes/3381", "method": "GET" }, { "rel": "follow", "href": "https://api.service.com/notes/3381/followers", "method": "POST" }, ... ] }
  7. GET /notes/3381 HTTP/1.1 Host: api.service.com Authorization: bearer <token> --- {

    data: {...}, links: [ { "rel": "self", "href": "https://api.service.com/notes/3381", "method": "GET" }, { "rel": "follow", "href": "https://api.service.com/notes/3381/followers", "method": "POST" }, ... ] }
  8. namespace My.Service.REST { public class Hypermedia { public Hypermedia(string rel,

    string method, string href) { ... } public string Rel { get; private set; } public string Method { get; private set; } public string Href { get; private set; } } public class Resource : IResource { protected Resource() { Links = new Dictionary<string, Hypermedia>(); } public IDictionary<string, Hypermedia> Links { get; set; } } }
  9. namespace My.Service.REST { public class Resources<T> : Resource, ICollectionOfResources<T> where

    T: IResource { protected Resources(IEnumerable<T> members) { Members = new ReadOnlyCollection(members) } public ReadOnlyCollection<T> Members { get; set; } } }
  10. namespace My.Service.Controllers { public class NotesController { … public Notes

    Get(NoteId id) { var note = GetNoteById(id) .WrapInResource() .Link("self", _ => _.Get(id)) .Link("follow", _ => _.Follow(id)) … return note; } } }
  11. HTTP 1.1 POST /my/own/resource Host: example.org Authorization: Bearer <token> …

    /my /own /resource HTTP/1.1 200 OK ETag: "4ab4bec9811" Location: ???
  12. HTTP 1.1 POST /my/own/resource Host: example.org Authorization: Bearer <token> …

    /my /own /resource HTTP/1.1 200 OK ETag: "4ab4bec9811" Location: /my/own/resource/123
  13. HTTP 1.1 POST /my/own/resource Host: example.org Authorization: Bearer <token> …

    /my /own /resource HTTP/1.1 200 OK ETag: "4ab4bec9811" Location: /my/own/resource/123 "basePath": "/my/own/"
  14. HTTP 1.1 POST /my/own/resource Host: example.org Authorization: Bearer <token> …

    /my /own /resource HTTP/1.1 200 OK ETag: "4ab4bec9811" Location: /my/own/resource/123 X-Forwarded-BasePath: /my X-Forwarded-BasePath: {$X_Forwarded_BasePath}/own
  15. HTTP 1.1 POST /my/own/resource Host: example.org Authorization: Bearer <token> …

    /my /own /resource HTTP/1.1 200 OK ETag: "4ab4bec9811" Location: /my/own/resource/123 X-Forwarded-BasePath: /my X-Forwarded-BasePath: {$X_Forwarded_BasePath}/own
  16. HTTP 1.1 POST /my/own/resource Host: example.org Authorization: Bearer <token> …

    /my /own /resource HTTP/1.1 200 OK ETag: "4ab4bec9811" Location: /my/own/resource/123 X-Forwarded-BasePath: /my X-Forwarded-BasePath: {$X_Forwarded_BasePath}/own
  17. GET /notes/3381/followers HTTP/1.1 Host: api.service.com Authorization: bearer <token> --- {

    data: {...}, links: [ { "rel": "self", "href": "https://api.service.com/notes/3381/followers", "method": "GET" }, { "rel": "add", "href": "https://api.service.com/notes/3381/followers", "method": "POST" }, ... ] }
  18. GET /notes/3381/followers HTTP/1.1 Host: api.service.com Authorization: bearer <token> --- {

    data: {...}, links: [ { "rel": "self", "href": "https://api.service.com/notes/3381/followers", "method": "GET" }, { "rel": "add", "href": "https://api.service.com/notes/3381/followers", "method": "POST" }, ... ] }
  19. { title: "Skończyła się kawa w kuchni na parterze", status:

    "Open", links: [ { "rel": "self", "href": "https://api.service.com/notes/3381", "method": "GET" }, { "rel": "delete", "href": "https://api.service.com/notes/3381 ", "method": "DELETE" }, ... ] }
  20. { members: […] _links: [ { "rel": "self", "href": "https://api.service.com/notes?after=3381",

    "method": "GET" }, { "rel": "next", "href": "https://api.service.com/notes?after=3421", "method": "GET" }, … ] }
  21. { members: […] _links: [ { "rel": "self", "href": "https://api.service.com/notes?after=3381",

    "method": "GET" }, { "rel": "next", "href": "https://api.service.com/notes?after=3421", "method": "GET" }, … ] } Go to next page… Process my items
  22. { members: […] _links: [ { "rel": "self", "href": "https://api.service.com/notes?after=3381",

    "method": "GET" }, { "rel": "next", "href": "https://api.service.com/notes?after=3421", "method": "GET" }, … ] } Iterator IProcessor
  23. HTTP 1.1 GET /my/own/resource/123 Host: example.org If-None-Match: "4ab4bec9811" Authorization: Bearer

    <token> /my /own /resource HTTP/1.1 304 Not Modified ETag: "4ab4bec9811"
  24. HTTP 1.1 GET /my/own/resource/123 Host: example.org If-None-Match: "4ab4bec9811" Authorization: Bearer

    <token> /my /own /resource HTTP/1.1 200 OK ETag: "4ab4bec9811" …
  25. HTTP 1.1 GET /my/own/resource/123 Host: example.org If-None-Match: "4ab4bec9811" Authorization: Bearer

    <token> /my /own /resource HTTP/1.1 200 OK ETag: "4ab4bec9811" … Cache
  26. namespace My.Service.REST { public class Hypermedia { public Hypermedia(string rel,

    string method, string href) { ... } public string Rel { get; private set; } public string Method { get; private set; } public string Href { get; private set; } } public class Resource : IResource { protected Resource() { Links = new Dictionary<string, Hypermedia>(); } public IDictionary<string, Hypermedia> Links { get; set; } } }
  27. HAL

  28. HAL

  29. TAK

  30. TAK

  31. TAK

  32. NIE

  33. NIE

  34. NIE

  35. NIE

  36. NIE

  37. NIE