REST API 개념


10

REST API 디자인에 대해 누군가가 밝힐 수있는 세 가지 질문이 있습니다. 나는 몇 시간 동안 끊임없이 검색했지만 어디에서나 내 질문에 대한 답변을 찾지 못했습니다 (어쩌면 무엇을 검색 해야할지 모르겠습니까?).

질문 1

첫 번째 질문은 액션 / RPC와 관련이 있습니다. 나는 잠시 동안 REST API를 개발해 왔으며 컬렉션과 리소스 측면에서 생각하는 데 익숙합니다. 그러나 패러다임이 적용되지 않는 것처럼 보이는 몇 가지 사례를 발견했으며 REST 패러다임과 조정하는 방법이 있는지 궁금합니다.

특히 리소스를 수정하면 이메일이 생성되는 경우가 있습니다. 그러나 나중에 사용자는 이전에 보낸 전자 메일을 다시 보내겠다고 구체적으로 표시 할 수 있습니다. 이메일을 재전송 할 때 리소스가 수정되지 않습니다. 상태가 변경되지 않습니다. 단순히 발생해야하는 조치입니다. 작업은 특정 리소스 유형에 연결됩니다.

일종의 조치 호출을 자원 URI와 혼합하는 것이 적절 /collection/123?action=resendEmail합니까 (예 :) ? 조치를 지정하고 자원 ID를 전달하는 것이 더 낫습니까 (예 :) /collection/resendEmail?id=123? 이것이 잘못된 길입니까? 전통적으로 (적어도 HTTP를 사용하여) 수행되는 작업은 요청 방법 (GET, POST, PUT, DELETE)이지만 실제로는 리소스에 대한 사용자 지정 작업을 허용하지 않습니다.

질문 2

URL의 querystring 부분을 사용하여 컬렉션을 쿼리 할 때 반환되는 리소스 세트를 필터링합니다 (예 :) /collection?someField=someval. 그런 다음 API 컨트롤러 내에서 해당 필드 및 값과 비교할 종류를 결정합니다. 나는 이것이 실제로 작동하지 않는 것을 발견했다. API 사용자가 수행하려는 비교 유형을 지정할 수있는 방법이 필요합니다.

나는했습니다 지금까지 가지고 올 가장 좋은 아이디어는 API 사용자가 예를 들어, 필드 이름에 부속 (로 지정할 수 있도록하는 것입니다 /collection?someField:gte=someval- 그것은 어디에 자원을 반환해야 함을 나타 내기 위해 someField보다 큰거나 같음 (> =) 어떤 someval이다 이것이 좋은 생각입니까, 나쁜 생각입니까? 그렇다면 왜입니까? 사용자가 주어진 필드와 값으로 수행 할 비교 유형을 지정할 수있는 더 좋은 방법이 있습니까?

질문 3

나는 종종 s /person/123/dogs를 얻는 것과 같은 URI를 본다 . 나는 일반적으로 URI를 생성함으로써 실제로 특정 ID로 필터링 된 컬렉션에 액세스한다는 것을 알기 때문에 일반적으로 이와 같은 것을 피 했습니다 . 와 동일합니다 . REST URI가 두 레벨 이상 ( ) 이상인 좋은 이유가 있습니까?persondogsdogsperson/dogs?person=123/collection/resource_id


10
세 가지 질문이 있습니다. 별도로 게시하지 않겠습니까?
anaximander

3
이것을 3 가지 질문으로 나누는 것이 좋습니다. 시청자는 하나의 질문에 모두 답변 할 수 있지만 모든 질문에 대한 답변은 아닙니다.

2
나는 그들이 모두 관련이 있다고 생각합니다. 제목은 약간 높지만이 질문은 많은 사람들에게 도움이되고 SE 검색 중에 쉽게 찾을 수 있습니다. 충분한 투표와 내용이 추가되면이 질문은 Community Wiki가되어야합니다. 이 물건을 연구하는 데 몇 주가 걸렸습니다.
Andrew T Finnell

1
IDK는 별도로 게시하는 것이 좋을 것입니다. 그러나 @AndrewFinnell이 언급했듯이, 내가 가진 가장 어려운 REST 관련 질문이었고 다른 사람들이 답을 찾을 수 있기 때문에 질문을 함께 유지하는 것이 좋습니다. 함께.
저스틴 워 켄틴

답변:


11

일종의 조치 호출을 자원 URI와 혼합하는 것이 적절 /collection/123?action=resendEmail합니까 (예 :) ? 조치를 지정하고 자원 ID를 전달하는 것이 더 낫습니까 (예 :) /collection/resendEmail?id=123? 이것이 잘못된 길입니까? 전통적으로 (적어도 HTTP를 사용하여) 수행되는 작업은 요청 방법 (GET, POST, PUT, DELETE)이지만 실제로는 리소스에 대한 사용자 지정 작업을 허용하지 않습니다.

차라리 보낼 이메일을 나타내는 리소스 모음을 사용하여 다른 방식으로 모델링하고 싶습니다. 보내는 과정은 서비스 내부에서 처리되며 해당 시점에 해당 리소스가 제거됩니다. 또는 사용자가 리소스를 조기에 삭제하여 전송 요청 취소가 발생할 수 있습니다.

무엇을하든 리소스 이름에 동사를 넣지 마십시오! 그것은 명사입니다 (그리고 쿼리 부분은 형용사 집합입니다). 명사 동사 이상한 REST!

URL의 querystring 부분을 사용하여 컬렉션을 쿼리 할 때 반환되는 리소스 세트를 필터링합니다 (예 :) /collection?someField=someval. 그런 다음 API 컨트롤러 내에서 해당 필드 및 값과 비교할 종류를 결정합니다. 나는 이것이 실제로 작동하지 않는 것을 발견했다. API 사용자가 수행하려는 비교 유형을 지정할 수있는 방법이 필요합니다.

지금까지 생각해 낸 가장 좋은 아이디어는 API 사용자가 필드 이름 /collection?someField:gte=someval에 추가 물로 지정할 수 있도록하는 것입니다 (예 : someField가 ( >=) 이상인 리소스를 반환해야 함을 나타냅니다 ) someval. 이것이 좋은 생각입니까, 나쁜 생각입니까? 그렇다면 왜입니까? 사용자가 주어진 필드와 값으로 수행 할 비교 유형을 지정할 수있는 더 좋은 방법이 있습니까?

차라리 일반 필터 절을 지정하고 컬렉션의 내용을 가져 오는 요청에 대해 선택적 쿼리 매개 변수로 사용하고 싶습니다. 그런 다음 클라이언트는 원하는 방식으로 반환 된 세트를 제한하는 방법을 정확하게 지정할 수 있습니다. 또한 필터 / 쿼리 언어의 검색 가능성에 대해 약간 걱정할 것입니다. 부자가 많을수록 임의의 클라이언트가 발견하기가 더 어려워집니다. 적어도 이론적으로 그 발견 가능성 문제를 다루는 다른 접근법은 컬렉션의 제한 하위 리소스를 만드는 것을 허용하는 것이며, 클라이언트는 컬렉션 리소스에 대한 제한을 설명하는 문서를 POST하여 얻을 수 있습니다. 그것은 여전히 ​​약간의 남용이지만, 적어도 그것은 당신이 분명히 발견 할 수있는 것입니다!

이러한 종류의 발견 가능성은 REST에서 가장 강하지 않은 것 중 하나입니다.

나는 종종 /person/123/dogs사람들에게 개를 얻는 것과 같은 URI를 봅니다 . 나는 결국 URI를 생성함으로써 실제로 특정 개인 ID로 필터링 된 개 컬렉션에 액세스하고 있다고 생각하기 때문에 일반적으로 이와 같은 것을 피했습니다. 와 동일합니다 /dogs?person=123. REST URI가 두 레벨 이상 ( /collection/resource_id) 이상인 좋은 이유가 있습니까?

중첩 컬렉션이 외부 컬렉션 멤버 엔터티의 하위 기능인 경우 하위 컬렉션으로 구성하는 것이 합리적입니다. “하위 기능”이란 UML 구성 관계와 같은 것을 의미합니다. 외부 자원을 파괴하는 것은 내부 콜렉션을 파괴하는 것을 의미합니다.

다른 유형의 콜렉션은 HTTP 경로 재 지정으로 모델링 될 수 있습니다. 따라서으로 /person/123/dogs리디렉션되는 307을 수행하여 실제로 응답 할 수 있습니다 /dogs?person=123. 이 경우 컬렉션은 실제로 UML 구성이 아니라 UML 집계입니다. 차이점이 중요합니다. 중요하다!


2
전반적으로 확실한 포인트가 있습니다. 그러나 resendEmail컬렉션을 만들고 게시 하여 작업을 처리 할 수는 있지만 자연스럽지 않은 것 같습니다. 실제로 이메일을 다시 보낼 때 (필요하지 않은) 데이터베이스에 아무것도 저장하지 않습니다. 리소스는 수정되지 않으므로 단순히 성공하거나 실패한 작업입니다. 호출 수명을 넘어 존재하는 리소스 ID를 반환 할 수 없어 RESTful 대신 이러한 구현을 해킹했습니다. 단순히 CRUD 작업이 아닙니다.
저스틴 Warkentin

3

대기업이 REST API를 디자인하는 것을 본 모든 방법을 기반으로 REST를 올바르게 사용하는 방법에 대해 약간 혼란스러워하는 것은 이해할 만합니다.

REST가 자원 콜렉션 시스템이라는 점이 맞습니다. 그것은 대표 국가 이전을 의미합니다. 당신이 나에게 묻는다면 큰 정의가 아닙니다. 그러나 주요 개념은 4 개의 HTTP 동사이며 상태 비 저장입니다.

주의해야 할 중요한 부분은 REST가있는 4 개의 동사 만 있다는 것입니다. 이들은 GET, POST, PUT 및 DELETE입니다. 귀하의 resend예는 REST에 새로운 동사를 추가하는 것입니다. 이것은 붉은 깃발이어야합니다.

질문 1

REST API 호출자는 PUT콜렉션에서 수행을 수행하면 이메일이 생성됨 을 알 필요가 없다는 것을 인식해야합니다 . 그것은 나에게 새는 냄새가 난다. 그들이 알 수있는 것은 PUT나중에 수행 할 수있는 추가 작업 을 수행 할 수 있다는 것입니다 . 그들은 GET최근에 생성 된 리소스 를 수행함으로써 이것을 알 것 입니다. 그러면 GET리소스와 Task연결된 모든 리소스 ID 가 반환 됩니다. 그런 다음 나중에 해당 작업을 쿼리하여 상태를 확인하고 새 작업을 제출할 수도 Task있습니다.

몇 가지 옵션이 있습니다.

REST-태스크 자원 기반 접근법

tasks시스템에 특정 작업을 제출하여 작업을 수행 할 수 있는 리소스를 만듭니다 . 그런 다음 반환 된 GET작업을 기반으로 작업 ID의 상태를 확인할 수 있습니다.

또는 SOAP over HTTP웹 서비스를 혼합 하여 아키텍처에 RPC를 추가 할 수 있습니다.

특정 리소스에 대한 모든 작업 쿼리

GET http://server/api/myCollection/123/tasks

{ "tasks" :
    [ { "22333" : "http://server/api/tasks/223333" } ] 
}

작업 리소스 예

PUT http://server/api/tasks

{ 
    "type" : "send-email" , 
    "parameters" : 
    { 
         "collection-type" : "foo" , 
         "collection-id" : "123" 
    } 
}

==> 작업 ID를 반환

223334

GET http://server/api/tasks/223334

{ 
    "status" : "complete" , 
    "date" : "whenever" 
}

REST- POST를 사용하여 조치 트리거

언제든지 POST리소스에 추가 데이터를 추가 할 수 있습니다 . 제 생각에는 이것이 REST 정신을 위반하지만 여전히 준수 할 것입니다.

다음과 비슷한 POST를 수행 할 수 있습니다.

POST http://server/api/collection/123

{ "action" : "send-email" }

추가 데이터로 콜렉션에서 자원 123을 업데이트합니다. 추가 데이터는 본질적으로 백엔드에게 해당 리소스에 대한 전자 메일을 보내도록 지시하는 작업입니다.

내가 가진 문제 GET는 리소스 에이 업데이트 된 데이터를 반환 한다는 것입니다. 그러나 이것은 요구 사항을 해결하고 여전히 RESTful입니다.

SOAP-REST에서 얻은 자원을 승인하는 웹 서비스

REST API에서 이전 자원 ID를 기반으로 이메일을 보낼 수있는 새 WebService를 작성하십시오. 원래 질문은 REST에 관한 것이기 때문에 SOAP에 대해 자세히 설명하지 않겠습니다.이 두 개념 / 기술은 Apples 및 Oranges와 비교할 수 없습니다 .

질문 2

여기에는 몇 가지 옵션이 있습니다.

REST API를 게시하는 많은 대기업 search이 실제로 리소스를 반환하기 위해 쿼리 매개 변수를 전달하는 방법 인 컬렉션을 노출시키는 것으로 보입니다 .

GET http://server/api/search?q="type = myCollection & someField >= someval"

다음과 같은 정규화 된 REST 자원 콜렉션을 리턴합니다.

{
    "results" : 
       { [ 
             "location" : "http://server/api/myCollection/1",
             "location" : "http://server/api/myCollection/9",
             "location" : "http://server/api/myCollection/56"
         ]
       }
}

또는 MVEL 과 같은 것을 쿼리 매개 변수로 허용 할 수 있습니다 .

질문 3

쿼리 매개 변수를 사용하여 다른 리소스를 백업하고 쿼리하는 것보다 하위 수준을 선호합니다. 나는 어떤 식 으로든 규칙이 있다고 생각하지 않습니다. 두 가지 방법을 모두 구현하고 발신자가 시스템에 처음 들어간 방법에 따라 더 적합한 방법을 결정할 수 있습니다.

노트

나는 다른 사람들의 가독성 의견에 동의하지 않습니다. 일부 사람들은 REST가 여전히 인간의 소비가 아니라고 생각할 수도 있습니다. 기계 소 비용입니다. 내 트윗을 보려면 Twitter의 일반 웹 사이트를 사용합니다. API로 REST GET을 수행하지 않습니다. 트윗으로 프로그래밍 방식으로 무언가를하고 싶다면 REST API를 사용합니다. 예, API는 이해할 수 있어야하지만 gte그렇게 나쁘지는 않습니다. 직관적이지 않습니다.

REST의 또 다른 주요 사항은 API의 특정 시점에서 시작하여 미리 다른 리소스의 정확한 URL을 알지 않고도 다른 모든 관련 리소스로 이동할 수 있어야한다는 것입니다. GETREST 에서 동사 의 결과는 참조하는 자원의 전체 REST URL을 리턴해야합니다. 따라서 Person개체 의 ID를 반환하는 쿼리 대신 과 같은 정규화 된 URL을 반환합니다 http://server/api/people/13. 그러면 URL이 변경된 경우에도 항상 프로그래밍 방식으로 결과를 탐색 할 수 있습니다.

의견에 대한 답변

실제로는 자원을 생성, 읽기, 업데이트 또는 삭제 (CRUD)하지 않는 일이 실제로 발생합니다.

자원에 대한 추가 조치를 수행 할 수 있습니다. 일반적인 관계형 데이터베이스는 저장 프로 시저 개념을 지원합니다. 이들은 일련의 데이터에서 실행될 수있는 추가 명령입니다. REST에는 본질적으로 해당 개념이 없습니다. 그리고 그럴 이유가 없습니다. 이러한 유형의 작업은 RPC 또는 SOAP 웹 서비스에 적합합니다.

이것이 REST API에서 볼 수있는 일반적인 문제입니다. 개발자는 REST를 둘러싼 개념적 제한을 좋아하지 않기 때문에 원하는대로 수행 할 수 있습니다. 그래도 RESTful 서비스가 아닙니다. 본질적으로 이러한 URL GET은 의사 -REST와 유사한 서블릿에 대한 호출이됩니다.

몇 가지 옵션이 있습니다.

  • 작업 리소스 생성
  • POST자원에 추가 데이터를 지원 하여 조치 수행
  • SOAP 웹 서비스를 통해 추가 명령을 추가하십시오.

쿼리 매개 변수를 사용하여 전자 메일을 다시 보내는 데 사용할 HTTP 동사는 무엇입니까?

  • GET-이메일을 다시 보내고 리소스 데이터를 반환합니까? 시스템이 해당 URL을 캐시하고 해당 자원의 고유 URL처럼 처리하면 어떻게됩니까? 그들이 URL을 칠 때마다 이메일을 다시 보냅니다.
  • POST -실제로 새로운 데이터를 리소스로 보내지 않고 추가 쿼리 매개 변수 만 사용했습니다.

주어진 모든 요구 사항에 따라 POST 데이터 POST를 사용하여 리소스를 수행 action field하면 문제가 해결됩니다.


3
HTTP를 통해 구현 된 REST가 4 가지 동사를 제공하지만 해당 동사가 그 동사의 끝이어야한다고 확신하지는 않습니다. 실제로는 자원을 생성, 읽기, 업데이트 또는 삭제 (CRUD)하지 않는 일이 실제로 발생합니다. 이메일을 재전송하는 것도 그 중 하나입니다. 데이터베이스에 아무것도 저장하거나 수정할 필요가 없습니다. 그것은 단순히 성공하거나 실패하는 행동입니다.
Justin Warkentin

@JustinWarkentin 나는 당신의 요구가 무엇인지 이해합니다. 그러나 이것이 REST가 아닌 것을 만들지는 않습니다. URL에 새 동사를 추가하는 것은 REST 아키텍처에 대한 것입니다. RESTful 한 다른 대안을 제공하기 위해 답변을 업데이트하겠습니다.
Andrew T Finnell

@JustinWarkentin 내 답변에서 'REST-POST를 사용하여 작업 트리거'를 확인하십시오.
Andrew T Finnell

0

질문 1 : 일종의 액션 호출을 리소스 URI와 혼합하는 것이 적절합니까? [또는] 액션을 지정하고 리소스 ID를 전달하는 것이 더 좋습니까?

좋은 질문. 이 경우 후자의 방법, 즉 작업을 지정하고 리소스 ID를 전달하는 것이 좋습니다. 이런 방식으로, 자원이 처음 수정 될 때 /sendEmail, 별도의 RESTful 요청 (수정 된 자원에 관계없이 나중에 다시 호출 할 수 있음)으로 조치 (부록 : "재전송"이라고 할 필요가 없음)를 호출합니다. ).

질문 2 : 비교 연산자 사용과 관련하여 :/collection?someField:gte=someval

이것은 기술적으로 괜찮지 만 나쁜 생각 일 것입니다. REST의 주요 원칙 중 하나는 가독성입니다. 예를 들어 비교 연산자를 다른 매개 변수로 전달하는 것이 좋습니다. /collection?someField=someval&operator=gte물론 operator매개 변수가 URI 에서 제외되는 경우 기본 사례를 충족하도록 API를 디자인하십시오 .

질문 3 : REST URI가 두 수준 이상인 이유가 있습니까?

예; 추상화를 위해. : 나는 예를 들어, 여러 URI 수준을 통해 추상화 레이어를 활용하는 몇 REST API를 본 적이 /vehicles/cars/123또는 /vehicles/bikes/123차례로 모두에 관한 유용한 정보와 함께 작업 할 수있는 /vehicles/vehicles/bikes컬렉션. 내가 말했듯이, 나는이 접근법의 열렬한 팬이 아니다. 실제로이 작업을 수행해야하는 경우는 거의 없으며 2 단계 만 사용하도록 API를 다시 디자인 할 수 있습니다.

그리고 네, 위의 의견에서 알 수 있듯이 앞으로 질문을 별도의 게시물로 나누는 것이 가장 좋습니다.)


2 번 질문에 대한 나의 예는 단순한 것이 아니라고 생각합니다. 컬렉션이 아닌 컬렉션을 필터링하는 데 사용되는 각 필드에 대해 비교 연산자를 지정해야하므로 예제에서는와 같아야 /collection?field1=someval&field1Operator=gte&field2=someval&field2Operator=eq합니다.
저스틴 워 켄틴

0

질문 2의 경우 다른 대안이 더 유연 할 수 있습니다. 각 검색을 사용하기 전에 사용자가 작성하는 리소스를 고려하십시오.

"검색"컨테이너가 있다고 가정하면 POST /api/searches/콘텐츠에 대한 쿼리 사양을 사용할 수 있습니다. JSON, XML 또는 심지어 SQL 문서 일 수도 있습니다. 쿼리가 올바르게 구문 분석되면 고유 한 URI를 가진 새 자원으로 새 검색이 작성됩니다./api/searches/q123/

그런 다음 클라이언트는 단순히 GET /api/searches/q123/쿼리 결과를 검색 할 수 있습니다 .

마지막으로 클라이언트에게 쿼리를 삭제하도록 요청하거나 세션을 닫은 후 제거 할 수 있습니다.


0

일종의 조치 호출을 자원 URI와 혼합하는 것이 적절합니까 (예 : / collection / 123? action = resendEmail)? 조치를 지정하고 자원 ID를 전달하는 것이 더 낫습니까 (예 : / collection / resendEmail? id = 123)? 이것이 잘못된 길입니까? 전통적으로 (적어도 HTTP를 사용하여) 수행되는 작업은 요청 방법 (GET, POST, PUT, DELETE)이지만 실제로는 리소스에 대한 사용자 지정 작업을 허용하지 않습니다.

IRI는 자원을 식별하기위한 것이며 조작이 아닌 것이기 때문에 적절하지 않습니다 (단, ppl이 POST 및 GET 메소드를 사용하지 않는 경우 ppl은이 메소드 대체 방법 을 잠시 사용합니다). 할 수있는 일은 적절한 HTTP 메소드를 찾거나 새 메소드를 작성하는 것입니다. 이러한 경우 POST는 친구가 될 수 있습니다 (적절한 방법을 찾을 수없고 요청이 검색되지 않은 경우 ppl 사용). 이메일 전송에서 리소스를 POST /emails만들어 실제 리소스를 만들지 않고 메일을 보낼 수있는 또 다른 방법 입니다. Btw. URI 구조는 의미를 전달하지 않으므로 REST 관점에서 사용하는 URI의 종류는 중요하지 않습니다. 중요한 것은 클라이언트에게 보낸 링크에 할당 된 메타 데이터 (예 : 링크 관계 )입니다.

지금까지 생각해 낸 가장 좋은 아이디어는 API 사용자가 필드 이름에 대한 부속물로이를 지정할 수 있도록하는 것입니다 (예 : / collection? someField : gte = someval-someField가 더 큰 경우 리소스를 반환해야 함을 나타냅니다) 또는 (> =) 같음 어떤 것이 든간에, 이것이 좋은 생각입니까? 나쁜 생각입니까? 그렇다면 왜입니까? 사용자가 주어진 필드와 값으로 수행 할 비교 유형을 지정할 수있는 더 좋은 방법이 있습니까?

고유 한 쿼리 언어를 만들 필요가 없습니다. 오히려 기존의 것을 사용하고 링크 메타 데이터에 쿼리 설명을 추가하려고합니다. RDF 미디어 유형 (예 : JSON-LD)을 사용하거나 사용자 정의 MIME 유형을 사용해야합니다 (이를 지원하는 비 RDF 형식은 없습니다). 기존 표준을 사용하면 클라이언트를 서버에서 분리 할 수 ​​있습니다. 이는 균일 한 인터페이스 제약 조건입니다.

/ dogs? person = 123과 같습니다. REST URI가 두 레벨 이상 (/ collection / resource_id) 이상이되는 충분한 이유가 있습니까?

앞서 언급했듯이 URI 구조는 REST 관점에서 중요하지 않습니다. /x71fd823df2예를 들어 사용할 수 있습니다 . URI 구조가 아닌 링크에 할당 된 메타 데이터를 확인하기 때문에 여전히 클라이언트에게 의미가 있습니다. URI의 주요 목적은 리소스를 식별하는 것입니다. URI 표준에서는 경로에 계층 데이터가 포함되고 쿼리에 비 계층 데이터가 포함되어 있다고 명시되어 있습니다. 그러나 계층적인 것은 매우 주관적 일 수 있습니다. 그래서 긴 쿼리로 여러 수준의 딥 URI 및 URI를 충족시키는 이유입니다.

나는 몇 시간 동안 끊임없이 검색했지만 어디에서나 내 질문에 대한 답변을 찾지 못했습니다 (어쩌면 무엇을 검색 해야할지 모르겠습니까?).

Fielding 논문 , HTTP 표준 및 Markus의 3 세대 웹 API 에서 적어도 REST 제약 조건을 읽어야 합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.