development

REST 거래?

big-blog 2020. 6. 21. 19:05
반응형

REST 거래?


REST에서 다음 유스 케이스를 어떻게 구현하는지 궁금합니다. 개념적 모델을 손상시키지 않으면서도 가능합니까?

단일 트랜잭션 범위 내에서 여러 리소스를 읽거나 업데이트하십시오. 예를 들어 Bob의 은행 계좌에서 John의 계좌로 $ 100를 이체하십시오.

내가 알 수있는 한, 이것을 구현하는 유일한 방법은 부정 행위입니다. John 또는 Bob과 연관된 자원에 POST하고 단일 트랜잭션을 사용하여 전체 조작을 수행 할 수 있습니다. 내가 아는 한, 이것은 실제로 개별 리소스에서 작동하는 대신 POST를 통해 RPC 호출을 터널링하기 때문에 REST 아키텍처를 손상시킵니다.


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


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

특히, 적절하게 좋은 방법은 다음과 같습니다. POST가 두 번 (중간에 일부 캐시가 들어 있기 때문에) 두 번 전송하면 안됩니다.

이를 위해 트랜잭션을 객체로 만듭니다. 여기에는 이미 알고있는 모든 데이터가 포함될 수 있으며 트랜잭션을 보류 상태로 만듭니다.

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의 GET조차도 현재 상태를 반환합니다. 특히, 두 번째 PUT은 첫 번째 PUT이 이미 적절한 상태에 있음을 감지하고이를 반환합니다. 또는 이미 "committed"상태에있는 후 "rolledback"상태로 전환하려고하면 오류 및 실제 커밋 된 트랜잭션 백.

단일 데이터베이스 또는 통합 된 트랜잭션 모니터가있는 데이터베이스와 통신하는 한이 메커니즘은 실제로 제대로 작동합니다. 트랜잭션에 대한 시간 제한을 추가로 도입 할 수도 있습니다. 원하는 경우 Expires 헤더를 사용하여 표현할 수도 있습니다.


REST 용어에서 자원은 CRUD (create / read / update / delete) 동사로 수행 할 수있는 명사입니다. "송금"동사가 없기 때문에 CRUD로 처리 할 수있는 "트랜잭션"리소스를 정의해야합니다. 다음은 HTTP + POX의 예입니다. 첫 번째 단계는 것입니다 CREATE (HTTP POST 방법) 새로운 거래를 :

POST /transaction

이것은 "1234"와 URL "/ transaction / 1234"에 따라 트랜잭션 ID를 반환합니다. 이 POST를 여러 번 실행하면 여러 ID로 동일한 트랜잭션이 생성되지 않고 "대기 중"상태가 발생하지 않습니다. 또한 POST가 항상 dem 등원 (REST 요구 사항) 일 수는 없으므로 일반적으로 POST의 데이터를 최소화하는 것이 좋습니다.

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

다음 으로 모든 데이터로 트랜잭션을 UPDATE (PUT HTTP 메소드)하여 암시 적으로 커밋합니다.

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

ID가 "1234"인 트랜잭션이 이전에 PUT 인 경우 서버는 오류 응답을 제공하고 그렇지 않으면 확인 응답 및 완료된 트랜잭션을 볼 수있는 URL을 제공합니다.

주의 : / account / john에서 "john"은 John의 고유 계정 번호 여야합니다.


훌륭한 질문은 REST는 주로 데이터베이스와 유사한 예제로 설명되며 무언가가 저장, 업데이트, 검색, 삭제됩니다. 서버가 어떤 방식으로 데이터를 처리해야하는 경우와 같은 몇 가지 예가 있습니다. Roy Fielding은 그의 논문에 어떤 것도 포함하지 않았다고 생각합니다.

그러나 그는 다음 상태로 링크가 이동하는 상태 머신으로서의 "표현 상태 이전"에 대해 이야기합니다. 이러한 방식으로 문서 (표현)는 서버가 수행하지 않고 클라이언트 상태를 추적합니다. 이러한 방식으로 클라이언트 상태는 없으며 현재 연결되어있는 상태 만 나타냅니다.

나는 이것에 대해 생각하고 있었고, 서버가 당신을 위해 무언가를 처리하게하고, 업로드 할 때 서버가 자동으로 관련 리소스를 생성하고 링크를 제공한다는 것이 합리적입니다. 자동으로 만들 필요는 없습니다. 링크를 알려줄 수 있으며, 링크를 만들 때만 따라야하는 경우에만 게으른 생성입니다. 또한 새로운 관련 리소스 를 만드는 링크를 제공하기 위해 관련 리소스는 동일한 URI를 갖지만 더 길어집니다 (접미사 추가). 예를 들면 다음과 같습니다.

  1. 모든 정보 가 포함 된 거래 개념을 업로드 ( POST )합니다 . 이것은 RPC 호출과 비슷하지만 실제로 "제안 된 트랜잭션 리소스"를 생성하고 있습니다. 예 : URI : /transaction결함으로 인해 각각 다른 URI를 가진 여러 리소스가 생성됩니다.
  2. 서버의 응답은 생성 된 리소스의 URI를 나타냅니다. 여기에는 새로운 "커밋 된 트랜잭션 리소스" 의 관련 리소스를 만들기위한 링크 ( URI )가 포함됩니다 . 다른 관련 리소스는 제안 된 거래를 삭제하는 링크입니다. 상태 머신의 상태는 클라이언트가 따를 수 있습니다. 논리적으로 이들은 클라이언트가 제공 한 정보를 넘어 서버에서 생성 된 리소스의 일부입니다. 예를 들어 URI를 : /transaction/1234/proposed,/transaction/1234/committed
  3. You POST to the link to create the "committed transaction resource", which creates that resource, changing the state of the server (the balances of the two accounts)**. By its nature, this resource can only be created once, and can't be updated. Therefore, glitches committing many transactions can't occur.
  4. You can GET those two resources, to see what their state is. Assuming that a POST can change other resources, the proposal would now be flagged as "committed" (or perhaps, not available at all).

This is similar to how webpages operate, with the final webpage saying "are you sure you want to do this?" That final webpage is itself a representation of the state of the transaction, which includes a link to go to the next state. Not just financial transactions; also (eg) preview then commit on wikipedia. I guess the distinction in REST is that each stage in the sequence of states has an explicit name (its URI).

In real-life transactions/sales, there are often different physical documents for different stages of a transaction (proposal, purchase order, receipt etc). Even more for buying a house, with settlement etc.

OTOH This feels like playing with semantics to me; I'm uncomfortable with the nominalization of converting verbs into nouns to make it RESTful, "because it uses nouns (URIs) instead of verbs (RPC calls)". i.e. the noun "committed transaction resource" instead of the verb "commit this transaction". I guess one advantage of nominalization is you can refer to the resource by name, instead of needing to specify it in some other way (such as maintaining session state, so you know what "this" transaction is...)

But the important question is: What are the benefits of this approach? i.e. In what way is this REST-style better than RPC-style? Is a technique that's great for webpages also helpful for processing information, beyond store/retrieve/update/delete? I think that the key benefit of REST is scalability; one aspect of that is not needing to maintain client state explicitly (but making it implicit in the URI of the resource, and the next states as links in its representation). In that sense it helps. Perhaps this helps in layering/pipelining too? OTOH only the one user will look at their specific transaction, so there's no advantage in caching it so others can read it, the big win for http.


If you stand back to summarize the discussion here, it's pretty clear that REST is not appropriate for many APIs, particularly when the client-server interaction is inherently stateful, as it is with non-trivial transactions. Why jump through all the hoops suggested, for client and server both, in order to pedantically follow some principle that doesn't fit the problem? A better principle is to give the client the easiest, most natural, productive way to compose with the application.

In summary, if you're really doing a lot of transactions (types, not instances) in your application, you really shouldn't be creating a RESTful API.


I've drifted away from this topic for 10 years. Coming back, I can't believe the religion masquerading as science that you wade into when you google rest+reliable. The confusion is mythic.

I would divide this broad question into three:

  • Downstream services. Any web service you develop will have downstream services that you use, and whose transaction syntax you have no choice but to follow. You should try and hide all this from users of your service, and make sure all parts of your operation succeed or fail as a group, then return this result to your users.
  • Your services. Clients want unambiguous outcomes to web-service calls, and the usual REST pattern of making POST, PUT or DELETE requests directly on substantive resources strikes me as a poor, and easily improved, way of providing this certainty. If you care about reliability, you need to identify action requests. This id can be a guid created on the client, or a seed value from a relational DB on the server, it doesn't matter. For server generated ID's, use a 'preflight' request-response to exchange the id of the action. If this request fails or half succeeds, no problem, the client just repeats the request. Unused ids do no harm.

    This is important because it lets all subsequent requests be fully idempotent, in the sense that if they are repeated n times they return the same result and cause nothing further to happen. The server stores all responses against the action id, and if it sees the same request, it replays the same response. A fuller treatment of the pattern is in this google doc. The doc suggests an implementation that, I believe(!), broadly follows REST principals. Experts will surely tell me how it violates others. This pattern can be usefully employed for any unsafe call to your web-service, whether or not there are downstream transactions involved.
  • Integration of your service into "transactions" controlled by upstream services. In the context of web-services, full ACID transactions are considered as usually not worth the effort, but you can greatly help consumers of your service by providing cancel and/or confirm links in your confirmation response, and thus achieve transactions by compensation.

Your requirement is a fundamental one. Don't let people tell you your solution is not kosher. Judge their architectures in the light of how well, and how simply, they address your problem.


You'd have to roll your own "transaction id" type of tx management. So it would be 4 calls:

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)

You'd have to handle the storing of the actions in a DB (if load balanced) or in memory or such, then handling commit, rollback, timeout.

Not really a RESTful day in the park.


First of all transferring money is nothing that you can not do in a single resource call. The action you want to do is sending money. So you add a money transfer resource to the account of the sender.

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

Done. You do not need to know that this is a transaction that must be atomic etc. You just transfer money aka. send money from A to B.


But for the rare cases here a general solution:

If you want to do something very complex involving many resources in a defined context with a lot of restrictions that actually cross the what vs. why barrier (business vs. implementation knowledge) you need to transfer state. Since REST should be stateless you as a client need to transfer the state around.

If you transfer state you need to hide the information inside from the client. The client should not know internal information only needed by the implementation but does not carry information relevant in terms of business. If those information have no business value the state should be encrypted and a metaphor like token, pass or something need to be used.

This way one can pass internal state around and using encryption and signing the system can be still be secure and sound. Finding the right abstraction for the client why he passes around state information is something that is up to the design and architecture.


The real solution:

Remember REST is talking HTTP and HTTP comes with the concept of using cookies. Those cookies are often forgotten when people talk about REST API and workflows and interactions spanning multiple resources or requests.

Remember what is written in the Wikipedia about HTTP cookies:

Cookies were designed to be a reliable mechanism for websites to remember stateful information (such as items in a shopping cart) or to record the user's browsing activity (including clicking particular buttons, logging in, or recording which pages were visited by the user as far back as months or years ago).

So basically if you need to pass on state, use a cookie. It is designed for exactly the very same reason, it is HTTP and therefore it is compatible to REST by design :).


The better solution:

If you talk about a client performing a workflow involving multiple requests you usually talk about protocol. Every form of protocol comes with a set of preconditions for each potential step like perform step A before you can do B.

This is natural but exposing protocol to clients makes everything more complex. In order to avoid it just think what we do when we have to do complex interactions and things in the real world... . We use an Agent.

Using the Agent metaphor you can provide a resource that can perform all necessary steps for you and store the actual assignment / instructions it is acting upon in its list (so we can use POST on the agent or an 'agency').

A complex example:

Buying a house:

You need to prove your credibility (like providing your police record entries), you need to ensure financial details, you need to buy the actual house using a lawyer and a trusted third party storing the funds, verify that the house now belongs to you and add the buying stuff to your tax records etc. (just as an example, some steps may be wrong or whatever).

These steps might take several days to be completed, some can be done in parallel etc.

In order to do this, you just give the agent the task buy house like:

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

Done. The agency sends you back a reference to you that you can use to see and track the status of this job and the rest is done automatically by the agents of the agency.

Think about a bug tracker for instance. Basically you report the bug and can use the bug id to check whats going on. You can even use a service to listen to changes of this resource. Mission Done.


I think that in this case it is totally acceptable to break the pure theory of REST in this situation. In any case, I don't think there is anything actually in REST that says you can't touch dependent objects in business cases that require it.

I really think it's not worth the extra hoops you would jump through to create a custom transaction manager, when you could just leverage the database to do it.


You must not use server side transactions in REST.

One of the REST contraints:

Stateless

The client–server communication is further constrained by no client context being stored on the server between requests. Each request from any client contains all of the information necessary to service the request, and any session state is held in the client.

The only RESTful way is to create a transaction redo log and put it into the client state. With the requests the client sends the redo log and the server redoes the transaction and

  1. rolls the transaction back but provides a new transaction redo log (one step further)
  2. or finally complete the transaction.

But maybe it's simpler to use a server session based technology which supports server side transactions.


I believe that would be the case of using a unique identifier generated on the client to ensure that the connection hiccup not imply in an duplicity saved by the API.

I think using a client generated GUID field along with the transfer object and ensuring that the same GUID was not reinserted again would be a simpler solution to the bank transfer matter.

Do not know about more complex scenarios, such as multiple airline ticket booking or micro architectures.

I found a paper about the subject, relating the experiences of dealing with the transaction atomicity in RESTful services.


In the simple case (without distributed resources), you could consider the transaction as a resource, where the act of creating it attains the end objective.

So, to transfer between <url-base>/account/a and <url-base>/account/b, you could post the following to <url-base>/transfer.

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

This would create a new transfer resource and return the new url of the transfer - for example <url-base>/transfer/256.

At the moment of successful post, then, the 'real' transaction is carried out on the server, and the amount removed from one account and added to another.

This, however, doesn't cover a distributed transaction (if, say 'a' is held at one bank behind one service, and 'b' is held at another bank behind another service) - other than to say "try to phrase all operations in ways that don't require distributed transactions".


I guess you could include the TAN in the URL/resource:

  1. PUT /transaction to get the ID (e.g. "1")
  2. [PUT, GET, POST, whatever] /1/account/bob
  3. [PUT, GET, POST, whatever] /1/account/bill
  4. DELETE /transaction with ID 1

Just an idea.

참고URL : https://stackoverflow.com/questions/147207/transactions-in-rest

반응형