Подтвердить что ты не робот

RESTful восстановить

Это довольно распространенное требование для поддержки отказов или отложенных/пакетных удалений для служб данных. Мне интересно, как реализовать это с помощью RESTful. Я разрывается между несколькими разными вариантами (ни одна из них не кажется мне ужасно привлекательной). Я полагаю, что общий для этих разных вариантов - это потребность в API, который возвращает весь ресурс, помеченный как удаленный для определенного типа ресурса.

Вот некоторые варианты, о которых я думал, и некоторые из их плюсов и минусов:

Параметры для отметки ресурса как удаленные:

  • Используйте HTTP DELETE, чтобы пометить ресурс как удаленный.
  • Используйте HTTP PUT/POST для обновления удаленного флага. Это не кажется правильным, поскольку он отображает то, что по сути является удалением от метода DELETE HTTP и других методов HTTP.

Параметры, когда GET-ресурс помечен для удаления:

  • Возвращает статус HTTP 404 для ресурса, помеченного как удаленный. Чистое и прозрачное, но как мы говорим о разнице между действительно удаленным ресурсом или только что помеченным как удаленное.
  • Возврат HTTP-статуса 410. Предоставляет способ сказать разницу, но 410 технически говорит, что "ожидается, что он будет считаться постоянным. Клиенты с возможностями редактирования ссылок ДОЛЖНЫ удалять ссылки на Request-URI после утверждения пользователем". Там может быть достаточно места для маневра в словах "ожидаемый" и "СЛЕДУЕТ" здесь. Не уверен, насколько хорошо 410 поддерживается/понимается там в клиентах.
  • Возвращает статус HTTP 200 и включает в себя флаг, указывающий, что ресурс удаляется. Это кажется странным, поскольку идея удалить его в первую очередь была потому, что вы на самом деле хотели, чтобы он не появлялся. Это подталкивает ответственность за фильтрацию удаленных ресурсов до клиентов.

Параметры ответов, которые включают этот удаленный ресурс:

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

Параметры при обновлении ресурса, помеченного для удаления:

  • Использовать HTTP-статус 404. Ресурс пропал? Но как вы можете определить разницу между ресурсом, помеченным как удаленным, и фактически удаленным. Тело HTTP в ответе 404 может быть здесь неоднозначным, но тогда клиенты остаются в синтаксическом анализе или интерпретации вашего тела для устранения неоднозначности. Может быть, заголовок ответа может помочь здесь? Который из? Пользовательский заголовок?
  • Используйте HTTP-статус 409 с сообщением о том, как ресурс должен быть сначала восстановлен.

Параметры для восстановления ресурса, помеченного для удаления:

  • Используйте HTTP PUT/POST для обновления работы ресурса и снова отмечайте его. Это работает только до тех пор, пока вы не возвращаете HTTP 404 для операции GET для ресурса, так как он не делает с PUT/POST ресурсу, который "не найден" (404).
  • Используйте HTTP PUT/POST для создания ресурса. Проблема в том, какие данные имеют приоритет? Данные, отправленные в операции создания? Или данные, которые восстанавливаются? отфильтруйте его из любых других запросов, которые вернули бы его. Затем обработайте HTTP PUT/POST, который создает ресурс как undelete, если идентификатор ресурса указывает на ресурс, помеченный как удаленный.
  • Отдельный путь REST, предназначенный для восстановления ресурсов, помеченных для удаления.

Это далеко не полный список. Я просто хотел перечислить некоторые варианты, которые подпрыгивали у меня в голове.

Я знаю, что ответ на вопрос, как это сделать, как обычно, "это зависит". Что мне интересно, какова квалификация/требования, которые вы будете использовать для принятия решения? Как вы видели, как это реализовано или реализовано самостоятельно?

4b9b3361

Ответ 1

Переход по книге: RFC 2616-9.7:

  The DELETE method requests that the origin server delete the resource 
  identified by the Request-URI. This method MAY be overridden by human 
  intervention (or other means) on the origin server. The client cannot
  be guaranteed that the operation has been carried out, even if the 
  status code returned from the origin server indicates that the action
  has  been completed successfully. However, the server SHOULD NOT 
  indicate success unless, at the time the response is given, if it intends
  to delete the resource or move it to an inaccessible location.

Когда вы удаляете ресурс, сервер должен пометить ресурс для удаления на нем. На самом деле не нужно удалять ресурс, он просто не может дать никакой гарантии, что операция была выполнена. Тем не менее, сервер не должен сказать, что он был удален, когда он этого не сделал.

  A successful response SHOULD be 200 (OK) if the response includes an entity
  describing the status, 202 (Accepted) if the action has not yet been enacted,
  or 204 (No Content) if the action has been enacted but the response does not
  include an entity.

Если операция задерживается, отправьте 202 и тело объекта, описывающее результат действия. (Подумайте о возможности "опроса", представляющего сервер, отложенное удаление ресурса, теоретически можно оставить его навсегда в этом состоянии.) Все, что ему нужно сделать, - это не допустить, чтобы клиент снова извлек его в оригинальной форме. Используйте код 410 для кода ответа, а когда завершается "задача" или сервер удаляет ресурс, верните 404.

Однако, если семантика DELETE не имеет смысла для рассматриваемого ресурса, возможно, это не удаление, которое вы ищете, а переход состояния добавления, который изменяет состояние ресурса, но сохраняет его доступным? В этом случае используйте PUT/PATCH для обновления ресурса и выполнения.

Ответ 2

Я думаю, что самый RESTful способ решить это - использовать HTTP PUT, чтобы пометить ресурс для удаления (и восстановить его), а затем использовать HTTP DELETE для окончательного удаления ресурса. Чтобы получить список ресурсов, помеченных для удаления, я бы использовал параметр в запросе HTTP GET, например. ?state=markedForDeletion. Если вы запрашиваете ресурс, помеченный для удаления без параметра, я думаю, вы должны вернуть статус "404 Not Found".

Ответ 3

Краткая версия

Вы не можете RESTfully восстановить ресурс, используя любой метод на его исходном URI - это нелогично, потому что любая операция, предпринятая на удаленном ресурсе, должна вернуть либо 404, либо 410. Хотя это явно не указано в спецификации, это сильно подразумевается в определении метода DELETE 1 (выделено мной):

По сути, этот метод похож на команду rm в UNIX: он выражает операцию удаления при отображении URI исходного сервера, а не в ожидании удаления ранее связанной информации.

Другими словами, когда у вас есть DELETEd ресурса, сервер больше не сопоставляет этот URI этим данным. Таким образом, вы не можете использовать PUT или POST для обновления, например "отметьте это как undeleted" и т.д. (Помните, что ресурс определяется как сопоставление между URI и некоторыми базовыми данными).

Некоторые решения

Поскольку он явно заявил, что базовые данные не обязательно удаляются, это не исключает, что сервер делает новое сопоставление URI как часть реализации DELETE, тем самым эффективно создавая резервную копию, которую можно восстановить позже.

У вас может быть коллекция "/deleted/", содержащая все удаленные элементы, но как вы на самом деле выполняете восстановление? Возможно, самый простой способ RESTful заключается в том, чтобы клиент извлекал элемент с помощью GET, а затем POST его на нужный URL.

Что делать, если вам нужно восстановить удаленный элемент в исходное местоположение? Если вы используете тип носителя, который его поддерживает, вы можете включить исходный URI в ответ на GET из /deleted/collection. Затем клиент может использовать его для POST. Такой ответ может выглядеть так в JSON:

{
    "original-url":"/some/place/this/was/deleted/from",
    "body":<base64 encoded body>
}

Клиент мог бы затем отправить этот орган в этот URI для выполнения восстановления.

В качестве альтернативы, если определение вашего ресурса допускает концепцию перемещения (путем обновления свойства "location" или что-то в этом роде), вы можете сделать частичное обновление и избежать кругового путешествия всего объекта. Или сделайте то, что делают большинство людей, и выполните RPC-подобную операцию, чтобы сообщить серверу переместить ресурс! UnRESTful, да, но он, вероятно, будет работать нормально в большинстве ситуаций.

Как вы решаете эти вещи

Что касается вопроса о том, как вы решаете эти вещи: вам нужно подумать о том, что означает удаление в контексте вашего приложения, и почему вы этого хотите. Во многих приложениях ничего не удаляется, а "удалить" на самом деле просто означает "исключить этот элемент из всех последующих запросов/списков и т.д., Если я явно не удалю его". Таким образом, это действительно просто фрагмент метаданных или операция перемещения. В этом случае зачем беспокоиться об удалении HTTP DELETE? Одна из причин может быть, если вы хотите, чтобы 2-уровневое удаление - мягкая или временная версия, которая была отменена, и жесткая/постоянная версия, ну... нет.

При отсутствии какого-либо конкретного контекста приложения я бы склонен реализовать их следующим образом:

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

Я не хочу, чтобы кто-нибудь мог получить этот ресурс больше, потому что это смущало/обвиняло/стоило мне денег /etc: HTTP DELETE

Следующий вопрос, который следует рассмотреть, заключается в следующем: должен ли постоянный удалить только unmap URI навсегда, чтобы никто больше не мог ссылаться на него, или же необходимо также очистить базовые данные? Очевидно, что если вы сохраните данные, администратор может восстановить даже "постоянно" удаленный ресурс (но не через любой интерфейс RESTful). Недостатком этого является то, что если владелец данных действительно хочет его очистить, администратор должен сделать это за пределами интерфейса REST.

Ответ 4

Элементы "Удаленные" (разбитые) также могут рассматриваться как ресурс, не так ли? Затем мы можем получить доступ к этому ресурсу одним из этих способов (например, для удаленного пользователя):

PATCH deleted_users/{id}
PATCH trash/users/{id}
PATCH deleted/users/{id}

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

PATCH deleted/{id}?type=users

и в полезной нагрузке происходит примерно так:

{ deleted_at: null }

Ответ 5

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

Другие правы, что DELETE - это путь. Вы можете включить флаг, чтобы определить, является ли это немедленным постоянным DELETE или перемещением в корзину (и, вероятно, только администраторы могут сделать немедленное постоянное DELETE.)

DELETE /api/1/book/33
DELETE /api/1/book/33?permanent

Затем сервер может пометить книгу как удаленную. Предполагая, что у вас есть база данных SQL, это может быть что-то вроде:

UPDATE books SET status = 'deleted' WHERE book_id = 33;

Как уже упоминалось другими, после DELETE GET коллекции не возвращает этот элемент. С точки зрения SQL это означает, что вы должны убедиться, что не вернули элемент со статусом deleted.

SELECT * FROM books WHERE status <> 'deleted';

Кроме того, когда вы делаете GET/api/1/book/33, вы должны вернуть 404 или 410. Одна из проблем с 410 заключается в том, что это означает "Унесенные навсегда" (по крайней мере, мое понимание этого кода ошибки), поэтому я бы вернулся 404, пока элемент существует, но помечен как 'deleted' и 410, как только он был окончательно удален.

Теперь, чтобы восстановить, правильный путь - PATCH. В отличие от PUT который используется для обновления элемента, ожидается, что PATCH будет операцией над элементом. Из того, что я вижу, ожидается, что операция будет в полезной нагрузке. Чтобы это работало, ресурс должен быть доступен каким-то образом. Как предложил кто-то другой, вы можете trashcan область trashcan где книга появится после удаления. Примерно так будет работать, чтобы перечислить книги, которые были помещены в корзину:

GET /api/1/trashcan/books

[{"path":"/api/1/trashcan/books/33"}]

Итак, итоговый список теперь будет включать в себя книгу № 33, которую вы можете затем PATCH с помощью такой операции:

PATCH /api/1/trashcan/books/33

{
    "operation": "undelete"
}

Если вы хотите сделать операцию более универсальной, вы можете использовать что-то вроде:

PATCH /api/1/trashcan/books/33

{
    "operation": "move",
    "new-path": "/api/1/books/33"
}

Затем "перемещение" может быть использовано для других изменений URL, где это возможно в вашем интерфейсе. (Я работаю над CMS, где путь к странице находится в одной таблице, называемой tree, а каждая страница находится в другой таблице, называемой page и имеет идентификатор. Я могу изменить путь страницы, перемещая ее между путями в моем tree таблица! Здесь PATCH очень полезен.)

К сожалению, RFC не дают четкого определения PATCH, только то, что он должен использоваться с операцией, как показано выше, в отличие от PUT который принимает полезную нагрузку, представляющую новую версию, возможно частичную, целевого элемента:

PUT /api/1/books/33

{
    "title": "New Title Here"
}

Принимая во внимание, что соответствующий PATCH (если бы вы поддерживали оба) был бы:

PATCH /api/1/books/33

{
    "operation": "replace",
    "field": "title",
    "value": "New Title Here"
}

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

Вы можете думать об этом как: использование patch - это изменение виртуального поля или выполнение сложной операции, такой как перемещение, которое в противном случае потребовало бы GET, POST, DELETE (и при условии, что DELETE является немедленным, и вы можете получить ошибки и в конечном итоге с частичным перемещением...) В некотором смысле, PATCH похож на любое количество методов. Метод UNDELETE или MOVE будет работать аналогичным образом, но в RFC четко сказано, что существует набор стандартизированных методов, и вам, безусловно, следует их придерживаться, а PATCH дает вам достаточно места, чтобы не добавлять свои собственные методы. Хотя в спецификациях я ничего не видел, говоря, что вы не должны добавлять свои собственные методы. Если вы это сделаете, убедитесь, что четко документировали их.

Ответ 6

Мы заставили модель создать

POST/имя модели /: id/восстановить