2014-11-27 1 views
0

모든 것이 단일 엔티티를 기반으로 구축 된 RESTful API에서 트랜잭션 안전성을 설정하는 방법을 알고 싶습니다.RESTful API의 트랜잭션 안전

데이터베이스 모델 :

  • 송장
  • 항목 브라우저에서

사용자 수행 단계 :

  1. 변경 주문 번호.
  2. 항목을 추가하십시오.
  3. 항목을 제거하십시오.
  4. 항목을 편집하십시오.

만든 요청 :

  1. PATCH/PUT 송장 데이터/주문 번호.
  2. POST 항목입니다.
  3. DELETE 항목입니다.
  4. PATCH/PUT 항목입니다.

문제

오류 위의 요청의 발생 후하면 더 전화를 엉망으로 데이터 무결성을 수 있습니다. 또한 이전 요청을 취소해야합니다. 예 : 품목을 삭제하는 데 실패하면 단계 1과 2를 전체 인보이스가 이전과 동일하게 되 감기 위해 다시 감겨 야합니다.

발생할 수있는 또 다른 문제는 브라우저 충돌, 인터넷 연결 끊김, 서버 오류 등입니다.

데이터 무결성과 안전성을 유지하기 위해 어떤 종류의 트랜잭션에서 특정 동작이 실행되도록하려면 어떻게해야합니까?

+2

REST 호출 비 저장해야 응답의 본문에 새로 생성 된 ID를 가진 당신에게 청구서의 항목의 상태를 반환 할 수 있습니다. 향후 요청이 실패 할 경우 이전 요청을 실행 취소하면 안됩니다. 이 경우 동일한 요청에서 상태 변경을 수행해야하며 함께 실패해야합니다. 각 HTTP 요청은 성공한 경우 유효한 상태로 남아 있어야합니다. 예를 들어 항목을 삭제하려면 대체 항목을 추가해야합니다. 그러면 항목이 분리되어서는 안됩니다. –

+1

다른 방법으로 말하자면 단일 HTTP 요청에는 단일 트랜잭션이 포함되어야합니다. –

+0

고마워요, 그건 많은 의미가 있습니다. 당신의 대체 예제는 나에게 궁금해하게한다 : 당신이 그것을하는 방법에 대한 구체적인 예를들 수 있습니까? DELETE와 POST라는 두 개의 분리 된 요청 만 상상할 수 있습니다. – stschindler

답변

4

그래서 REST로 기억해야 할 것은 "상태 전송"비트입니다. 리소스를 업데이트하는 데 필요한 단계를 서버에 알려주지 않고 이미 클라이언트에서 리소스를 업데이트했기 때문에 리소스가 업데이트되어야하는 상태를 서버에 알리고 이제이 새로운 상태를 서버로 전송하기 만하면됩니다. 섬기는 사람.

그래서 서버

{ 
    invoice_id: 123, 
    invoice_description: "Some invoice", 
    invoice_items: [ 
     {item_id: 10, item_desc: "Large item", order_amount: 34}, 
     {item_id: 11, item_desc: "Small item", order_amount: 400} 
    ] 
} 

에 JSON과 같은 그리고 사용자가 하나의 원자 트랜잭션으로 그 송장을 편집하고자하는 서버에 송장 항목을 말한다. 먼저 서버에서 인보이스를받습니다. 이것은 본질적으로 사용자는 다음 어쨌든 그들이 원하는 송장을 편집

GET /invoices/123 

"나에게 송장의 현재 상태를주십시오"라고. 그들은 큰 항목의 수가 34가 아닌 40이어야한다고 결정합니다. 그들은 작은 항목을 완전히 삭제하기로 결정합니다. 그리고 마침내 그들은 다른 "여분의"항목을 송장에 추가하기로 결정합니다.사용자가 송장을 편집 한 후 클라이언트는 다음 송장

{ 
    invoice_id: 123, 
    invoice_description: "Some invoice", 
    invoice_items: [ 
     {item_id: 10, item_desc: "Large item", order_amount: 40}, 
     {item_id: 30, item_desc: "Extra small item", order_amount: 5} 
    ] 
} 

그래서 클라이언트가 서버에 다른 상태에서 송장을 가지고에게 있습니다. 이제 사용자는이를 저장할 서버로 다시 보내려고하므로 송장의 새 상태를 서버에 보냅니다.

PUT /invoices/123 

"본 리소스의 새 상태가 여기에 있습니다."

이제 서버의 유효성 검사가 얼마나 원할한지에 따라 인보이스가있는 그대로 새 상태를 받아 들일 수 있습니다. 또는 인보이스가있는 경우 각 변경 사항에 대해 전체 유효성 검사를 수행 할 수 있습니다. 당신이 원하는 것은 당신에게 달린 것입니다.

적어도 사용자가 클라이언트에서이 송장 사본을 편집하는 동안 다른 클라이언트가 서버에 업데이트 된 송장을 PUT하지 않았는지 확인하고 싶습니다. HTTP 요청의 다양한 헤더 (예 : etag header http://en.wikipedia.org/wiki/HTTP_ETag)를 확인하여이를 수행 할 수 있습니다.

서버가 어떤 이유로이 업데이트가 유효하지 않다고 판단하면 단순히 전체 PUT 요청에 실패합니다. 이것은 HTTP에서 트랜잭션을 제공합니다. 요구 사항은 작동하거나 실패해야합니다. 실패 할 경우 자원이 실패한 요청에 의해 영향을받지 않았는지 확인하는 것은 서버의 책임입니다. 서버의 이식 관점에서 새 JSON에 대한 유효성 검사를 한 다음 DB 트랜잭션 내에서 새 데이터를 데이터베이스에 저장하려고 시도 할 것입니다. 실패한 경우 데이터베이스는 원래 상태로 유지되고 PUT이 작동하지 않는다고 사용자에게 알립니다.

요청이 실패하면 사용자에게 PUT이 실패한 이유를 설명하는 HTTP 상태 코드와 응답이 반환되어야합니다. 이는 사용자가 변경 사항을 생각하는 동안 누군가 다른 사람이 인보이스를 편집했기 때문일 수 있습니다. 사용자가 인보이스를 유효하지 않은 상태로 만들려고 시도하는 중일 수 있습니다 (사용자가 항목이없는 송장을 PUT하려고했는데 회사의 비즈니스 논리가 깨졌습니다).

할 수 있습니다 물론

GET /invoices/123/items/10 

송장 번호 123에서 당신에게 항목 ID 10 줄 것이라고하지만 이렇게하면 당신은 예를 들어, 송장에 개별 항목의 편집을 가능하게하는 URI 체계를 개발 서로 독립적으로 이러한 리소스를 편집 할 수 있습니다. 삭제 명령을 보내서 항목 10을 삭제하면

DELETE /invoice/123/items/10 

해당 작업이 독립적 인 트랜잭션이어야합니다. 다른 요청이 이것에 의존하는 경우 단일 요청으로 인보이스 자체를 업데이트하여 위에 설명 된대로 수행해야합니다. 단일 HTTP 요청을 통해 리소스를 잘못된 상태로 만들거나 리소스를 유효한 상태로 만들기 위해 여러 HTTP 요청이 필요하지 않게해야합니다 (따라서 HTTP 요청 문자열이 필요하지 않음)) 유효하기 위해

+0

큰 답변, 고마워요. 확실하지 않은 점은 한 가지뿐입니다. 사용자가 ID가없는 새 항목이있는 업데이트 된 인보이스를 다시 보내면 항상 서버에서 생성되기 때문에 어떨까요? AFAIK는 적어도'PUT'은 여기에 맞지 않지만'POST'는 대신 사용합니다. 그러나 그것이 어떻게 완료 되었습니까? – stschindler

+0

또 다른 긴 응답이므로 다른 대답으로 넣으십시오 :-) –

1

멋진 대답을하는 데 도움이

희망, 덕분에 많은 일을합니다. 확실하지 않은 부분이 하나 있습니다. 사용자가 에 아직 ID가없는 새 항목이있는 업데이트 된 인보이스를 다시 보내면 서버에서 항상 생성되기 때문에 어떻게됩니까? AFAIK는 적어도 여기서는 정확하지 않지만 POST는 올바르지 않습니다. 그러나 어떻게 완료 되었습니까?

예 PUT이 잘못되었습니다. PUT은 멱등수 여야합니다. 즉, 자원에 대해 여러 PUT 요청을 할 수 있어야하며, 동일한 요청 인 경우 최종 결과는 모두 동일해야합니다.

다시 상태 전송을 다시 생각하면 같은 상태의 PUT을 여러 번 수행하면 해당 상태의 리소스로 끝나야합니다. 20 번 PNG 파일을 리소스에 업로드하면 PNG 파일은 방금 한 번 업로드 한 것과 같은 PNG 파일이어야합니다.

그래서 서버에 PUT 할 때 명백한 것이 있으면 안됩니다. 당신이 본질적으로 서버에 말하는 아이템의 ID를 빠뜨린 경우 "이 상태 업데이트의 일환으로 아이템을 생성하십시오". 물론 10 번 실행하면 10 개의 새 항목이 만들어지며 인보이스가 같은 상태가되지 않습니다.

그래서 여기서는 POST가 더 좋을 것입니다. 항목을 업데이트하는 경우 명확하게하기 위해 POST를 "항목"끝점으로 지정하는 것이 좋습니다.

POST /invoices/123/items 

[ 
    {item_id: 10, item_desc: "Large item", order_amount: 40}, 
    {item_desc: "Extra small item", order_amount: 5} 
] 

서버는 다음

[ 
    {item_id: 10, item_desc: "Large item", order_amount: 40}, 
    {item_id: 30, item_desc: "Extra small item", order_amount: 5} 
] 
+1

또한 인보이스 항목이 클라이언트가 조회 할 수있는 리소스 인 경우 빠른 후속 조치를 통해 ID 사용을 중단하고 URIs.So 대신 item_id : 10 당신은 item_uri :/invoice/123/items/10을 원할 것입니다. –

+0

다시 한번 감사드립니다. :) 마지막 질문 하나 (잘하면) : 원래 POST 요청에 항목을 남겨 두었을 때 데이터베이스에 놓는 것이 맞습니까? (항목은 청구서와 100 % 결합되어 있으므로 인보이스에 넣지 않으려면 보통 더 이상 필요하지 않습니다.) 기본적으로 내가 POST 한 것은 청구서의 최종 상태입니다. – stschindler

+0

POST 사양은 이러한 세부 정보 수준을 의도적으로 정의하지 않으므로 REST/HTTP 관점에서 이에 대한 '올바른'대답이 없습니다. 클라이언트와 서버간에 정의한 비즈니스 논리는 무엇을 할 것입니다. 그러나 가장 합리적인 소리가 들리면 항목을 삭제하면 완전히 삭제 된 것으로 간주됩니다. –

관련 문제