Konrad Kowalski (rootsher)Principal Platform & Reliability Architect100011110011001101001110001010011010001111110100

REST: zasoby, reprezentacje i semantyka HTTP

data
kategoria
Backend
także w
Software Architecture · System Design
czytanie
1 min / 226 słów

REST często redukuje się do schematu:

text
GET    /users
POST   /users
DELETE /users/42

To użyteczna konwencja, ale nie definicja REST.

REST jest stylem architektonicznym opartym między innymi na zasobach, reprezentacjach, statelessness i jednolitym interfejsie.

Zasób nie jest rekordem JSON

Załóżmy, że istnieje użytkownik:

text
/users/42

Tożsamością zasobu jest URI.

JSON jest jedną z jego reprezentacji:

json
{
  "id": 42,
  "name": "Alice"
}

Ten sam zasób mógłby teoretycznie mieć inną reprezentację:

http
Accept: application/json

lub:

http
Accept: text/html

REST rozdziela więc:

text
resource
!=
representation

HTTP wnosi semantykę

Dobrze zaprojektowane API wykorzystuje znaczenie metod HTTP.

http
GET /users/42

odczytuje reprezentację.

http
DELETE /users/42

usuwa zasób.

http
PUT /users/42

zwykle oznacza zastąpienie reprezentacji.

http
PATCH /users/42

częściową modyfikację.

To ważniejsze niż estetyka URL-i.

HTTP ma już pojęcia takie jak:

  • safe methods,
  • idempotency,
  • cacheability,
  • conditional requests.

API może z nich korzystać zamiast budować własny protokół wewnątrz POST.

Statelessness

Każdy request powinien zawierać informacje potrzebne do jego obsłużenia.

To nie oznacza:

serwer nie przechowuje żadnego stanu.

Serwer oczywiście przechowuje stan domeny.

Chodzi o brak ukrytego kontekstu konwersacji wymaganego do zrozumienia kolejnego requestu.

text
request N
request N+1

Drugi request nie powinien wymagać lokalnej, tymczasowej sesji protokołu stworzonej przez pierwszy, poza jawnie modelowanym stanem aplikacji.

Gdzie REST zaczyna być niewygodny

Wyobraźmy sobie operację:

text
approve invoice

Możemy próbować modelować ją jako zmianę zasobu:

http
PATCH /invoices/42

{
  "status": "approved"
}

Ale czy każda operacja domenowa naprawdę jest prostą zmianą pola?

Co z:

text
recalculate invoice
send invoice
archive invoice
retry payment
generate report

Da się wszystko opisać zasobami.

Pytanie brzmi, czy zawsze warto.

Jeżeli API jest silnie operacyjne, model zasobowy może zacząć ukrywać prawdziwą semantykę domeny.

Wtedy naturalną alternatywą staje się RPC:

text
zamiast: manipulate resource
robimy: invoke operation

To nie jest krok „wstecz”.

To inny model API.