문제

REST에서 다음 사용 사례를 어떻게 구현하는지 궁금합니다.개념적 모델을 손상시키지 않고 수행하는 것이 가능합니까?

단일 트랜잭션 범위 내에서 여러 리소스를 읽거나 업데이트합니다.예를 들어, Bob의 은행 계좌에서 John의 계좌로 100달러를 이체합니다.

내가 아는 한, 이를 구현하는 유일한 방법은 부정행위입니다.John 또는 Bob과 연결된 리소스에 POST하고 단일 트랜잭션을 사용하여 전체 작업을 수행할 수 있습니다.내가 생각하는 한 이것은 실제로 개별 리소스에서 작동하는 대신 POST를 통해 RPC 호출을 본질적으로 터널링하기 때문에 REST 아키텍처를 깨뜨립니다.

도움이 되었습니까?

해결책

편안한 쇼핑 바구니 시나리오를 고려하십시오. 쇼핑 바구니는 개념적으로 거래 포장지입니다. 쇼핑 바구니에 여러 품목을 추가 한 다음 해당 바구니를 제출하여 주문을 처리 할 수있는 것과 같은 방식으로 Bob의 계정 항목을 거래 래퍼에 추가 한 다음 Bill의 계정 항목을 래퍼에 추가 할 수 있습니다. 모든 조각이 제자리에 있으면 모든 구성 요소 조각으로 트랜잭션 래퍼를 게시/넣을 수 있습니다.

다른 팁

이 질문에 의해 답변되지 않은 몇 가지 중요한 사례가 있습니다. 검색어에 대해 Google에서 높은 순위를 가지고 있기 때문에 너무 나쁘다고 생각합니다.

구체적으로, 좋은 속성은 다음과 같습니다. 두 번 게시하면 (일부 캐시가 중간에 hiccuped하기 때문에) 금액을 두 번 전송해서는 안됩니다.

이를 얻으려면 객체로서 트랜잭션을 만듭니다. 여기에는 이미 알고있는 모든 데이터가 포함되어 있으며 거래를 보류 상태에 두십시오.

POST /transfer/txn
{"source":"john's account", "destination":"bob's account", "amount":10}

{"id":"/transfer/txn/12345", "state":"pending", "source":...}

이 거래가 있으면 다음과 같은 거래를 할 수 있습니다.

PUT /transfer/txn/12345
{"id":"/transfer/txn/12345", "state":"committed", ...}

{"id":"/transfer/txn/12345", "state":"committed", ...}

이 시점에서 다중 퍼트는 중요하지 않습니다. TXN에 도착하더라도 현재 상태를 반환합니다. 구체적으로, 두 번째 풋은 첫 번째 풋이 이미 적절한 상태에 있음을 감지하고, 그냥 반환 할 것입니다. 오류 및 실제 커밋 된 트랜잭션이 다시.

단일 데이터베이스 또는 통합 트랜잭션 모니터가있는 데이터베이스와 대화하는 한이 메커니즘은 실제로 잘 작동합니다. 트랜잭션에 대한 타임 아웃을 추가로 도입 할 수 있으며, 원하는 경우 Expire Headers를 사용하여 표현할 수도 있습니다.

휴식 용어로, 자원은 CRUD (생성/읽기/업데이트/삭제) 동사로 행동 할 수있는 명사입니다. "전송 돈"동사가 없으므로 CRUD로 수행 할 수있는 "거래"자원을 정의해야합니다. 다음은 HTTP+Pox의 예입니다. 첫 번째 단계는 만들다 (http post method) 새로운 비어 있는 거래:

POST /transaction

이것은 트랜잭션 ID (예 : "1234"및 URL "/Transaction/1234"에 따라 반환됩니다. 이 게시물을 여러 번 해고하면 여러 ID와 동일한 트랜잭션이 생기지 않으며 "보류중인"상태의 도입을 피할 수 있습니다. 또한 Post가 항상 idempotent (REST 요구 사항) 일 수는 없으므로 일반적으로 게시물의 데이터를 최소화하는 것이 좋습니다.

트랜잭션 ID 생성을 클라이언트에게 맡길 수 있습니다. 이 경우 트랜잭션 "1234"를 생성하기 위해 /Transaction /1234를 게시하면 서버가 이미 존재하면 오류를 반환합니다. 오류 응답에서 서버는 적절한 URL이있는 현재 사용하지 않은 ID를 반환 할 수 있습니다. GET가 서버 상태를 변경해서는 안되며 새 ID를 생성하면 서버 상태가 변경 될 수 있으므로 GET 메소드를 사용하여 새 ID에 대한 서버를 쿼리하는 것은 좋은 생각이 아닙니다.

다음으로, 우리 업데이트 (HTTP 방법을 넣음) 모든 데이터와의 거래를 암시 적으로 커밋합니다.

PUT /transaction/1234
<transaction>
  <from>/account/john</from>
  <to>/account/bob</to>
  <amount>100</amount>
</transaction>

ID "1234"와의 트랜잭션이 이전에 제시된 경우 서버는 오류 응답을 제공합니다. 그렇지 않으면 완성 된 트랜잭션을 볼 수있는 OK 응답 및 URL이 제공됩니다.

NB : /Acc

좋은 질문, REST는 주로 데이터베이스와 같은 예제로 설명되며, 여기서 무언가가 저장, 업데이트, 검색, 삭제됩니다. 서버가 어떤 방식으로 데이터를 처리 해야하는이 예제는 거의 없습니다. 나는 Roy Fielding이 그의 논문에 어떤 것도 포함되었다고 생각하지 않습니다.

그러나 그는 링크가 다음 상태로 이동하면서 상태 기계로서 "표현 상태 이전"에 대해 이야기합니다. 이런 식으로 문서 (표현)는 서버가 수행하는 대신 클라이언트 상태를 추적합니다. 이런 식으로, 클라이언트 상태는 없으며, 어떤 링크가있는 상태에서만 상태 만 있습니다.

나는 이것에 대해 생각하고 있었고, 서버가 당신을 위해 무언가를 처리하도록하는 것이 합리적입니다. 업로드하면 서버가 자동으로 관련 리소스를 생성하고 링크를 제공합니다 (사실, 실제로는 링크를 제공합니다. '자동으로 만들어야합니다. 링크를 알려줄 수 있으며 링크를 말할 수 있습니다. 또한 생성 링크를 제공합니다 새로운 관련 리소스 - 관련 리소스의 URI는 동일하지만 더 길다 (접미사 추가). 예를 들어:

  1. 업로드 (게시하다) a의 개념의 표현 모든 정보와의 거래. 이것은 RPC 호출처럼 보이지만 실제로 "제안 된 트랜잭션 리소스"를 생성하고 있습니다. 예 : URI : /transaction결함은 각각 다른 URI를 사용하여 여러 자원을 생성하게됩니다.
  2. 서버의 응답은 생성 된 리소스의 URI, 그 표현을 명시하고 있습니다. 여기에는 링크가 포함됩니다 (우리) 관련 리소스를 작성합니다 새로운 "커밋 된 거래 리소스". 다른 관련 리소스는 제안 된 거래를 삭제하는 링크입니다. 이들은 고객이 따를 수있는 주 대기업의 상태입니다. 논리적으로, 이들은 고객이 제공 한 정보를 넘어 서버에서 작성된 리소스의 일부입니다. 예 : Uris : /transaction/1234/proposed, /transaction/1234/committed
  3. 게시하다 링크에 "커밋 된 거래 리소스"생성, 이 자원을 생성하여 서버의 상태를 변경합니다 (두 계정의 균형) **. 본질적 으로이 리소스는 한 번만 만들 수 있으며 업데이트 할 수 없습니다. 따라서 많은 거래를 저지른 결함은 발생할 수 없습니다.
  4. 당신은 그들의 상태가 무엇인지 확인하기 위해이 두 자원을 얻을 수 있습니다. 게시물이 다른 리소스를 변경할 수 있다고 가정하면 제안은 이제 "커밋 된"것으로 표시됩니다 (또는 아마도 전혀 사용할 수 없음).

이는 웹 페이지가 작동하는 방식과 유사하며 최종 웹 페이지가 "이 작업을 수행하고 싶습니까?" 최종 웹 페이지 자체는 트랜잭션 상태를 나타내는 것으로, 다음 상태로 이동할 수있는 링크가 포함됩니다. 금융 거래뿐만 아니라; 또한 (예 :) 미리보기와 Wikipedia에 커밋하십시오. 휴식의 차이점은 국가 순서의 각 단계에 명시적인 이름 (URI)이 있다는 것입니다.

실제 거래/판매에는 거래의 다른 단계 (제안, 구매 주문, 영수증 등)에 대한 물리적 문서가 종종 있습니다. 집을 구입하고 정착지 등

Otoh 이것은 나에게 의미론을 가지고 노는 것 같은 느낌이 든다. 동사를 명사로 변환하여 명사로 변환하여 "동사 (rpc 호출) 대신 명사 (URI)를 사용하기 때문에 편안한 동사를 명사로 변환하는 것이 불편합니다. 즉, "이 거래 커밋"대신 명사 "커밋 된 거래 리소스". 공칭화의 장점 중 하나는 다른 방법으로 지정할 필요가있는 대신 리소스를 이름으로 언급 할 수 있다는 것입니다 (예 : 세션 상태를 유지하는 것과 같은 "이"트랜잭션이 무엇인지 알고 있습니다 ...)

그러나 중요한 질문은 :이 접근법의 이점은 무엇입니까? 즉, RPC 스타일 보다이 휴식 스타일이 어떤 방식으로 더 좋습니까? 웹 페이지에 적합한 기술이 저장/검색/업데이트/삭제 이외의 정보 처리에도 도움이됩니까? 휴식의 주요 이점은 확장 성이라고 생각합니다. 그것의 한 가지 측면은 클라이언트 상태를 명시 적으로 유지할 필요가 없지만 (자원의 URI에 암시 적으로, 다음 상태는 그 표현의 링크로서) 그런 의미에서 그것은 도움이됩니다. 아마도 이것은 레이어링/파이프 라인에도 도움이됩니까? OTOH One 사용자만이 특정 트랜잭션을 살펴볼 것이므로 캐싱 할 때는 이점이 없으므로 다른 사람이 읽을 수 있도록 HTTP의 큰 승리입니다.

여기서 토론을 요약하기 위해 다시 서 있으면, 특히 클라이언트-서버 상호 작용이 본질적으로 비 사소한 거래와 마찬가지로 고유 한 상태가 될 때 REST가 많은 API에 적합하지 않다는 것이 분명합니다. 문제에 맞지 않는 몇 가지 원칙을 따르기 위해 클라이언트와 서버 모두에 대해 제안 된 모든 후프를 뛰어 넘는 이유는 무엇입니까? 더 나은 원칙은 고객에게 응용 프로그램을 구성하는 가장 쉽고 자연스럽고 생산적인 방법을 제공하는 것입니다.

요약하면, 응용 프로그램에서 많은 트랜잭션 (인스턴스가 아닌 유형)을 실제로 수행하는 경우 실제로 편안한 API를 만들어서는 안됩니다.

TX 관리의 "트랜잭션 ID"유형을 출시해야합니다. 그래서 그것은 4 개의 전화가 될 것입니다.

http://service/transaction (some sort of tx request)
http://service/bankaccount/bob (give tx id)
http://service/bankaccount/john (give tx id)
http://service/transaction (request to commit)

DB (로드 균형 균형이 잡힌 경우) 또는 메모리 또는 그와 그 다음에 커밋, 롤백, 타임 아웃에서 동작을 저장해야합니다.

실제로 공원에서 편안한 날이 아닙니다.

나는 이 주제에서 10년 동안 멀어져 왔습니다.다시 돌아와서, Google에서 Rest+Reliable을 검색할 때 과학으로 가장한 종교를 접하게 되었다는 사실을 믿을 수 없습니다.혼란은 신화적이다.

나는 이 광범위한 질문을 세 가지로 나누고 싶습니다.

  • 다운스트림 서비스.귀하가 개발하는 모든 웹 서비스에는 귀하가 사용하는 다운스트림 서비스가 있으며, 그 트랜잭션 구문을 따를 수밖에 없습니다.서비스 사용자에게 이 모든 것을 숨기고 작업의 모든 부분이 그룹으로서 성공 또는 실패했는지 확인한 다음 이 결과를 사용자에게 반환해야 합니다.
  • 귀하의 서비스.클라이언트는 웹 서비스 호출에 대한 명확한 결과를 원하며, 실질적인 리소스에 대해 직접 POST, PUT 또는 DELETE 요청을 수행하는 일반적인 REST 패턴은 이러한 확실성을 제공하는 열악하고 쉽게 개선되는 방식으로 생각됩니다.안정성에 관심이 있다면 작업 요청을 식별해야 합니다.이 ID는 클라이언트에서 생성된 guid일 수도 있고 서버에 있는 관계형 DB의 시드 값일 수도 있으며 중요하지 않습니다.서버 생성 ID의 경우 요청-응답은 ID 교환에만 사용됩니다.이 요청이 실패하거나 절반만 성공해도 문제 없습니다. 클라이언트는 요청을 반복하기만 하면 됩니다.사용하지 않는 ID는 해를 끼치 지 않습니다.

    이는 모든 후속 요청이 완전히 멱등성을 갖도록 하기 때문에 중요합니다. 즉, n번 반복되면 동일한 결과를 반환하고 더 이상 아무 일도 발생하지 않는다는 의미입니다.서버는 작업 ID에 대한 모든 응답을 저장하고 동일한 요청이 있으면 동일한 응답을 재생합니다.패턴을 더욱 완벽하게 처리한 것입니다. 이 구글 문서.이 문서는 REST 원칙을 광범위하게 따르는 구현을 제안합니다(!).전문가들은 그것이 어떻게 다른 사람을 침해하는지 알려줄 것입니다.이 패턴은 관련된 다운스트림 트랜잭션이 있는지 여부에 관계없이 웹 서비스에 대한 안전하지 않은 호출에 유용하게 사용될 수 있습니다.
  • 업스트림 서비스에 의해 제어되는 "트랜잭션"에 서비스를 통합합니다.웹 서비스의 맥락에서 전체 ACID 거래는 일반적으로 노력할 가치가 없는 것으로 간주되지만 확인 응답에 취소 및/또는 확인 링크를 제공하여 서비스 소비자에게 큰 도움을 줄 수 있습니다. 보상에 의한 거래.

귀하의 요구 사항은 기본적인 것입니다.사람들이 당신의 솔루션이 정결하지 않다고 말하지 못하게 하십시오.문제를 얼마나 잘, 얼마나 간단하게 해결하는지에 비추어 아키텍처를 판단하십시오.

우선 돈을 이체하는 것은 단일 리소스 호출로 할 수 없는 일이 아닙니다.당신이하고 싶은 행동은 돈을 보내는 것입니다.따라서 송금인의 계좌에 송금 리소스를 추가합니다.

POST: accounts/alice, new Transfer {target:"BOB", abmount:100, currency:"CHF"}.

완료.이것이 원자성이어야 하는 트랜잭션이라는 것을 알 필요가 없습니다.일명 돈을 이체하면됩니다.A에서 B로 돈을 보내세요.


그러나 드문 경우에 일반적인 해결책은 다음과 같습니다.

실제로 무엇과 무엇을 교차하는 많은 제한이 있는 정의된 컨텍스트에서 많은 리소스를 포함하는 매우 복잡한 작업을 수행하려는 경우장벽이 있는 이유(비즈니스 대구현 지식) 상태를 전송해야 합니다.REST는 상태 비저장이어야 하므로 클라이언트로서 상태를 전송해야 합니다.

상태를 전송하는 경우 클라이언트로부터 내부 정보를 숨겨야 합니다.클라이언트는 구현에만 필요한 내부 정보를 알아야 하며 비즈니스 측면에서 관련된 정보를 갖고 있지 않아야 합니다.해당 정보가 비즈니스 가치가 없는 경우 상태를 암호화해야 하며 토큰, 패스 등과 같은 비유를 사용해야 합니다.

이렇게 하면 내부 상태를 전달할 수 있으며 암호화를 사용하고 시스템에 서명하는 것이 여전히 안전하고 건전할 수 있습니다.클라이언트가 상태 정보를 전달하는 이유에 대한 올바른 추상화를 찾는 것은 디자인과 아키텍처에 달려 있습니다.


실제 솔루션:

REST는 HTTP를 말하고 있으며 HTTP에는 쿠키 사용 개념이 함께 제공된다는 점을 기억하세요.사람들이 REST API, 워크플로, 여러 리소스나 요청에 걸친 상호 작용에 대해 이야기할 때 이러한 쿠키는 종종 잊혀집니다.

HTTP 쿠키에 관해 Wikipedia에 기록된 내용을 기억하세요.

쿠키는 웹사이트가 상태 정보(장바구니에 있는 항목 등)를 기억하거나 사용자의 검색 활동(특정 버튼 클릭, 로그인 또는 사용자가 방문한 페이지 기록 등)을 기록하는 신뢰할 수 있는 메커니즘으로 설계되었습니다. 몇 달 또는 몇 년 전으로 거슬러 올라갑니다).

따라서 기본적으로 상태를 전달해야 하는 경우 쿠키를 사용하세요.이는 정확히 동일한 이유로 설계되었습니다. HTTP이므로 설계상 REST와 호환됩니다. :)


더 나은 솔루션:

여러 요청이 포함된 워크플로를 수행하는 클라이언트에 대해 이야기하는 경우 일반적으로 프로토콜에 대해 이야기합니다.모든 형태의 프로토콜에는 B 단계를 수행하기 전에 A 단계를 수행하는 것과 같이 각 잠재적 단계에 대한 일련의 전제 조건이 함께 제공됩니다.

이는 당연하지만 클라이언트에 프로토콜을 노출하면 모든 것이 더 복잡해집니다.이를 방지하려면 현실 세계에서 복잡한 상호 작용과 일을 해야 할 때 무엇을 하는지 생각해보세요....우리는 에이전트를 사용합니다.

에이전트 비유를 사용하면 필요한 모든 단계를 수행할 수 있는 리소스를 제공하고 실제 할당/지침을 목록에 저장할 수 있습니다(따라서 에이전트 또는 '에이전시'에서 POST를 사용할 수 있음).

복잡한 예:

집을 구입:

귀하는 귀하의 신뢰성을 입증해야 하며(예: 경찰 기록 항목 제공), 재정 세부 사항을 확인해야 하며, 변호사와 자금을 보관하는 신뢰할 수 있는 제3자를 통해 실제 주택을 구입해야 하며, 해당 주택이 현재 귀하의 소유인지 확인하고 세금 기록 등에 구매 항목을 추가하십시오.(예를 들어, 일부 단계는 잘못되었을 수도 있습니다.)

이러한 단계는 완료하는 데 며칠이 걸릴 수 있으며, 일부는 병렬로 수행할 수도 있습니다.

이를 수행하려면 에이전트에게 다음과 같이 주택 구매 작업을 제공하면 됩니다.

POST: agency.com/ { task: "buy house", target:"link:toHouse", credibilities:"IamMe"}.

완료.대행사는 귀하가 이 작업의 상태를 보고 추적하는 데 사용할 수 있는 참조를 귀하에게 다시 보내며 나머지는 대행사의 대리인에 의해 자동으로 수행됩니다.

예를 들어 버그 추적기를 생각해 보세요.기본적으로 버그를 보고하고 버그 ID를 사용하여 무슨 일이 일어나고 있는지 확인할 수 있습니다.서비스를 사용하여 이 리소스의 변경 사항을 수신할 수도 있습니다.임무 완료.

나는이 경우이 상황에서 순수한 휴식 이론을 깨는 것이 완전히 받아 들여질 것이라고 생각한다. 어쨌든, 나는 당신이 그것을 요구하는 비즈니스 사례에서 의존적 객체를 만질 수 없다고 말하는 것이 실제로 없다고 생각합니다.

데이터베이스를 활용하여 사용자 정의 트랜잭션 관리자를 만들기 위해 뛰어 들기 위해 뛰어들 것입니다.

REST에서 서버 측 트랜잭션을 사용해서는 안됩니다.

나머지 금기 중 하나 :

무국적

클라이언트 - 서버 통신은 요청간에 서버에 저장되지 않아서 더욱 제한됩니다. 모든 클라이언트의 각 요청에는 요청을 서비스하는 데 필요한 모든 정보가 포함되어 있으며 모든 세션 상태는 고객에게 보유됩니다.

유일한 편안한 방법은 트랜잭션 REDO 로그를 작성하여 클라이언트 상태에 넣는 것입니다. 요청을 통해 클라이언트는 Redo Log를 보내고 서버는 트랜잭션을 재정의하고

  1. 트랜잭션을 되돌리지 만 새로운 트랜잭션 REDO 로그를 제공합니다 (한 단계 더 나아가)
  2. 또는 마지막으로 거래를 완료하십시오.

그러나 서버 측 트랜잭션을 지원하는 서버 세션 기반 기술을 사용하는 것이 더 간단 할 수 있습니다.

클라이언트에서 생성 된 고유 식별자를 사용하여 연결 히히 큐이 API에 의해 저장된 이중성을 암시하지 않도록하는 경우가 될 것입니다.

전송 개체와 함께 클라이언트 생성 안내 필드를 사용하고 동일한 안내서가 다시 재 삽입되지 않도록하는 것이 은행 이전 문제에 대한 간단한 솔루션이 될 것이라고 생각합니다.

다중 항공사 티켓 예약 또는 마이크로 아키텍처와 같은보다 복잡한 시나리오에 대해 모릅니다.

나는 그 주제에 관한 논문을 발견했다. RESTFul Services의 거래 원자력 처리.

간단한 경우 (분산 자원이 없음), 트랜잭션을 리소스로 간주 할 수 있으며,이를 생성하는 행위는 최종 목표를 달성합니다.

따라서 사이를 전송합니다 <url-base>/account/a 그리고 <url-base>/account/b, 당신은 다음을 게시 할 수 있습니다 <url-base>/transfer.

<transfer>
    <from><url-base>/account/a</from>
    <to><url-base>/account/b</to>
    <amount>50</amount>
</transfer>

이것은 새로운 전송 자원을 생성하고 전송의 새로운 URL을 반환합니다 (예 : <url-base>/transfer/256.

성공적인 게시물의 순간에, '실제'트랜잭션은 서버에서 수행되며 한 계정에서 제거되어 다른 계정에 추가됩니다.

그러나 이것은 분산 거래를 다루지 않습니다 (경우 'A'가 한 서비스 뒤의 한 은행에서 보유되고 있으며 다른 서비스 뒤에 다른 은행에서 'B'가 보유 된 경우). 분산 거래가 필요하지 않은 방식으로 운영 ".

URL/리소스에 황갈색을 포함시킬 수 있다고 생각합니다.

  1. ID를 얻기 위해 /거래 /거래 (예 : "1")
  2. PUT, GET, POST, 무엇이든]/1/계정/BOB
  3. PUT, GET, POST, 무엇이든]/1/계정/청구서
  4. ID 1으로 삭제 /트랜잭션 1

그냥 아이디어.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top