RESTFul : 상태 변경 조치


59

RESTfull API를 만들 계획이지만 내 머리에 문제를 일으키는 아키텍처 질문이 있습니다. 비즈니스 로직이 빠르게 변경 될 수있는 경우 여러 클라이언트 플랫폼을 업데이트하는 것은 실시간으로 유지하기 어렵 기 때문에 클라이언트에 백엔드 비즈니스 로직을 추가하는 것은 피하고 싶은 옵션입니다.

우리가 기사를 자원 (api / article)으로 가지고 있다고 가정 해 봅시다. 게시, 게시 취소, 활성화 또는 비활성화 등과 같은 작업을 어떻게 구현해야하지만 가능한 한 간단하게 유지하려고합니까?

1) 원격 위치로 이동하거나 여러 속성을 변경하는 등 많은 백엔드 논리가 발생할 수 있으므로 api / article / {id} / {action}을 사용해야합니다. 아마도 여기서 가장 어려운 것은 업데이트하기 위해 모든 기사 데이터를 API로 다시 보내야하며 다중 사용자 작업을 구현할 수 없다는 것입니다. 예를 들어 편집기는 5 초 더 오래된 데이터를 보내고 다른 기자가 2 초 전에 방금 수정 한 수정 사항을 덮어 쓸 수 있으며 기사를 게시하는 사람들은 실제로 콘텐츠 업데이트와 관련이 없기 때문에 고객에게 설명 할 방법이 없습니다.

2) 새 리소스를 만드는 것도 api / article- {action} / id 옵션 일 수 있지만 반환 된 리소스는 article- {action}이 아니라 이것이 올바른지 확실하지 않은 기사입니다. 또한 서버 측 코드 기사 클래스에서 리소스 모두에 대한 실제 작업을 처리하고 있으며 이것이 RESTfull 사고에 반대하는지 확실하지 않습니다.

모든 제안을 환영합니다 ..


'조치'가 RESTful URI의 일부가되는 것은 완벽하게 합법적입니다. 수행 할 조치 / 알고리즘이 명시된 경우. 무슨 일이야 api/article?action=publish? 쿼리 매개 변수는 리소스 상태가 언급 한 '알고리즘'(또는 작업)에 따라 달라지는 경우에 사용됩니다. 예 : api/articles?sort=asc유효
PhD

1
글 을 확인하여 RESTful 한 솔루션으로 영감을 얻을 수 있습니다 .
Benjol

api / article? action = publish에서 볼 수있는 문제 중 하나는 RESTfull 응용 프로그램에서 게시를 위해 모든 기사 데이터를 보내야하지만이 작업을 선호하는 것입니다. api / article / 4545 / publish / 추가 항목없이
Miro Svrtan

2
이 문제에 대한 우수한 리소스 : REST API 디자인-리소스 모델링
Izhaki

@PhD : 프로토콜에서는 완벽하게 합법적이지만 URI는 일반적으로 동사가 아닌 명사 여야합니다. URI에 동사가있는 것은 일반적으로 REST 디자인이 잘못되었음을 나타냅니다.
Lie Ryan

답변:


49

여기 에 설명 된 관행 이 도움이된다고 생각합니다.

CRUD 운영의 세계에 맞지 않는 행동은 어떻습니까?

이곳은 상황이 흐려질 수있는 곳입니다. 여러 가지 접근 방식이 있습니다.

  1. 자원 필드처럼 보이도록 조치를 재구성하십시오. 동작이 매개 변수를 사용하지 않는 경우 작동합니다. 예를 들어 활성화 조치는 부울 activated필드에 맵핑되고 PATCH를 통해 자원으로 갱신 될 수 있습니다 .
  2. RESTful 원칙을 가진 하위 리소스처럼 취급하십시오. 예를 들어 GitHub의 API를 사용하면 요점별표 를 표시 PUT /gists/:id/star하고 별표 표시 를 해제 할 수 있습니다 DELETE /gists/:id/star.
  3. 때로는 액션을 합리적인 RESTful 구조에 매핑 할 수있는 방법이 없습니다. 예를 들어, 다중 리소스 검색은 특정 리소스의 엔드 포인트에 적용되는 것이 적절하지 않습니다. 이 경우 /search리소스가 아니더라도 가장 의미가 있습니다. API 소비자의 관점에서 옳은 일을하고 혼란을 피하기 위해 명확하게 문서화되도록하십시오.

접근 방식 2에 투표합니다. API 호출자에게는 어색한 인공 자원처럼 보일 수 있지만 실제로 서버에서 무슨 일이 일어나고 있는지 전혀 알지 못합니다. /article/123/deactivations기사 123에 대해 새 비활성화 요청을 작성하기 위해 POST 를 호출 하면 서버는 요청 된 자원을 비활성화 할뿐만 아니라 실제로 비활성화 요청을 저장하여 나중에 상태를 검색 할 수 있습니다.
JustAMartin

2
PUT /gists/:id/star 안돼 POST /gists/:id/star?
Filip Bartuzi

9
@FilipBartuzi PUT은 dem 등성이 기 때문에 (즉, 동일한 매개 변수로 조치를 여러 번 수행하더라도 결과는 항상 동일합니다 (예 : 조명을 껐다 켜면 변경됨). 다시 켜지지 만 아무런 변화가 없습니다-이미 켜져 있습니다). POST는 dem 등성이 아닙니다. 즉, 동일한 매개 변수를 사용하더라도 조치를 수행 할 때마다 조치의 결과가 다릅니다 (예 : 다른 사람에게 편지를 보낼 경우 해당 사람이 편지를 받았습니다). 같은 사람은 이제 2 글자가 있습니다).
Raphael

9

설명하는 "게시"조치와 같이 서버 측에서 주요 상태 및 동작 변경을 초래하는 조작은 REST에서 명시 적으로 모델링하기가 어렵습니다. 내가 흔히 볼 수있는 해결책은 데이터를 통해 이러한 복잡한 동작을 암시 적으로 유도하는 것입니다.

온라인 판매자가 공개 한 REST API를 통한 상품 주문을 고려하십시오. 주문은 복잡한 작업입니다. 여러 제품이 포장되어 배송되며 계정에 요금이 청구되며 영수증을 받게됩니다. 한정된 기간 동안 주문을 취소 할 수 있으며 환불을 위해 제품을 반품 할 수있는 전액 환불 보증이 제공됩니다.

복잡한 구매 작업 대신 이러한 API를 사용하면 새로운 리소스 인 구매 주문을 만들 수 있습니다. 처음에는 제품 추가 또는 제거, 배송 주소 변경, 다른 지불 옵션 선택 또는 주문 취소 등 원하는대로 수정할 수 있습니다. 아직 아무것도 사지 않았기 때문에이 모든 작업을 수행 할 수 있습니다. 서버의 일부 데이터 만 조작하는 것입니다.

구매 주문이 완료되고 유예 기간이 지나면 서버는 추가 변경을 방지하기 위해 주문을 잠급니다. 이때에만 복잡한 작업 순서가 시작되지만 이전에 구매 주문서에 배치 한 데이터를 통해서만 간접적으로 직접 제어 할 수는 없습니다.

설명에 따라 "게시"는 이러한 방식으로 구현 될 수 있습니다. 작업을 노출하는 대신 검토 한 초안의 사본을 / publish 아래에 새 자원으로 게시하려고합니다. 이렇게하면 게시 작업 자체가 몇 시간 후에 완료 되더라도 이후에 초안에 대한 업데이트가 게시되지 않습니다.


게시되지 않은 기사에서 초안으로 전체 리소스를 변경하려는 아이디어는이 경우와 정확히 일치하지만 일반적으로 리소스에 존재하는 다른 모든 작업에는 적합하지 않습니다. REST도 처리해야합니까? 어쩌면 나는 그것을 학대하고 CRUD로만 사용해야하며 SQL 쿼리와 같이 로직 자체가 쿼리 자체 안에 없을 것으로 예상하는 SQL 쿼리와 같이 아무것도 사용하지 않아야합니다.
Miro Svrtan

왜이 모든 것을 묻고 있습니까? 얼마 전부터 웹 앱은 다중 플랫폼으로 시작하기 때문에 iOS, Android, 웹, 데스크톱 또는 다른 플랫폼에서 떠오르는 비즈니스 로직을 업데이트하는 것이 불가능하기 때문에 서버에 많은 탈취 로직을 유지하는 것을 선호합니다. 작은 BL 조각을 변경할 때 이전 버전과의 호환성 문제를 모두 피하고 싶습니다.
Miro Svrtan

2
REST가 비즈니스 로직을 잘 처리 할 수 ​​있다고 생각하지만 REST를 염두에 두지 않고 작성된 기존 비즈니스 로직을 노출하는 데 적합하지 않습니다. 이것이 바로 Microsoft, SAP 및 기타 회사와 같은 많은 회사가 귀하가 말한 것처럼 CRUD 작업으로 데이터 만 노출하는 이유입니다. 어떻게 작동하는지 보려면 Open data Protocol을 살펴보십시오.
Ferenc Mihaly

7

업데이트를 위해 모든 기사 데이터를 API로 다시 보내야하며 다중 사용자 작업을 구현할 수 없습니다. 예를 들어 편집기는 5 초 더 오래된 데이터를 보내고 다른 기자가 2 초 전에 방금 수정 한 수정 사항을 덮어 쓸 수 있으며 기사를 게시하는 사람들은 실제로 콘텐츠 업데이트와 관련이 없기 때문에이를 고객에게 설명 할 방법이 없습니다.

이런 종류의 일은 당신이 무엇을하든 도전입니다. 분산 소스 제어 (mercurial, git 등)와 매우 유사한 문제이며 HTTP / ReST로 철자 한 솔루션은 약간 비슷합니다.

Alice와 Bob이라는 두 명의 사용자가 모두 작업하고 있다고 가정합니다 /articles/lunch. (명확하게하기 위해 응답은 굵은 글씨로 표시됨)

먼저, alice가 기사를 작성합니다.

PUT /articles/lunch HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?

301 Moved Permanently
Location: /articles/lunch/1 

요청에 첨부 된 "버전"이 없기 때문에 서버가 리소스를 만들지 않았습니다 (의 식별자를 가정합니다 /articles/{id}/{version}. 생성을 수행하기 위해 Alice는 만들려는 기사 / 버전의 URL로 리디렉션 됨) Alice의 사용자 상담원은 새 주소에서 요청을 다시 적용합니다.

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?

201 Created

그리고 이제 기사가 작성되었습니다. 다음으로 밥은 기사를 봅니다.

GET /articles/lunch HTTP/1.1
Host: example.com
Authorization: Basic Ym9iOnBhc3N3b3Jk

301 Moved Permanently
Location: /articles/lunch/1 

밥은 거기에 보인다 :

GET /articles/lunch/1 HTTP/1.1
Host: example.com
Authorization: Basic Ym9iOnBhc3N3b3Jk

200 Ok
Content-Type: text/plain

Hey Bob, what do you want for lunch today?

그는 자신의 변화를 추가하기로 결정합니다.

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic Ym9iOnBhc3N3b3Jk

Hey Bob, what do you want for lunch today?
Does pizza sound good to you, Alice?

301 Moved Permanently
Location: /articles/lunch/2

Alice와 마찬가지로 Bob은 새 버전을 만들 위치로 리디렉션됩니다.

PUT /articles/lunch/2 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic Ym9iOnBhc3N3b3Jk

Hey Bob, what do you want for lunch today?
Does pizza sound good to you, Alice?

201 Created

마지막으로 Alice는 자신의 기사에 추가하기로 결정합니다.

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?
I was thinking about getting Sushi.

409 Conflict
Location: /articles/lunch/3
Content-Type: text/diff

---/articles/lunch/2
+++/articles/lunch/3
@@ 1,2 1,2 @@
 Hey Bob, what do you want for lunch today?
-Does pizza sound good to you, Alice?
+I was thinking about getting Sushi.

정상적인 상태로 리디렉션되는 대신 다른 상태 코드가 클라이언트에 반환되어 409Alice가 분기하려는 버전이 이미 분기되었음을 알립니다. 어쨌든 ( Location헤더 로 표시 한대로) 새 자원이 작성되었으며 두 자원 의 차이점이 응답 본문에 포함되었습니다. Alice는 이제 방금 요청한 내용을 어떻게 병합해야하는지 알고 있습니다.


이 모든 리디렉션은의 의미와 관련되어 PUT있으므로 요청 라인이 요청하는 위치에 정확하게 새로운 리소스를 만들어야합니다. 이것은 POST대신 사용하여 요청주기를 절약 할 수 있지만 버전 번호는 다른 마술에 의해 요청에 인코딩되어야합니다. 이는 설명을 위해 나에게 덜 분명해 보이지만 실제 API에서는 여전히 선호됩니다. 요청 / 응답주기를 최소화합니다.


1
Versoning은 여기서 문제가 아니 었습니다. 기사를 게시 작업을위한 리소스로 사용하는 경우 가능한 문제의 예라고 말했습니다
Miro Svrtan

3

다음은 문서 내용을 다루지 않고 일시적인 상태를 다루는 또 다른 예입니다. (일반적으로 각 버전이 새로운 리소스가 될 수 있다는 점을 감안할 때 버전 관리가 발견됩니다. 일종의 쉬운 문제입니다.)

REST를 통해 머신에서 실행되는 서비스를 중지, 시작, 다시 시작하는 등의 서비스를 제공하고 싶다고 가정 해 봅시다.

여기서 가장 RESTful 한 방법은 무엇입니까? POST / service? command = 다시 시작 하시겠습니까? 또는 '실행 중'이라는 본문이있는 POST / service / state?

여기에서 모범 사례를 정리하고 REST가 이러한 유형의 상황에 적합한 지 여부를 체계화하는 것이 좋습니다.

둘째, 자체 상태에 영향을 미치지 않고 부작용을 유발하는 서비스에서 일부 작업을 수행한다고 가정 해 봅시다. 예를 들어, 호출시 작성된 보고서를 여러 이메일 주소로 전송하는 메일러 서비스입니다.

GET / report는 보고서 사본을 직접 얻는 방법 일 수 있습니다. 그러나 위에서 말한 것처럼 이메일 전송과 같은 추가 조치를 서버 측으로 푸시하려면 어떻게해야합니까? 또는 데이터베이스에 쓰는 중입니다.

이 사례는 자원 행동 분할을 중심으로 춤을 추며 REST 지향 방식으로 처리하는 방법을 알지만 솔직히 그렇게하는 것은 약간의 해킹처럼 느껴집니다. 아마도 주요 질문은 REST API가 일반적으로 부작용을 지원해야하는지 여부입니다.


2

REST는 데이터 지향적이며 이러한 자원은 조치가 아닌 "사물"로 가장 잘 작동합니다. http 메소드의 내재적 의미; GET, PUT, DELETE 등은 방향을 강화하는 역할을합니다. 물론 POST는 예외입니다.

리소스는 데이터의 혼합 일 수 있습니다. 기사 내용; 메타 데이터 즉. 게시, 잠김, 수정 데이터를 슬라이스 할 수있는 다른 방법이 많이 있지만, 가장 최적 인 것을 결정하기 위해 데이터 흐름이 먼저 어떻게 보이는지 살펴 봐야합니다 (있는 경우). 예를 들어, 개정은 TokenMacGuy가 제안한 기사에서 자체 리소스 여야합니다.

구현에 관해서는 아마도 TockenMacGuy가 제안한 것과 같은 것을 할 것입니다. 또한 'locked'및 'published'와 같이 개정 본이 아닌 아티클에 메타 데이터 필드를 추가합니다.


1

기사의 상태를 직접 조작하는 것으로 생각하지 마십시오. 대신 기사 작성을 요청 하는 변경 순서 를 설정합니다.

새로운 변경 주문 리소스 (POST)를 생성하여 변경 주문을 모델링 할 수 있습니다. 많은 장점이 있습니다. 예를 들어, 변경 순서의 일부로 기사를 게시 할 미래의 날짜와 시간을 지정하고 서버에서 구현 방식에 대해 걱정하도록 할 수 있습니다.

게시가 즉각적인 프로세스가 아닌 경우 클라이언트로 돌아 오기 전에 완료 될 때까지 기다릴 필요가 없습니다. 변경 주문이 생성되었음을 확인하고 변경 주문 ID를 반환합니다. 그런 다음 해당 변경 순서에 해당하는 URL을 사용하여 변경 순서의 상태를 공유 할 수 있습니다.

나를위한 주요 통찰은이 변경 순서 비유를 인식하는 것이 객체 지향 프로그래밍을 설명하는 또 다른 방법 일뿐입니다. 자원 대신 우리는 객체라고 부릅니다. 변경 순서 대신 메시지라고합니다. OO에서 A에서 B로 메시지를 보내는 한 가지 방법은 A가 B에서 메서드를 호출하는 것입니다. 특히 다른 컴퓨터에 A와 B가있을 때 A가 새 개체 M을 만들고 REST는 B에게 전달합니다. REST는 단순히 해당 프로세스를 공식화합니다.


실제로 이것은 OO보다 Actor 모델에 더 가깝다고 생각합니다. REST 요청을 메시지 (메시지)로 간주하면 상태 관리를 위해 이벤트 소싱에 매우 정확하게 정렬됩니다. REST는 CQRS 라인을 따라 상호 작용을 깔끔하게 나눕니다. GET 메시지는 Query, POST, PUT, PATCH, DELETE가 Command 영역에 속합니다.
WillD

0

내가 당신을 올바르게 이해한다면, 당신이 가진 것은 기술적 인 문제보다 '비즈니스 규칙'결정 문제에 더 가깝다고 생각합니다.

기사를 덮어 쓸 수 있다는 사실은 상급 사용자가 주니어 사용자의 버전을 재정의 할 수있는 권한 부여 수준을 도입하여 해결할 수 있습니다. 또한 기사의 상태를 캡처하는 열과 버전을 도입하여 (예 : '개발 중', '최종' 등등), 당신은 이것을 극복 할 수 있습니다. 제출 시간과 버전 번호의 조합으로 지정된 버전을 선택할 수있는 기능을 사용자에게 제공 할 수도 있습니다.

위의 모든 경우에 서비스는 설정 한 비즈니스 규칙을 구현해야합니다. 따라서 userid, article, version, action 매개 변수를 사용하여 서비스를 호출 할 수 있습니다 (버전이 선택적인 경우 비즈니스 규칙에 따라 다름).


나는 이것이 비즈니스 규칙이지만 엄밀히 기술적 인 규칙이라는 것을 믿지 않습니다. 버전을 추가한다는 아이디어는 규칙을 재정의하는 데 도움이되지만 여전히 컨텐츠 업데이트 및 컨텐츠 공개가 관련 조치가 아닌 상황을 해결하지는 못합니다.
Miro Svrtan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.