Веб

HTTP POST vs PUT в REST API: когда использовать каждый метод

Различия между HTTP POST и PUT методами в REST API. Когда использовать POST для создания ресурсов, а когда PUT. Принципы проектирования REST API.

1 ответ 76 просмотров

В чем разница между методами HTTP POST и PUT, и когда каждый из них следует использовать для создания ресурса?

Фон:

  • Согласно RFC 2616, § 9.5, POST используется для создания нового ресурса как подчиненного ресурсу, идентифицированному Request‑URI
  • Согласно RFC 2616, § 9.6, PUT используется для создания или замены ресурса по указанному URI

Вопрос:
Какой метод HTTP следует использовать для создания ресурса, или оба должны поддерживаться в проектировании REST API?

HTTP POST и PUT методы REST API имеют фундаментальные различия в управлении URI ресурсов. POST создает новый ресурс с URI, определяемым сервером, в то время как PUT создает или заменяет ресурс по URI, указанному клиентом. Выбор метода зависит от того, кто контролирует идентификатор ресурса - сервер (POST) или клиент (PUT), и оба метода могут поддерживаться в REST API для разных сценариев использования.


Содержание


Основные различия между HTTP POST и PUT в REST API

HTTP POST и PUT методы представляют два различных подхода к созданию ресурсов в REST API, и их различия коренятся в фундаментальных принципах проектирования веб-сервисов. Согласно RFC 2616, § 9.5, POST предназначен для создания нового ресурса как подчиненного ресурсу, идентифицированному Request-URI, в то время как PUT, как указано в RFC 2616, § 9.6, используется для создания или замены ресурса по указанному URI.

Ключевое различие между этими методами заключается в контроле над URI ресурса. При использовании POST сервер генерирует URI для нового ресурса и возвращает его в ответе через заголовок Location. Клиент отправляет запрос на создание ресурса без указания его точного URI в запросе, делегируя эту задачу серверу. Это идеально подходит для создания ресурсов, идентификаторы которых должны быть уникальными и генерируемыми системой, например, для постов в блоге, пользователей или заказов в интернет-магазине.

С другой стороны, PUT дает клиенту полный контроль над URI ресурса. Клиент указывает точный URI, по которому должен быть создан или изменен ресурс. Этот подход подходит для ситуаций, когда клиент знает точный идентификатор ресурса и хочет гарантировать его размещение по конкретному адресу. Типичные примеры включают обновление профиля пользователя по известному ID или сохранение конфигурации приложения по предсказуемому пути.

Еще одно важное различие касается семантики идемпотентности. PUT идемпотентен - многократные выполнения одного и того же PUT запроса приведут к одному и тому же состоянию ресурса. POST не идемпотентен - повторное выполнение POST запроса приведет к созданию нескольких новых ресурсов.

В таблице ниже представлены основные различия между POST и PUT методами:

Критерий HTTP POST HTTP PUT
Контроль URI Сервер генерирует URI Клиент указывает URI
Идемпотентность Не идемпотентен Идемпотентен
Использование Создание коллекции Создание/обновление конкретного ресурса
Ответ сервера 201 Created с Location 201 Created или 200 OK
Повторные запросы Создают дубликаты Не создают дубликаты

Понимание этих различий критически важно для правильного проектирования REST API, так как выбор между POST и PUT влияет на архитектуру клиентского приложения и серверной части.


Когда использовать POST для создания ресурсов

HTTP POST метод является предпочтительным выбором для создания ресурсов в следующих сценариях:

  1. Создание ресурсов в коллекциях. Когда новый ресурс должен быть частью существующей коллекции, POST является естественным выбором. Например, создание нового поста в блоге, нового комментария к статье или нового товара в категории. В этом случае URI нового ресурса будет генерироваться сервером на основе существующей коллекции.

  2. Когда URI ресурса должен быть уникальным и непредсказуемым. Для ресурсов, идентификаторы которых должны быть случайными или сложными для угадывания, например, для сессий пользователей, временных токенов или уникальных кодов подтверждения. Сервер может гарантировать уникальность генерируемых идентификаторов.

  3. При создании ресурсов с большой нагрузкой. Когда система должна обрабатывать большое количество запросов на создание ресурсов, POST позволяет распределить нагрузку между различными серверами, так как каждый сервер может генерировать уникальные URI без необходимости координации.

  4. Для асинхронных процессов создания. Когда создание ресурса может быть длительным процессом, требующим асинхронной обработки, POST позволяет немедленно вернуть клиенту подтверждение получения запроса (202 Accepted) с возможностью отслеживания процесса создания.

  5. При работе с нереляционными базами данных. В системах, где идентификаторы генерируются автоматически (как в MongoDB или других NoSQL базах данных), POST является более естественным выбором, так как позволяет использовать встроенные механизмы генерации ID.

Пример использования POST для создания нового пользователя:

POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json

{
 "username": "john_doe",
 "email": "john@example.com",
 "password": "secure123"
}

Ответ сервера:

HTTP/1.1 201 Created
Location: /api/users/12345
Content-Type: application/json

{
 "id": "12345",
 "username": "john_doe",
 "email": "john@example.com",
 "created_at": "2023-01-01T12:00:00Z"
}

В этом примере сервер генерирует уникальный идентификатор “12345” для нового пользователя и возвращает его в ответе через заголовок Location. Клиент не должен предполагать, какой URI будет у нового ресурса - это полностью контролируется сервером.

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


Когда использовать PUT для создания ресурсов

HTTP PUT метод становится предпочтительным выбором для создания ресурсов в следующих сценариях:

  1. Когда клиент знает точный URI ресурса. Если у клиента есть информация о том, где должен быть размещен ресурс, PUT позволяет создать его по указанному URI. Это особенно полезно при обновлении существующих ресурсов, но также подходит и для создания новых, когда идентификатор ресурса известен заранее.

  2. Для создания ресурсов с предсказуемыми идентификаторами. В системах, где идентификаторы ресурсов имеют предопределенную структуру или формат, например, UUID, слаги URL или другие типы идентификаторов, которые могут быть сформированы клиентом. Например, создание профиля пользователя по его имени пользователя: /api/users/john_doe.

  3. При работе с файловой системой или файловым хранилищем. Когда ресурсы должны быть доступны по предсказуемым путям, например, при загрузке аватара пользователя по пути /api/users/12345/avatar.jpg. PUT позволяет клиенту контролировать точное расположение файла.

  4. Для создания ресурсов в распределенных системах. В системах, где разные узлы отвечают за разные части данных, PUT позволяет клиенту направлять запросы непосредственно на нужный узел на основе URI ресурса, что повышает эффективность распределенной системы.

  5. Когда требуется идемпотентность. PUT идемпотентен, что означает, что многократное выполнение одного и того же запроса приведет к одному и тому же состоянию ресурса. Это делает PUT идеальным для операций, которые могут повторяться, например, при синхронизации данных между клиентом и сервером.

Пример использования PUT для создания пользователя по известному ID:

PUT /api/users/12345 HTTP/1.1
Host: example.com
Content-Type: application/json

{
 "username": "john_doe",
 "email": "john@example.com",
 "password": "secure123"
}

Ответ сервера:

HTTP/1.1 201 Created
Content-Type: application/json

{
 "id": "12345",
 "username": "john_doe",
 "email": "john@example.com",
 "created_at": "2023-01-01T12:00:00Z"
}

В этом примере клиент указывает точный URI /api/users/12345 для создания пользователя. Если ресурс с таким URI уже существует, PUT его заменит. Если не существует - будет создан новый.

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

Еще одно преимущество PUT - возможность использования в сценариях, где клиент должен иметь полный контроль над состоянием ресурса. Например, при обновлении конфигурации приложения, где клиент знает точный путь и содержимое конфигурационного файла.


Идемпотентность и ее значение при выборе метода

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

HTTP PUT метод идемпотентен. Если вы отправляете PUT запрос для создания ресурса по URI /api/users/12345 с определенными данными, а затем отправляете точно такой же запрос еще раз, система останется в том же состоянии: ресурс с URI /api/users/12345 будет существовать и содержать те же данные, что и после первого запроса. Если ресурс уже существовал, PUT его заменит, а если не существовал - создаст. Повторные запросы не приведут к созданию дубликатов.

В отличие от этого, HTTP POST метод не идемпотентен. Каждый POST запрос создает новый ресурс, поэтому повторная отправка того же POST запроса приведет к созданию еще одного ресурса с новым URI, сгенерированным сервером. Это может привести к непреднамеренному созданию дубликатов данных.

Идемпотентность имеет практическое значение в различных сценариях:

  1. Повторные запросы из-за сбоев сети. Если клиент отправляет запрос, но не получает ответа из-за проблем с сетью, он не знает, был ли запрос обработан сервером. В случае идемпотентного метода (PUT) клиент может безопасно повторить запрос, не опасаясь создания дубликатов. Для неидемпотентного метода (POST) повторение запроса приведет к созданию дубликата.

  2. Автоматические повторные попытки. Многие HTTP-клиенты автоматически повторяют запросы при получении определенных ошибок (например, 5xx ошибок сервера). Для идемпотентных методов это безопасно, а для неидемпотентных может привести к нежелательным последствиям.

  3. Оффлайн-работа и синхронизация. В сценариях, когда пользователь работает в оффлайн-режиме и затем синхронизирует данные с сервером, идемпотентность PUT гарантирует, что повторная отправка одного и того же запроса не создаст дубликатов.

  4. Кэширование и CDN. Идемпотентные запросы могут безопасно кэшироваться и обслуживаться из кэша, так как повторное выполнение не изменит состояние системы.

Для реализации идемпотентности на уровне приложения можно использовать различные подходы:

  1. Идентификаторы операций. Каждый запрос может содержать уникальный идентификатор операции (например, UUID). Сервер может отслеживать уже обработанные операции и игнорировать повторяющиеся запросы с тем же идентификатором.

  2. Клиентские токены. Клиент может включать в запрос токен, который гарантирует уникальность операции. Сервер сохраняет состояние уже обработанных токенов.

  3. Временные окна. Сервер может игнорировать повторные запросы в течение определенного временного окна после первого выполнения.

  4. Контрольные суммы или хеши. Сервер может вычислять хеш содержимого запроса и сравнивать его с ранее обработанными запросами.

Пример реализации идемпотентности для POST запроса:

POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Idempotency-Key: 74a6f9c8-1f5b-4d8c-b5d5-7d3c6e8f2a4b

{
 "username": "john_doe",
 "email": "john@example.com",
 "password": "secure123"
}

В этом примере заголовок Idempotency-Key гарантирует, что сервер обработает этот запрос только один раз, даже если клиент повторит его. Сервер может использовать этот ключ для отслеживания уже обработанных операций.

Выбор между POST и PUT должен учитывать требования к идемпотентности в конкретном сценарии. Если создание ресурса должно быть идемпотентным, PUT является более подходящим выбором. Если же идемпотентность не требуется, или наоборот, требуется создание уникальных ресурсов при каждом запросе, POST может быть предпочтительнее.


Принципы проектирования REST API: поддержка POST, PUT или обоих методов

При проектировании REST API возникает важный вопрос: следует ли поддерживать оба метода POST и PUT для создания ресурсов, или выбрать только один из них? Ответ зависит от конкретных требований к API и сценариев его использования, но существуют общие принципы и рекомендации, которые помогут принять обоснованное решение.

Поддержка обоих методов

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

  1. Разные потребности разных клиентов. Разные клиенты могут иметь разные требования к идентификаторам ресурсов. Некоторые клиенты могут предпочитать контролировать URI ресурсов (используя PUT), в то время как другие могут полагаться на сервер для генерации URI (используя POST).

  2. Разные уровни контроля. В некоторых случаях приложение может требовать полного контроля над идентификаторами ресурсов (PUT), а в других - полагаться на генерацию сервера (POST), например, для временных или одноразовых ресурсов.

  3. Гибкость архитектуры. Поддержка обоих методов позволяет проектировать более гибкую архитектуру, где разные типы ресурсов могут создаваться разными способами в зависимости от их природы.

  4. Соответствие стандартам. REST не запрещает использование обоих методов для создания ресурсов, поэтому их совместная поддержка соответствует философии REST.

Пример API, поддерживающего оба метода:

# Создание пользователя с генерацией ID на сервере
POST /api/users

# Создание пользователя с указанием ID
PUT /api/users/12345

# Обновление пользователя по ID
PUT /api/users/12345

Поддержка только POST

В некоторых случаях может быть целесообразно поддерживать только POST для создания ресурсов:

  1. Упрощение API. Ограничение количества методов упрощает понимание и использование API, особенно для новичков.

  2. Контроль над идентификаторами. Если система требует полного контроля над генерацией идентификаторов ресурсов (например, для обеспечения безопасности или уникальности), использование только POST гарантирует, что все идентификаторы генерируются сервером.

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

  4. Упрощение безопасности. При использовании только POST проще реализовать механизмы безопасности, так как все запросы на создание ресурсов проходят через одну точку входа.

Поддержка только PUT

Поддержка только PUT для создания ресурсов встречается реже, но может быть оправдана в определенных сценариях:

  1. Полный контроль клиента. Если вся система спроектирована так, что клиенты всегда контролируют идентификаторы ресурсов, использование только PUT может быть оправдано.

  2. Идемпотентность по умолчанию. Если идемпотентность является критически важной характеристикой API, использование только PUT гарантирует, что все операции создания идемпотентны.

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

Однако использование только PUT имеет недостатки:

  1. Ограниченная гибкость. Клиенты не могут полагаться на сервер для генерации идентификаторов, что может быть проблемой для некоторых типов ресурсов.

  2. Сложность реализации. Сервер должен обрабатывать запросы PUT к несуществующим ресурсам, что требует дополнительной логики.

  3. Нестандартное использование. Многие разработчики ожидают, что POST используется для создания ресурсов, а PUT - для обновления, что может привести к путанице.

Рекомендации по выбору

Основываясь на лучших практиках проектирования REST API, можно предложить следующие рекомендации:

  1. Поддерживайте оба метода, если:
  • API предназначен для различных типов клиентов с разными требованиями
  • Система использует разные стратегии генерации идентификаторов для разных типов ресурсов
  • Важно обеспечить гибкость и соответствие принципам REST
  1. Используйте только POST, если:
  • Система требует полного контроля над идентификаторами ресурсов
  • Упрощение API является приоритетом
  • Все ресурсы должны создаваться только через серверную генерацию ID
  1. Используйте только PUT в редких случаях, когда:
  • Все клиенты всегда контролируют идентификаторы ресурсов
  • Идемпотентность является обязательным требованием для всех операций создания
  • API предназначен для специфических сценариев с предсказуемыми путями
  1. Документируйте явно, какие методы поддерживаются для создания каких ресурсов, чтобы избежать путаницы у разработчиков клиентов.

  2. Рассмотрите возможность использования PATCH для частичных обновлений ресурсов, чтобы дополнить POST и PUT и обеспечить полный набор операций CRUD.

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


Источники

  1. REST API Design: PUT vs POST — Подробное сравнение методов PUT и POST с примерами: https://restfulapi.net/rest-put-vs-post/

  2. PUT vs POST for RESTful Resource Creation — Практическое руководство по выбору между PUT и POST: https://blog.api-fiddle.com/posts/put-vs-post-for-restful-resource-creation

  3. HTTP Methods: PUT vs POST — Официальные REST практики с примерами запросов и ответов: https://restcookbook.com/HTTP Methods/put-vs-post/

  4. HTTP POST, PUT, PATCH — Техническое объяснение идемпотентности и ее важности: https://www.mscharhag.com/api-design/http-post-put-patch

  5. Difference between POST and PUT — Сообщество Stack Overflow о поддержке обоих методов: https://stackoverflow.com/questions/630453/what-is-the-difference-between-post-and-put-in-http


Заключение

HTTP POST и PUT методы REST API представляют два различных подхода к созданию ресурсов, каждый со своими преимуществами и недостатками. POST создает новые ресурсы с URI, генерируемыми сервером, что идеально подходит для коллекций и ресурсов с уникальными идентификаторами. PUT позволяет клиентам создавать или заменять ресурсы по указанным URI, обеспечивая идемпотентность и полный контроль над местоположением ресурса.

При проектировании REST API важно понимать эти различия и выбирать методы в зависимости от конкретных сценариев использования. В большинстве случаев поддержка обоих методов обеспечивает необходимую гибкость, но в некоторых ситуациях может быть предпочтительна поддержка только одного из них для упрощения API или обеспечения контроля над идентификаторами ресурсов.

Ключевым фактором при выборе метода является контроль над URI ресурса: если сервер должен генерировать уникальные идентификаторы, используйте POST; если клиент контролирует идентификатор ресурса, PUT является более подходящим выбором. Понимание семантики идемпотентности также критически важно для обеспечения надежности и предсказуемости поведения API.

Независимо от выбранного подхода, четкая документация и примеры использования помогут разработчикам клиентов правильно использовать API и избегать распространенных ошибок. Правильное проектирование REST API с учетом различий между POST и PUT методами является основой для создания надежных, масштабируемых и удобных в использовании веб-сервисов.

Авторы
Проверено модерацией
Модерация