RESTfully оформить / войти или / зарегистрировать ресурсы?

122

Я разрабатывал веб-приложение, а затем остановился, чтобы подумать о том, как мой api должен быть спроектирован как веб-сервис RESTful. На данный момент большинство моих URI являются общими и могут применяться к различным веб-приложениям:

GET  /logout   // destroys session and redirects to /
GET  /login    // gets the webpage that has the login form
POST /login    // authenticates credentials against database and either redirects home with a new session or redirects back to /login
GET  /register // gets the webpage that has the registration form
POST /register // records the entered information into database as a new /user/xxx
GET  /user/xxx // gets and renders current user data in a profile view
POST /user/xxx // updates new information about user

У меня такое чувство, что я делаю здесь много не так после того, как ковырялся в SO и Google.

Начнем с того, что /logout, возможно, так как я на самом деле GETничего не имею - это может быть более подходящим для POSTзапроса /logout, уничтожить сеанс, а затем GETперенаправить. И должен ли /logoutсрок остаться?

Насчет /loginи /register. Я мог бы перейти /registerна, /registrationно это не повлияет на фундаментальную работу моего сервиса - если у него есть более серьезные проблемы.

Теперь я замечаю, что никогда не раскрываю /userресурс. Возможно, это можно как-то использовать. Например, возьмем пользователя myUser:

foo.com/user/myUser

или

foo.com/user

Конечному пользователю не требуется эта дополнительная многословность в URI. Однако какой из них визуально более привлекателен?

Я заметил несколько других вопросов здесь, в SO, об этом бизнесе REST, но я был бы очень признателен за некоторые рекомендации по тому, что я здесь изложил, если это возможно.

Спасибо!

ОБНОВИТЬ:

Мне также хотелось бы высказать свое мнение по поводу:

/user/1

против

/user/myUserName
QCOM
источник

Ответы:

63

Одна вещь особенно выделяется как не REST-ful: использование запроса GET для выхода из системы.

(из http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods )

Некоторые методы (например, HEAD, GET, OPTIONS и TRACE) определены как безопасные, что означает, что они предназначены только для поиска информации и не должны изменять состояние сервера. Другими словами, они не должны иметь побочных эффектов, помимо относительно безобидных эффектов, таких как ведение журнала, кеширование, показ баннерной рекламы или увеличение счетчика в сети. [...]

[... H] обработка [запросов GET] сервером технически не ограничена никаким образом. Следовательно, неосторожное или преднамеренное программирование может вызвать нетривиальные изменения на сервере. Это не рекомендуется, поскольку это может вызвать проблемы для веб-кеширования, поисковых систем и других автоматизированных агентов [...]

Что касается выхода из системы и перенаправления, у вас может быть сообщение на ваш URI выхода из системы с перенаправлением ответа 303 на страницу после выхода.

http://en.wikipedia.org/wiki/Post/Redirect/Get

http://en.wikipedia.org/wiki/HTTP_303

Отредактируйте, чтобы решить проблемы с дизайном URL:

«Как мне разработать свои ресурсы?» для меня важный вопрос; "как мне создать свои URL-адреса?" рассматривается в двух областях:

URL-адреса, которые будут видеть пользователи, по возможности не должны быть слишком уродливыми и значимыми; Если вы хотите, чтобы файлы cookie отправлялись в запросах к одному ресурсу, но не к другим, вам нужно структурировать свои пути и пути файлов cookie.

Если JRandomUserвы хотите посмотреть свой профиль и хотите, чтобы URL-адрес был красивее, чем foo.com/user/JRandomUserили foo.com/user/(JRandom's numeric user id here), вы можете создать отдельный URL-адрес только для того, чтобы пользователь мог просматривать свою информацию:

GET foo.com/profile /*examines cookies to figure out who 
                     * is logged in (SomeUser) and then 
                     * displays the same response as a
                     * GET to foo.com/users/SomeUser.
                     */

Я бы с большей готовностью заявил о незнании, чем о мудрости в этом вопросе, но вот несколько соображений по дизайну ресурсов:

  1. Потребитель: какие ресурсы предназначены для просмотра непосредственно в браузере, для загрузки через XHR или для доступа к другим типам клиентов?
  2. Доступ / идентификация: зависит ли ответ от файлов cookie или источников перехода?
ellisbben
источник
1
Отличный ответ, спасибо! Если бы я собирался реализовать ваше отдельное предложение URL ( GET foo.com/profile/), было бы это частью, как предлагал momo, уровня представления? Другими словами, что именно должен GETвернуть этот запрос? Веб-страница или какой-нибудь JSON?
Qcom
2
Ах, теперь я понимаю. Ответ Момо действительно прояснил ситуацию. Таким образом, RESTful API построен , чтобы позволить несколько платформ GET, POST, PUTи DELETEресурсов. Веб-сайт - это просто еще одна платформа, имеющая доступ к API. Другими словами, дизайн URL веб-сайта полностью отличается от дизайна RESTful API. Скажите, пожалуйста, если я все еще ошибаюсь, ха-ха.
Qcom
Да, сделайте свой REST API одним набором URL-адресов, а ваш веб-сайт другим набором. Затем URL-адрес вашего веб-сайта должен возвращать вам соответствующий HTML + Javascript, чтобы страница выполняла соответствующие запросы XmlHttpRequests для ваших URL-адресов API, чтобы действовать как клиент.
ellisbben 08
129

RESTful можно использовать в качестве руководства для создания URL-адресов, а также создавать сеансы и ресурсы пользователей :

  • GET /session/new получает веб-страницу с формой входа
  • POST /session аутентифицирует учетные данные по базе данных
  • DELETE /session уничтожает сеанс и перенаправляет на /
  • GET /users/new получает веб-страницу с регистрационной формой
  • POST /users записывает введенную информацию в базу данных как новый / user / xxx
  • GET /users/xxx // получает и отображает данные текущего пользователя в представлении профиля
  • POST /users/xxx // обновляет новую информацию о пользователе

Они могут быть во множественном или единственном числе (я не уверен, какой из них правильный). Я обычно использую /usersдля страницы индекса пользователя (как и ожидалось) и /sessionsдля того, чтобы увидеть, кто вошел в систему (как и ожидалось).

Использование имени в URL-адресе вместо числа ( /users/43vs. /users/joe) обычно обусловлено желанием быть более дружелюбным к пользователям или поисковым системам, а не какими-либо техническими требованиями. Либо это нормально, но я рекомендую вам действовать последовательно.

Я думаю, что если вы перейдете с регистрацией / входом / выходом из системы или sign(in|up|out), это не будет работать с успокаивающей терминологией.

NDP
источник
6
Потрясающие! Мне нравится, как вы назвали эти ресурсы; это довольно чисто. Хотя из того, что я слышал, разве не добавляется /newк GET /session/не RESTful? Я слышал, что глаголы обычно оставляют для глаголов HTTP ( GET, POSTи т. Д.).
Qcom
2
@Zach new - это не глагол. В данном случае это подресурс сеанса.
Кугель
Как определить, какой сеанс удалить в DELETE / session? Curl не отправляет ни файлы cookie, ни параметры в запросе DELETE. Я предполагаю - просто использовать DELETE / session / sessionId? Другой вопрос, как вернуть идентификатор сеанса в POST / session и в каком формате.
Tvaroh 03
9
На самом деле, спокойствие - это способ сделать себя несчастным и тратить время на вещи, которые вообще не имеют значения.
Цзян Чен
6
Лично мне не нравится идея иметь маршруты, возвращающие форму (/ new). Это нарушает разделение между представлением и бизнес-логикой. Та сказал, что без новых маршрутов предложенный выглядит идеально.
Scadge
60

Сеансы не являются RESTful

  • Да, я знаю. Обычно это делается с помощью OAuth, но на самом деле сеансы не являются RESTful. У вас не должно быть ресурса / login / logout в первую очередь потому, что у вас не должно быть сеансов.

  • Если вы собираетесь это сделать, сделайте это RESTful. Ресурсы - это существительные, а / login и / logout - не существительные. Я бы пошел с / session. Это делает создание и удаление более естественным действием.

  • POST vs. GET для сессий - это просто. Если вы отправляете имя пользователя / пароль в виде переменных, я бы использовал POST, потому что я не хочу, чтобы пароль отправлялся как часть URI. Он появится в журналах и, возможно, будет виден через провод. Вы также рискуете получить сбой программного обеспечения из-за ограничений GET args.

  • Я обычно использую базовую аутентификацию или без аутентификации со службами REST.

Создание пользователей

  • Это один ресурс, поэтому вам не нужно / регистрироваться.

    • POST / user - создает пользователя, если запрашивающая сторона не может указать идентификатор
    • PUT / user / xxx - создайте или обновите пользователя, если заранее знаете идентификатор.
    • GET / user - перечисляет x идентификаторов пользователей
    • GET / user / xxx - ПОЛУЧАЕТ сведения о пользователе с идентификатором xxx
    • DELETE / user / xxx - Удалить пользователя с идентификатором xxx
  • Какой тип ID использовать - сложный вопрос. Вы должны подумать об обеспечении уникальности, о повторном использовании старых идентификаторов, которые были УДАЛЕНЫ. Например, вы не хотите использовать эти идентификаторы в качестве внешних ключей на бэкэнде, если идентификаторы собираются повторно использовать (если это вообще возможно). Однако вы можете выполнить поиск преобразования внешнего / внутреннего идентификатора, чтобы снизить требования к бэкэнд.

dietbuddha
источник
6
Это лучший ответ. / login и / logout не являются ресурсами и нарушают идею REST.
wle8300
5
Аутентификация! = Сессия
dietbuddha
1
Да, в тезисе Филдинга в разделе 5.1.3 говорится, что «состояние [...] состояния полностью [...] остается за клиентом». Кроме того, я бы сказал, что в идеале аутентификация также должна быть без сохранения состояния на стороне сервера, т. Е. Вместо хранения активных «билетов аутентификации» в базе данных, сервер должен иметь возможность проверять учетные данные аутентификации только на основе самих учетных данных, например, используя автономный криптографический токен в сочетании с закрытым ключом. Итак, вместо ресурса / session можно было бы ввести ресурс / authentication, но это тоже не решает проблему ...
raner 03
Фактически, / login и / logout - существительные. Я предполагаю, что вы думаете о / log_in и / log_out.
TiggerToo 03
21

Я собираюсь просто рассказать о своем опыте интеграции различных веб-служб REST для моих клиентов, независимо от того, используется ли он для мобильных приложений или для связи между серверами, а также для создания REST API для других. Вот несколько наблюдений, которые я собрал из REST API других людей, а также из тех, которые мы создали сами:

  • Когда мы говорим API, это обычно относится к набору программных интерфейсов, а не к уровню представления. REST также ориентирован на данные, а не на презентацию. При этом большинство REST возвращают данные в форме JSON или XML и редко возвращают определенный уровень представления. Эта особенность (возвращение данных, а не прямой веб-страницы) дала возможность REST выполнять многоканальную доставку. Это означает, что один и тот же веб-сервис может отображаться в HTML, iOS, Android или даже использоваться в качестве комбинации сервера с сервером.
  • Очень редко комбинировать HTML и REST в качестве URL-адреса. По умолчанию REST - это мысли как службы, не имеющие уровня представления. Это задача тех, кто использует веб-сервисы, чтобы отображать данные из сервисов, которые они вызывают, в соответствии с тем, что они хотят. В этом случае ваш URL-адрес ниже не соответствует большей части дизайна на основе REST, с которым я сталкивался до сих пор (ни стандартам, таким как те, которые исходят из Facebook или Twitter).
GET / register // получает веб-страницу с регистрационной формой
  • Продолжая с предыдущего пункта, также необычно (и я не встречал), чтобы служба на основе REST выполняла перенаправление, например, предложенные ниже:
GET / logout // уничтожает сеанс и перенаправляет на /
POST / login // аутентифицирует учетные данные в базе данных и либо перенаправляет домой с новым сеансом, либо перенаправляет обратно в / login
 

Поскольку REST спроектированы как службы, такие функции, как вход в систему и выход из системы, обычно возвращают результат успеха / неудачи (обычно в формате данных JSON или XML), который затем интерпретирует потребитель. Такая интерпретация может включать перенаправление на соответствующую веб-страницу, как вы упомянули.

  • В REST URL-адрес обозначает предпринимаемые действия. По этой причине мы должны устранить как можно больше двусмысленности. Хотя в вашем случае вполне законно иметь и GET, и POST, которые имеют один и тот же путь (например, / register), которые выполняют разные действия, такой дизайн вносит двусмысленность в предоставляемые услуги и может сбить с толку потребителя ваших услуг. Например, URL-адреса, подобные приведенному ниже, не идеальны для служб на основе REST.
GET / register // получает веб-страницу с регистрационной формой
POST / register // записывает введенную информацию в базу данных как новый / user / xxx

Это некоторые моменты из того, с чем я имел дело. Я надеюсь, что это может дать вам некоторое представление.

Что касается реализации вашего REST, это типичная реализация, с которой я столкнулся:

  • ПОЛУЧИТЬ / выйти  
    

    Выполнить выход из системы в серверной части и вернуть JSON для обозначения успеха / неудачи операции

  • POST / логин
    

    Отправьте учетные данные на серверную часть. Вернуть успех / неудачу. В случае успеха обычно он также возвращает токен сеанса, а также информацию о профиле.

  • POST / зарегистрироваться
    

    Подайте регистрацию на серверную часть. Вернуть успех / неудачу. В случае успеха обычно рассматривается как успешный вход в систему, или вы можете выбрать регистрацию как отдельную услугу.

  • GET / пользователь / xxx
    

    Получить профиль пользователя и вернуть формат данных JSON для профиля пользователя

  • POST / user / xxx 
    // переименован в 
    POST / updateUser / xxx
    

    Опубликуйте обновленную информацию профиля в формате JSON и обновите информацию в серверной части. Вернуть успех / неудачу вызывающему

момо
источник
3
Да, если вы интегрируете свой REST API с приложением на основе HTML (через Javascript и AJAX), вы получите огромные преимущества, поскольку JSON изначально анализируется Javascript. В Android / Java JSON проще и проще анализировать по сравнению с XML.
momo
15
GET / выход из системы опасен. GET должен быть идемпотентным. Также браузеры любят предварительно загружать <a> hrefs, что приведет к выходу из системы!
Кугель
4

Я считаю, что это подход к аутентификации RESTful. Для входа в систему вы используете HttpPut. Этот HTTP-метод можно использовать для создания, если предоставлен ключ и повторные вызовы идемпотентны. Для выхода из системы вы указываете тот же путь в HttpDeleteметоде. Глаголы не используются. Правильное плюрализация коллекции. Методы HTTP поддерживают эту цель.

[HttpPut]
[Route("sessions/current")]
public IActionResult LogIn(LogInModel model) { ... }

[HttpDelete]
[Route("sessions/current")]
public IActionResult LogOff() { ... }

При желании вы можете заменить текущий на активный.

Чад Куэн
источник
1

Я бы порекомендовал использовать URL-адрес учетной записи пользователя, аналогичный Twitter, где URL-адрес учетной записи пользователя будет примерно таким, foo.com/myUserNameкак если бы вы могли перейти в мою учетную запись Twitter с URL-адресом https://twitter.com/joelbyler

Я не согласен с тем, что для выхода из системы требуется POST. В рамках вашего API, если вы собираетесь поддерживать сеанс, то идентификатор сеанса в форме UUID может быть чем-то, что можно использовать для отслеживания пользователя и подтверждения того, что выполняемое действие авторизовано. Тогда даже GET может передать идентификатор сеанса ресурсу.

Короче говоря, я бы порекомендовал вам оставаться простым, URL-адреса должны быть короткими и запоминающимися.

joelbyler
источник
Вопрос в ресурсах API. Ваш ответ касается уровня представления.
Хенно