Интересности      Книги      Утилиты    

18 апреля 2017 г.

REST и его Statelessness


Как я уже писал раньше, REST обладает таким свойством как Statelessness или независимости от состояния. Проще говоря, REST не хранит на сервере какое-либо клиентское состояние или сессию. Каждый вызов к REST сервису по одному и тому же адресу URI, и с тем же HTTP Verb по сути обрабатывается одинаково.
С одной стороны – это преимущество. Потому что не нужно расходовать ресурсы сервера на то, чтобы хранить состояние. С другой стороны, такие типичные сценарии, как например авторизация пользователя, просто невозможно выполнить не сохраняя где-то состояние. Пожалуй, это пока единственный самый распространенный сценарий, который мне сейчас приходит в голову, и в котором необходимо хранить состояние.
Как следствие отсутствия состояния в REST – возможность более высокой нагрузки таких сервисов и хорошая масштабируемость. Так как запросы к сервису независимые, то и масштабировать становится проще.
Изображение 1  и 2 иллюстрирует сервисы, которые хранят сессию и нет соответственно. Например тут запрашивается следующая страница результатов:
Изображение 1
Stateful Design
Такой же сервис только состояние не хранится на сервисе.
Изображение 2
Stateless Design
В сервисах, которые оперируют сессией, данные связанные с ней хранит как сервер (по большей части), так и клиент, для того чтобы можно было правильно идентифицировать клиента и синхронизироваться с клиентом.
В REST эту обязанность на себя перенимает клиент. Использовать можно любой способ передачи параметров с клиента на сервер. Но чаще всего, используются HTTP Headers.
Рассмотрим пример того как REST обходится без сохранения сессии и состояния на сервере для сценария авторизации. При авторизации сервер выдает клиенту токен. Токен – это может быть какой-то идентификатор пользователя, плюс отметка времени когда он выдан, плюс подпись этого дела. Клиент помещает в каждый свой запрос такой токен в стандартный (Authorization) или кастомный HTTP Header. Каждый раз, когда клиент приходит на сервер – данные переподписываются и подписи сравниваются, сравниваются и даты, не истек ли срок на которые выдан токе. Дальше, извлекается идентификтор пользователя и теперь можно на сервере делать свои черные дела (выполнять бизнес логику например).
Вот пример такого запроса
GET https://myaddressbook.com/contacts/7
HEADERS:
Accept: application/json    
Authorization: WRAP access_token="http%3a%2f%2fschemas.xmlsoap.org%2fws%2f2005%2f05%2fidentity%2fclaims%2fsid=7&http%3a%2f%2fschemas.microsoft.com%2fws%2f2008%2f06%2fidentity%2fclaims%2frole=http%3a%2f%2fschemas.myaddressvook.com%2fmyLibreeze%2fpermission%2fmodify&Issuer=OAuthProvider&Audience=http%3a%2f%2fmyaddressvook%2frp&ExpiresOn=1337764852&HMACSHA256=fUcljUY7i9rpiNvFp%2bPszYBf2AkCDjHHC7wPcGV21NE%3d" 
в данном случае в заголовке передается страшный токен:
WRAP access_token="http%3a%2f%2fschemas.xmlsoap.org%2fws%2f2005%2f05%2fidentity%2fclaims%2fsid=7&http%3a%2f%2fschemas.microsoft.com%2fws%2f2008%2f06%2fidentity%2fclaims%2frole=http%3a%2f%2fschemas.myaddressvook.com%2fmyaddressvook%2fpermission%2fmodify&Issuer=OAuthProvider&Audience=http%3a%2f%2fmylibreeze%2frp&ExpiresOn=1337764852&HMACSHA256=fUcljUY7i9rpiNvFp%2bPszYBf2AkCDjHHC7wPcGV21NE%3d"

В нем даже можно различить схему по которой он создан, где подпись, и маркер времени и даже идентификатор пользователя – 7, вся соль в плане безопасности в данном случае в подписи.

На токен может быть совсем другой – более короткий, и данные могут быть скрыты. Для того, как REST работает с состоянием – это не есть существенно. И все это счастье гоняется с каждым запросом на сервер. Если длина токена в данном случае смущает – ее можно сократить. Да, конечно же, есть небольшой overhead, но состояние все таки нужно как-то передать.

Например Amazon AWS для таких сценариев использует другой токен:
POST /2013-08-05/distribution HTTP/1.1
Host: cloudfront.amazonaws.com
Date: Thu, 05 Aug 2013 17:08:48 GMT
Authorization: AWS AKIAIOSFODNN7EXAMPLE:111111112222222jPXo7+e/YSu0g=
[Other required headers]
Еще один момент, в котором могут понадобится сессии – это обеспечение непротиворечивости данных и concurrency.
Представим себе ситуацию. Два пользователя выгребли у сервера данные, один из пользователей успел изменить данные, отправить их на сервер. Теперь у пользователя который ничего не меня старая версия данных и он может перезаписать новую версию.

На самом деле в большинстве случаев такие проблемы решаются на уровне базы.  Но если так не получается то можно использовать для этого Etag вместе с If-None-Match header, например для того, чтобы контролировать concurrency.
Вот так в грубом виде на примере авторизации и работает REST с состояниями.
Итак, основная идея REST  Statelessness в том, что состояние хранится не на сервере, а на клиенте. Клиент же получает от сервера все необходимые данные, чтобы это состояние на сервер передавать.

Комментариев нет:

Отправить комментарий