Вопрос: PUT против POST в REST


Согласно спецификации HTTP / 1.1 Spec:

POSTметод используется для запроса, чтобы исходный сервер принял объект, заключенный в запросе, в качестве нового подчиненного ресурса, идентифицированного Request-URIв Request-Line

Другими словами, POSTиспользуется для Создайте ,

PUTметод запрашивает, чтобы закрытый объект хранился под поставляемым Request-URI, Если Request-URIотносится к уже существующему ресурсу, закрытый объект СЛЕДУЕТ считаться измененной версией той, которая находится на сервере происхождения. Если Request-URIне указывает на существующий ресурс и что URI может быть определен как новый ресурс запрашивающим пользовательским агентом, исходный сервер может создать ресурс с этим URI ».

То есть, PUTиспользуется для создавать или обновлять ,

Итак, какой из них следует использовать для создания ресурса? Или нужно поддерживать оба?


4393


источник


Ответы:


В общем и целом:

И PUT, и POST можно использовать для создания.

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

Можно использовать оба варианта, поэтому я должен использовать его в дизайне RESTful:

Вам не нужно поддерживать PUT и POST.

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

Некоторые соображения:

  • Вы называете свои объекты URL, которые вы создаете явно, или разрешите серверу? Если вы их назовете, используйте PUT. Если вы разрешите серверу использовать POST.
  • PUT является идемпотентным, поэтому, если вы дважды нажмете объект, он не имеет никакого эффекта. Это приятное свойство, поэтому я мог бы использовать PUT, когда это возможно.
  • Вы можете обновить или создать ресурс с помощью PUT с тем же URL-адресом объекта
  • С помощью POST вы можете одновременно внести 2 запроса, внося изменения в URL-адрес, и они могут обновлять разные части объекта.

Пример:

Я написал следующее как часть другого ответа на SO относительно этого :

ПОСЛЕ:

Используется для изменения и обновления ресурса

POST /questions/<existing_question> HTTP/1.1
Host: www.example.com/

Обратите внимание, что следующая ошибка:

POST /questions/<new_question> HTTP/1.1
Host: www.example.com/

Если URL еще не создан, вы   не должен использовать POST для его создания   указывая имя. Это должно   привести к ошибке «ресурс не найден»   потому как <new_question>не существует   все же. Вы должны <new_question>сначала на сервере.

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

POST /questions HTTP/1.1
Host: www.example.com/

Обратите внимание, что в этом случае ресурс   имя не указано, новые объекты   URL-адрес будет возвращен вам.

ПОЛОЖИЛ:

Используется для создания ресурса или   перезапишите его. Пока вы указываете   ресурсов новый URL.

Для нового ресурса:

PUT /questions/<new_question> HTTP/1.1
Host: www.example.com/

Чтобы перезаписать существующий ресурс:

PUT /questions/<existing_question> HTTP/1.1
Host: www.example.com/

3433



Вы можете найти утверждения в Интернете, которые говорят

Не совсем верно.


Лучше выбирать между PUT и POST на основе идемпотентность действия.

ПОЛОЖИЛ подразумевает помещение ресурса - полностью заменяя все, что доступно на данном URL, с другой. По определению, PUT является идемпотентным. Сделайте это столько раз, сколько хотите, и результат будет таким же. x=5идемпотент. Вы можете указать ресурс, существовавший ранее или нет (например, для создания или для обновления)!

ПОСЛЕ обновляет ресурс, добавляет вспомогательный ресурс или вызывает изменение. POST не является идемпотентным, таким образом, что x++не является идемпотентным.


По этому аргументу PUT предназначен для создания, когда вы знаете URL-адрес того, что вы создадите. POST можно использовать для создания, когда вы знаете URL-адрес «фабрики» или менеджера для категории вещей, которые вы хотите создать.

так:

POST /expense-report

или:

PUT  /expense-report/10929

1855



  • ПОСЛЕ к URL-адресу создает дочерний ресурс на определен сервер URL.
  • ПОЛОЖИЛ к URL-адресу создает / заменяет ресурс в полном объеме на определенный клиентом URL.
  • PATCH к URL-адресу обновления часть ресурса на указанном клиенте URL.

Соответствующая спецификация для PUT и POST RFC 2616 §9.5ff.

POST создает дочерний ресурс , поэтому POST /itemsсоздает ресурсы, которые /itemsресурс. Например. /items/1, Отправка одного и того же почтового пакета дважды приведет к созданию двух ресурсов.

ПОЛОЖИЛ предназначен для создания или замены ресурса при URL, известный клиенту ,

Следовательно: ПОЛОЖИЛ является только кандидатом для CREATE, где клиент уже знает URL-адрес до создания ресурса. Например. /blogs/nigel/entry/when_to_use_post_vs_putпоскольку заголовок используется как ключ ресурса

ПОЛОЖИЛ заменяет ресурс на известном URL-адресе, если он уже существует, поэтому отправка одного и того же запроса дважды не влияет. Другими словами, звонки в PUT являются идемпотентными ,

RFC читает так:

Основное различие между запросами POST и PUT отражается в различном значении Request-URI. URI в запросе POST идентифицирует ресурс, который будет обрабатывать заключенный объект. Этот ресурс может быть процессом принятия данных, шлюзом к другому протоколу или отдельным объектом, который принимает аннотации. Напротив, URI в запросе PUT идентифицирует объект, заключенный с запросом - пользовательский агент знает, что такое URI, и сервер НЕ ДОЛЖЕН пытаться применить запрос к другому ресурсу. Если сервер желает, чтобы запрос был применен к другому URI,

Заметка: PUT в основном использовался для обновления ресурсов (путем их замены в полном объеме), но в последнее время происходит переход к использованию PATCH для обновления существующих ресурсов, поскольку PUT указывает, что он заменяет весь ресурс. RFC 5789.


552



Резюме:

Создайте:

Может выполняться как с PUT, так и с POST следующим образом:

ПОЛОЖИЛ

Создает новый ресурс с newResourceId как идентификатор, в / URI ресурсов или коллекция ,

PUT /resources/<newResourceId> HTTP/1.1 

ПОСЛЕ

Создает новый ресурс в / URI ресурсов или коллекция , Обычно идентификатор возвращается сервером.

POST /resources HTTP/1.1

Обновить:

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

ПОЛОЖИЛ

Обновляет ресурс с помощью existingResourceId как идентификатор, в / URI ресурсов или коллекция ,

PUT /resources/<existingResourceId> HTTP/1.1

Объяснение:

Когда вы работаете с REST и URI как общим, у вас есть общий на оставил а также конкретный на правильно , дженерики обычно называются коллекции и чем больше конкретный пункты можно назвать ресурс , Заметим, что ресурс может содержать коллекция ,

Примеры:

<- общий - - -

URI: website.com/users/john
website.com  - whole site
users        - collection of users
john         - item of the collection, or a resource

URI:website.com/users/john/posts/23
website.com  - whole site
users        - collection of users
john         - item of the collection, or a resource
posts        - collection of posts from john
23           - post from john with identifier 23, also a resource

Когда вы используете POST, вы всегда ссылаясь на коллекция , поэтому всякий раз, когда вы говорите:

POST /users HTTP/1.1

вы отправляете нового пользователя в пользователи коллекция ,

Если вы продолжите и попробуйте что-то вроде этого:

POST /users/john HTTP/1.1

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

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

PUT /users/john HTTP/1.1

вы сообщаете об обновлении сервера или создаете, если он не существует, Джон ресурс под пользователи коллекция ,

Spec:

Позвольте мне выделить некоторые важные части спецификации:

ПОСЛЕ

ПОСЛЕ метод используется для запроса, чтобы исходный сервер принимать объект, заключенный в запросе как новый подчиненный ресурса, идентифицированного Request-URI в строке запроса

Следовательно, создается новый ресурс на коллекция ,

ПОЛОЖИЛ

ПОЛОЖИЛ метод запрашивает, чтобы заключенный объект хранится в соответствии с предоставленным Request-URI. Если Request-URI ссылается на уже существующих ресурс, закрытая сущность СЛЕДУЕТ считаться измененная версия той, которая находится на сервере происхождения. Если Request-URI не указывают на существующее ресурс, и этот URI способный определяется как новый ресурс запрашивающим агентом пользователя, сервер происхождения может Создайте ресурс с этим URI. "

Следовательно, создайте или обновите, основываясь на существовании ресурс ,

Справка:


163



I'd like to add my "pragmatic" advice. Use PUT when you know the "id" by which the object you are saving can be retrieved. Using PUT won't work too well if you need, say, a database generated id to be returned for you to do future lookups or updates.

So: To save an existing user, or one where the client generates the id and it's been verified that the id is unique:

PUT /user/12345 HTTP/1.1  <-- create the user providing the id 12345
Host: mydomain.com

GET /user/12345 HTTP/1.1  <-- return that user
Host: mydomain.com

Otherwise, use POST to initially create the object, and PUT to update the object:

POST /user HTTP/1.1   <--- create the user, server returns 12345
Host: mydomain.com

PUT /user/12345 HTTP/1.1  <--- update the user
Host: mydomain.com

156



POST means "create new" as in "Here is the input for creating a user, create it for me".

PUT means "insert, replace if already exists" as in "Here is the data for user 5".

You POST to example.com/users since you don't know the URL of the user yet, you want the server to create it.

You PUT to example.com/users/id since you want to replace/create a specific user.

POSTing twice with the same data means create two identical users with different ids. PUTing twice with the same data creates the user the first and updates him to the same state the second time (no changes). Since you end up with the same state after a PUT no matter how many times you perform it, it is said to be "equally potent" every time - idempotent. This is useful for automatically retrying requests. No more 'are you sure you want to resend' when you push the back button on the browser.

A general advice is to use POST when you need the server to be in control of URL generation of your resources. Use PUT otherwise. Prefer PUT over POST.


144



Use POST to create, and PUT to update. That's how Ruby on Rails is doing it, anyway.

PUT    /items/1      #=> update
POST   /items        #=> create

103



REST is a very high-level concept. In fact, it doesn't even mention HTTP at all!

If you have any doubts about how to implement REST in HTTP, you can always take a look at the Atom Publication Protocol (AtomPub) specification. AtomPub is a standard for writing RESTful webservices with HTTP that was developed by many HTTP and REST luminaries, with some input from Roy Fielding, the inventor of REST and (co-)inventor of HTTP himself.

In fact, you might even be able to use AtomPub directly. While it came out of the blogging community, it is in no way restricted to blogging: it is a generic protocol for RESTfully interacting with arbitrary (nested) collections of arbitrary resources via HTTP. If you can represent your application as a nested collection of resources, then you can just use AtomPub and not worry about whether to use PUT or POST, what HTTP Status Codes to return and all those details.

This is what AtomPub has to say about resource creation:

To add members to a Collection, clients send POST requests to the URI of the Collection.


58



The decision of whether to use PUT or POST to create a resource on a server with an HTTP + REST API is based on who owns the URL structure. Having the client know, or participate in defining, the URL struct is an unnecessary coupling akin to the undesirable couplings that arose from SOA. Escaping types of couplings is the reason REST is so popular. Therefore, the proper method to use is POST. There are exceptions to this rule and they occur when the client wishes to retain control over the location structure of the resources it deploys. This is rare and likely means something else is wrong.

At this point some people will argue that if RESTful-URL's are used, the client does knows the URL of the resource and therefore a PUT is acceptable. After all, this is why canonical, normalized, Ruby on Rails, Django URLs are important, look at the Twitter API … blah blah blah. Those people need to understand there is no such thing as a Restful-URL and that Roy Fielding himself states that:

A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC's functional coupling].

http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

The idea of a RESTful-URL is actually a violation of REST as the server is in charge of the URL structure and should be free to decide how to use it to avoid coupling. If this confuses you read about the significance of self discovery on API design.

Using POST to create resources comes with a design consideration because POST is not idempotent. This means that repeating a POST several times does not guarantee the same behavior each time. This scares people into using PUT to create resources when they should not. They know it's wrong (POST is for CREATE) but they do it anyway because they don't know how to solve this problem. This concern is demonstrated in the following situation:

  1. The client POST a new resource to the server.
  2. The server processes the request and sends a response.
  3. The client never receives the response.
  4. The server is unaware the client has not received the response.
  5. The client does not have a URL for the resource (therefore PUT is not an option) and repeats the POST.
  6. POST is not idempotent and the server …

Step 6 is where people commonly get confused about what to do. However, there is no reason to create a kludge to solve this issue. Instead, HTTP can be used as specified in RFC 2616 and the server replies:

10.4.10 409 Conflict

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough

information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.

Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can’t complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.

Replying with a status code of 409 Conflict is the correct recourse because:

  • Performing a POST of data which has an ID which matches a resource already in the system is “a conflict with the current state of the resource.”
  • Since the important part is for the client to understand the server has the resource and to take appropriate action. This is a “situation(s) where it is expected that the user might be able to resolve the conflict and resubmit the request.”
  • A response which contains the URL of the resource with the conflicting ID and the appropriate preconditions for the resource would provide “enough information for the user or user agent to fix the problem” which is the ideal case per RFC 2616.

Update based on release of RFC 7231 to Replace 2616

RFC 7231 is designed to replace 2616 and in Section 4.3.3 describes the follow possible response for a POST

If the result of processing a POST would be equivalent to a representation of an existing resource, an origin server MAY redirect the user agent to that resource by sending a 303 (See Other) response with the existing resource's identifier in the Location field. This has the benefits of providing the user agent a resource identifier and transferring the representation via a method more amenable to shared caching, though at the cost of an extra request if the user agent does not already have the representation cached.

It now may be tempting to simply return a 303 in the event that a POST is repeated. However, the opposite is true. Returning a 303 would only make sense if multiple create requests (creating different resources) return the same content. An example would be a "thank you for submitting your request message" that the client need not re-download each time. RFC 7231 still maintains in section 4.2.2 that POST is not to be idempotent and continues to maintain that POST should be used for create.

For more information about this, read this article.


53



I like this advice, from RFC 2616's definition of PUT:

The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.

This jibes with the other advice here, that PUT is best applied to resources that already have a name, and POST is good for creating a new object under an existing resource (and letting the server name it).

I interpret this, and the idempotency requirements on PUT, to mean that:

  • POST is good for creating new objects under a collection (and create does not need to be idempotent)
  • PUT is good for updating existing objects (and update needs to be idempotent)
  • POST can also be used for non-idempotent updates to existing objects (especially, changing part of an object without specifying the whole thing -- if you think about it, creating a new member of a collection is actually a special case of this kind of update, from the collection's perspective)
  • PUT can also be used for create if and only if you allow the client to name the resource. But since REST clients aren't supposed to make assumptions about URL structure, this is less in the intended spirit of things.

48