REST API-단일 요청으로 대량 생성 또는 업데이트 [종료 됨]


94

의이 두 자원은 가정하자 BinderDoc것을 연결 관계의 의미를 Doc하고 Binder자신에 서있다. Doc수도 있고 있지 않을 가능성에 속한다 BinderBinder비어있을 수 있습니다.

사용자가 Docs 컬렉션을 보낼 수있는 REST API를 디자인 하려면 다음과 같이 IN A SINGLE REQUEST .

{
  "docs": [
    {"doc_number": 1, "binder": 1}, 
    {"doc_number": 5, "binder": 8},
    {"doc_number": 6, "binder": 3}
  ]
}

그리고 각 문서에 대한 docs,

  • (가) 경우 doc다음 존재에 할당Binder
  • doc존재하지 않는 경우 생성 한 다음 할당합니다.

이것이 어떻게 구현되어야하는지에 대해 정말 혼란 스럽습니다.

  • 사용할 HTTP 방법은 무엇입니까?
  • 어떤 응답 코드를 반환해야합니까?
  • REST에도 적합합니까?
  • URI는 어떻게 생겼습니까? /binders/docs?
  • 대량 요청을 처리 할 때 일부 항목에서 오류가 발생하지만 다른 항목은 통과하면 어떻게됩니까? 어떤 응답 코드를 반환해야합니까? 대량 작업은 원자 적이어야합니까?

답변:


59

나는 그들이 일반적으로 이것을 위해 설계하기 때문에 이것을 처리하기 위해 POST 또는 PATCH 방법을 사용할 수 있다고 생각합니다.

  • POST메소드 사용 은 일반적으로 목록 리소스에서 사용될 때 요소를 추가하는 데 사용되지만이 메소드에 대해 여러 작업을 지원할 수도 있습니다. 이 답변 : How to Update a REST Resource Collection을 참조하십시오 . 입력에 대해 다른 표현 형식을 지원할 수도 있습니다 (배열 또는 단일 요소에 해당하는 경우).

    이 경우 업데이트를 설명하기 위해 형식을 정의 할 필요가 없습니다.

  • PATCH해당 요청이 부분 업데이트에 해당하므로 메서드를 사용하는 것도 적합합니다. RFC5789 ( http://tools.ietf.org/html/rfc5789 ) 에 따르면 :

    HTTP (Hypertext Transfer Protocol)를 확장하는 여러 응용 프로그램에는 부분적인 리소스 수정 기능이 필요합니다. 기존 HTTP PUT 방법은 문서의 완전한 대체 만 허용합니다. 이 제안은 기존 HTTP 리소스를 수정하기 위해 새로운 HTTP 메소드 인 PATCH를 추가합니다.

    이 경우 부분 업데이트를 설명하는 형식을 정의해야합니다.

나는이 경우에 그 생각, POST그리고 PATCH당신이 정말로 각 요소에 대해 수행 할 작업을 설명 할 필요가 없기 때문에 매우 유사하다. 보낼 표현의 형식에 따라 달라집니다.

의 경우 PUT는 좀 덜 명확합니다. 사실, 메소드를 사용할 때 PUT전체 목록을 제공해야합니다. 사실, 요청에 제공된 표현은 목록 리소스 1을 대체합니다.

리소스 경로와 관련하여 두 가지 옵션이 있습니다.

  • 문서 목록에 리소스 경로 사용

이 경우 요청에서 제공하는 표현에 바인더가있는 문서의 링크를 명시 적으로 제공해야합니다.

다음은 이에 대한 샘플 경로입니다 /docs.

이러한 접근 방식의 내용은 다음과 POST같습니다.

[
    { "doc_number": 1, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 2, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 3, "binder": 5, (other fields in the case of creation) },
    (...)
]
  • 바인더 요소의 하위 리소스 경로 사용

또한 문서와 바인더 간의 링크를 설명하기 위해 하위 경로를 활용하는 것도 고려할 수 있습니다. 문서와 바인더 간의 연결에 대한 힌트는 이제 요청 콘텐츠 내에서 지정할 필요가 없습니다.

다음은 이에 대한 샘플 경로입니다 /binder/{binderId}/docs. 이 경우 문서 목록을 메서드와 함께 보내 POST거나 PATCH문서 binderId가없는 경우 문서를 만든 후 식별자를 사용하여 바인더에 문서를 첨부 합니다.

이러한 접근 방식의 내용은 다음과 POST같습니다.

[
    { "doc_number": 1, (other fields in the case of creation) },
    { "doc_number": 2, (other fields in the case of creation) },
    { "doc_number": 3, (other fields in the case of creation) },
    (...)
]

응답과 관련하여 응답 수준과 반환 할 오류를 정의하는 것은 사용자의 몫입니다. 상태 수준 (글로벌 수준)과 페이로드 수준 (얇은 수준)의 두 가지 수준이 있습니다. 또한 요청에 해당하는 모든 삽입 / 업데이트가 원자 적이어야하는지 여부를 정의하는 것은 사용자의 몫입니다.

  • 원자

이 경우 HTTP 상태를 활용할 수 있습니다. 모든 것이 잘되면 상태가 200됩니다. 그렇지 않은 400경우 제공된 데이터가 올바르지 않거나 (예 : 바인더 ID가 유효하지 않음) 다른 상태와 같은 다른 상태 입니다.

  • 비 원자

이 경우 상태 200가 반환되고 수행 된 작업과 결국 오류가 발생하는 위치를 설명하는 것은 응답 표현에 달려 있습니다. ElasticSearch에는 대량 업데이트를 위해 REST API에 엔드 포인트가 있습니다. 이 수준에서 몇 가지 아이디어를 얻을 수 있습니다. http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html .

  • 비동기

제공된 데이터를 처리하기 위해 비동기 처리를 구현할 수도 있습니다. 이 경우 HTTP 상태 반환은입니다 202. 클라이언트는 어떤 일이 발생하는지 확인하기 위해 추가 리소스를 가져와야합니다.

완료하기 전에 OData 사양이 내비게이션 링크 라는 기능을 사용하여 엔티티 간의 관계에 관한 문제를 해결하고 있음을 알고 싶습니다 . 아마도 당신은 이것을 볼 수 있습니다 ;-)

다음 링크도 도움이 될 수 있습니다 : https://templth.wordpress.com/2014/12/15/designing-a-web-api/ .

도움이 되었기를 바랍니다, Thierry


나는 질문을 따랐다. 중첩 된 하위 리소스가없는 플랫 경로를 선택했습니다. 모든 문서를 가져 오기 위해 GET /docs특정 바인더 내의 모든 문서를 호출 하고 검색합니다 GET /docs?binder_id=x. 자원의 부분 집합 내가 부를 것이다 삭제 DELETE /docs?binder_id=x또는 내가 전화해야 DELETE /docs로모그래퍼 {"binder_id": x}요청 본문에? PATCH /docs?binder_id=x일괄 업데이트에 사용 하시겠습니까 , 아니면 그냥 PATCH /docs통과 쌍을 사용 하시겠습니까?
Andy Fusniak 19 년

35

여러 리소스를 업데이트하고 생성하는 단일 요청이 멱 등성을 가질 가능성이 거의 없기 때문에 POST 또는 PATCH를 사용해야 할 것입니다.

하는 PATCH /docs것은 확실히 유효한 옵션입니다. 특정 시나리오에 대해 표준 패치 형식을 사용하는 것이 까다로울 수 있습니다. 이것에 대해 확실하지 않습니다.

200을 사용할 수 있습니다. 207-Multi Status를 사용할 수도 있습니다.

이것은 RESTful 방식으로 수행 할 수 있습니다. 제 생각에 핵심은 업데이트 / 작성할 문서 세트를 수용하도록 설계된 리소스를 확보하는 것입니다.

PATCH 방법을 사용하는 경우 작업이 원자 적이어야한다고 생각합니다. 즉, 207 상태 코드를 사용하지 않고 응답 본문에 성공과 실패를보고합니다. POST 작업을 사용하면 207 접근 방식이 실행 가능합니다. 성공한 작업과 실패한 작업을 전달하기위한 자체 응답 본문을 디자인해야합니다. 나는 표준화 된 것을 모른다.


정말 고맙습니다. 으로는 This can be done in a RESTful way당신은 갱신을 의미합니까 별도로 수행해야합니다 만들기?
Sam R.

1
@norbertpy 리소스에서 일종의 쓰기 작업을 수행하면 단일 요청에서 다른 리소스가 업데이트되고 생성 될 수 있습니다. REST에는 문제가 없습니다. 필자가 선택한 문구는 일부 프레임 워크가 HTTP 요청을 다중 부분 문서로 직렬화 한 다음 직렬화 된 HTTP 요청을 배치로 전송하여 대량 작업을 구현하기 때문입니다. 나는 그 접근 방식이 리소스 식별 REST 제약 조건을 위반한다고 생각합니다.
Darrel Miller

19

PUT ing

PUT /binders/{id}/docs 단일 문서를 작성 또는 업데이트하고 바인더에 연결

예 :

PUT /binders/1/docs HTTP/1.1
{
  "docNumber" : 1
}

패치 ING

PATCH /docs 문서가없는 경우 문서를 작성하고 바인더와 연결하십시오.

예 :

PATCH /docs HTTP/1.1
[
    { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } },
    { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } },
    { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } }
] 

나중에 추가 인사이트를 포함 할 것이지만, 원하신다면 RFC 5789 , RFC 6902 및 William Durand의 Please를 살펴보십시오 . 바보 블로그 항목 처럼 패치하지 마십시오 .


2
때때로 클라이언트는 대량 작업이 필요하며 리소스가 있는지 여부를 신경 쓰지 않습니다. 내가 질문에 말했듯이, 클라이언트의 무리를 보내고 자 docs와와 연결 binders. 클라이언트는 바인더가 없으면 만들고 연결하려고합니다. ONE SINGLE BULK 요청.
Sam R.

12

내가 일한 프로젝트에서 우리는 '일괄'요청이라는 것을 구현하여이 문제를 해결했습니다. /batch다음 형식으로 json을 허용 하는 경로를 정의했습니다 .

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 5,
         binder: 8
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      }
   },
]

응답의 상태 코드는 207 (다중 상태)이며 다음과 같습니다.

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
      status: 200
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         error: {
            msg: 'A document with doc_number 5 already exists'
            ...
         }
      },
      status: 409
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      },
      status: 200
   },
]

이 구조에서 헤더에 대한 지원을 추가 할 수도 있습니다. 우리는 한 요청의 응답을 다른 요청에 대한 입력으로 사용할 수 있다는 것을 의미하는 일괄 요청간에 사용할 변수 인 유용한 것으로 입증 된 것을 구현했습니다.

Facebook과 Google은 유사한 구현을 가지고 있습니다 :
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

동일한 호출로 리소스를 생성하거나 업데이트하려면 경우에 따라 POST 또는 PUT를 사용합니다. 문서가 이미있는 경우 전체 문서를 다음과 같이 하시겠습니까?

  1. 보낸 문서로 대체 되었습니까 (예 : 요청에서 누락 된 속성이 제거되고 이미 덮어 쓰기 됨)?
  2. 보낸 문서와 병합 되었습니까 (예 : 요청에서 누락 된 속성이 제거되지 않고 기존 속성을 덮어 쓰게 됨)?

대안 1의 동작을 원할 경우 POST를 사용해야하고 대안 2의 동작을 원할 경우 PUT를 사용해야합니다.

http://restcookbook.com/HTTP%20Methods/put-vs-post/

사람들이 이미 PATCH를 사용할 수 있다고 제안했듯이 API를 단순하게 유지하고 필요하지 않은 경우 추가 동사를 사용하지 않는 것을 선호합니다.


5
개념 증명과 Google 및 Facebook 링크에 대한이 답변을 좋아하십시오. 그러나 POST 또는 PUT에 대한 끝 부분에 동의하지 않습니다. 이 답변이 언급 된 두 경우에서 첫 번째는 PUT이고 두 번째는 PATCH 여야합니다.
RayLuo

@RayLuo, POST 및 PUT 외에도 PATCH가 필요한 이유를 설명해 주시겠습니까?
David Berg

2
그것이 PATCH가 발명 된 것이기 때문입니다. 이 정의 를 읽고 PUT 및 PATCH가 2 개의 글 머리 기호와 어떻게 일치하는지 확인할 수 있습니다 .
RayLuo

@DavidBerg, Google은 배치 요청을 처리하는 다른 접근 방식을 선호하는 것 같습니다. 즉, 각 하위 요청의 헤더와 본문을 기본 요청의 해당 부분에 --batch_xxxx. Google과 Facebook의 솔루션간에 몇 가지 중요한 차이점이 있습니까? 또한 "한 요청의 응답을 다른 요청에 대한 입력으로 사용"에 대해 매우 흥미롭게 들립니다. 자세한 내용을 공유해 주시겠습니까? 또는 어떤 종류의 시나리오를 사용해야합니까?
Yang
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.