객체의 총 항목 수를 반환하는 가장 좋은 RESTful 방법은 무엇입니까?


139

내가 참여하고있는 대규모 소셜 네트워킹 웹 사이트를위한 REST API 서비스를 개발 중입니다. 지금까지는 잘 작동하고 있습니다. 나는 발급 할 수 있습니다 GET, POST, PUTDELETE객체의 URL에 대한 요청과 내 데이터에 영향을 미친다. 그러나이 데이터는 페이징됩니다 (한 번에 30 개의 결과로 제한됨).

그러나 내 API를 통해 총 회원 수를 얻는 가장 좋은 RESTful 방법은 무엇입니까?

현재 다음과 같은 URL 구조에 요청을 발행합니다.

  • / api / members — 구성원 목록을 반환합니다 (위에서 언급 한대로 한 번에 30 개)
  • / api / members / 1 — 사용 된 요청 방법에 따라 단일 멤버에 영향을줍니다.

내 질문은 : 어떻게 비슷한 URL 구조를 사용하여 내 응용 프로그램의 총 멤버 수를 얻습니까? 분명히 id필드 (Facebook의 Graph API와 유사)를 요청 하고 결과를 세는 것은 효과가 없을 것입니다 .30 개의 결과 조각 만 반환됩니다.


답변:


84

/ API / users에 대한 응답이 페이징되고 30 개의 레코드 만 리턴하지만 응답에 총 레코드 수 및 페이지 크기, 페이지 번호 / 오프셋 등과 같은 기타 관련 정보도 포함시키지 못하게하는 것은 없습니다. .

StackOverflow API는 동일한 디자인의 좋은 예입니다. 다음은 Users 메소드에 대한 설명서입니다-https: //api.stackexchange.com/docs/users


3
+1 : 페치 한계가 전혀 적용되지 않을 경우 가장 RESTful 한 방법입니다.
Donal Fellows

2
@bzim rel = "next"와의 링크가 있으므로 다음 페치 페이지가 있음을 알고 있습니다.
Darrel Miller


1
@Darrel-예, 페이로드에서 "next"플래그 유형으로 수행 할 수 있습니다. 응답에 수집 항목의 총 수를 갖는 것이 그 자체로 가치 있고 "다음"플래그로 작동한다고 생각합니다.
Franci Penov

5
항목 목록이 아닌 오브젝트를 리턴하는 것은 REST API의 올바른 구현이 아니지만 REST는 부분 결과 목록을 얻는 방법을 제공하지 않습니다. 이를 존중하기 위해 총계, 다음 페이지 토큰 및 이전 페이지 토큰과 같은 다른 정보를 전송하기 위해 헤더를 사용해야한다고 생각합니다. 나는 그것을 시도하지 않았으며 다른 개발자의 조언이 필요합니다.
Loenix

74

이런 종류의 상황 정보에 HTTP 헤더를 사용하는 것이 좋습니다.

총 요소 수에는 X-total-count헤더를 사용 합니다.
다음 페이지, 이전 페이지 등의 링크는 http Link헤더를 사용합니다 :
http://www.w3.org/wiki/LinkHeader

Github은 https://developer.github.com/v3/#pagination 과 같은 방식으로 수행합니다.

내 의견으로는 하이퍼 링크를 지원하지 않는 콘텐츠 (예 : 이진, 그림)를 반환 할 때도 사용할 수 있기 때문에 더 깨끗합니다.


5
RFC6648은 표준화 되지 않은 매개 변수 이름 앞에 문자열을 사용하는 규칙을 사용하지 않습니다 X-.
JDawg

70

나는 최근에 이것과 다른 REST 페이징 관련 질문에 대한 광범위한 연구를 해 왔으며 여기에 내 발견 사항을 추가하는 것이 건설적인 것이라고 생각했습니다. 나는 페이징에 대한 생각과 그들이 친밀하게 관련된 수를 포함하도록 질문을 조금 확장하고 있습니다.

헤더

페이징 메타 데이터는 응답 헤더 형식으로 응답에 포함됩니다. 이 접근 방식의 가장 큰 장점은 응답 페이로드 자체가 실제 데이터 요청자가 요청한 것입니다. 페이징 정보에 관심이없는 클라이언트의 응답 처리를보다 쉽게 ​​만듭니다.

총 수를 포함하여 페이징 관련 정보를 반환하기 위해 와일드에 사용되는 많은 (표준 및 사용자 정의) 헤더가 있습니다.

X-Total-Count

X-Total-Count: 234

이것은 내가 야생에서 찾은 일부 API 에서 사용됩니다 . 루프백에이 헤더에 대한 지원을 추가하기위한 NPM 패키지 도 있습니다 . 일부 기사 에서는이 헤더를 설정하는 것이 좋습니다.

Link헤더 와 함께 사용되는 경우가 많습니다. 페이징에는 매우 적합한 솔루션이지만 총 개수 정보가 없습니다.

링크

Link: </TheBook/chapter2>;
      rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
      </TheBook/chapter4>;
      rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel

나는 일반적인 합의가 사용하는 것으로,이 주제에 많이 읽고, 느낌이 Link헤더를 사용하여 클라이언트에 대한 링크를 페이징 제공하기 위해 rel=next, rel=previous이에 등의 문제는 인, 얼마나 많은 총 레코드의 정보 부족이다 많은 API가 이것을X-Total-Count 헤더 .

또는 JsonApi 표준 과 같은 일부 API 는 Link형식을 사용 하지만 헤더 대신 응답 엔벨로프에 정보를 추가하십시오. 이는 엔벨로프를 추가하여 실제 데이터 자체에 대한 액세스 복잡성을 증가시키는 대신에 메타 데이터에 대한 액세스를 단순화하고 총 수 정보를 추가 할 수있는 장소를 만듭니다.

컨텐츠 범위

Content-Range: items 0-49/234

Range header 라는 블로그 기사에서 홍보 한 결과, 페이지 매김을 선택합니다. . 저자는 페이지 매김에 RangeContent-Range헤더를 사용하는 강력한 사례를 만듭니다 . 이 헤더 에서 RFC 를 주의 깊게 읽으면 실제로 RFC에서 바이트 범위를 넘어 그 의미를 확장하는 것이 예상되었으며 명시 적으로 허용되는 것을 알 수 있습니다. 의 맥락에서 사용하는 경우 items대신 bytes, 레인지 헤더는 실제로 모두 요청에 우리에게 항목의 특정 범위를 방법을 제공하고 응답 항목과 관련된 전체 결과의 범위는 무엇을 나타냅니다. 이 헤더는 또한 총 수를 표시하는 좋은 방법을 제공합니다. 그리고 일대일로 페이징을 매핑하는 것은 진정한 표준입니다. 또한 야생 에서도 사용됩니다 .

봉투

자주 사용 하는 Q & A 웹 사이트의 API를 포함 하여 많은 API 는 데이터에 대한 메타 정보를 추가하는 데 사용되는 데이터 래퍼 인 envelope를 사용합니다 . 또한 ODataJsonApi 표준은 모두 응답 범위를 사용합니다.

이에 대한 가장 큰 단점은 실제 데이터가 엔벨로프에서 찾아야하므로 응답 데이터 처리가 더 복잡해진다는 것입니다. 또한 해당 봉투에는 여러 가지 형식이 있으므로 올바른 것을 사용해야합니다. OData와 JsonApi의 응답 엔벨로프는 응답의 여러 지점에서 메타 데이터에서 혼합되는 OData와는 크게 다릅니다.

별도의 엔드 포인트

나는 이것이 다른 답변에서 충분히 다루어 졌다고 생각합니다. 여러 유형의 엔드 포인트가 있으므로 혼동된다는 의견에 동의하기 때문에 이것을 많이 조사하지 않았습니다. 모든 엔드 포인트가 (자원 모음)을 나타내는 것이 가장 좋다고 생각합니다.

추가 생각

응답과 관련된 페이징 메타 정보를 전달할 필요가있을뿐만 아니라 클라이언트가 특정 페이지 / 범위를 요청할 수도 있습니다. 일관된 솔루션으로 끝나기 위해이 측면을 보는 것도 흥미 롭습니다. 여기에서도 헤더 ( Range헤더가 매우 적합 함) 또는 쿼리 매개 변수와 같은 다른 메커니즘을 사용할 수 있습니다 . 어떤 사람들은 몇 가지 사용 사례에 의미가 있습니다 별도의 자원 (예를 들어 같은 결과 페이지를 치료 옹호 /books/231/pages/52나는 등 자주 사용하는 요청 매개 변수의 야생 범위를 선택 끝났다. pagesize, page[size]limit등 지원 외에 Range헤더 (요청 매개 변수로 게다가).


특히 Range헤더에 관심이 있었지만 bytes범위 유형 이외의 것을 사용 하는 것이 유효 하다는 충분한 증거를 찾을 수 없었습니다 .
VisioN

2
나는 명확한 증거에서 찾을 수 있습니다 생각 은 RFC의 섹션 14.5 : acceptable-ranges = 1#range-unit | "none"나는이 제제는 명시 적으로 이외의 범위 단위 여지 생각 bytes, 사양 자체에만 정의하지만 bytes.
Stijn de Witt

24

실제 품목이 필요하지 않은 경우의 대안

Franci Penov의 답변 은 확실히 가장 좋은 방법이므로 요청되는 엔티티에 대한 모든 추가 메타 데이터와 함께 항상 항목을 반환하십시오. 그렇게해야합니다.

그러나 때때로 모든 데이터를 반환하는 것은 전혀 필요하지 않기 때문에 의미가 없습니다. 요청한 리소스에 대한 메타 데이터 만 있으면됩니다. 총 페이지 수 또는 다른 페이지 수 이 경우 항상 URL 쿼리가 서비스에 항목을 반환하지 말고 메타 데이터 만 반환하도록 지시 할 수 있습니다.

/api/members?metaonly=true
/api/members?includeitems=0

또는 비슷한 ...


10
이 정보를 헤더에 포함하면 카운트를 얻기 위해 HEAD 요청을 할 수 있다는 이점이 있습니다.
felixfbecker

1
@felixfbecker 정확하게, 바퀴를 재발 명하고 모든 종류의 다른 메커니즘으로 API를 어지럽게 해주셔서 감사합니다. :)
EralpB

1
@EralpB 휠을 재발 명하고 API를 어지럽게 해주셔서 감사합니다!? HEAD는 HTTP로 지정됩니다. metaonly또는 includeitems아닙니다.
felixfbecker

2
@felixfbecker만이 "정확하게"당신을위한 것이고 나머지는 OP를위한 것입니다. 혼란을 드려 죄송합니다.
EralpB

REST는 HTTP를 활용하고 가능한 한 원하는 용도로 HTTP를 활용하는 것입니다. 이 경우 컨텐츠 범위 (RFC7233)를 사용해야합니다. 신체 내 솔루션은 특히 HEAD와 함께 작동하지 않기 때문에 좋지 않습니다. 여기에 제안 된대로 새로운 헤더를 만드는 것은 불필요하고 잘못되었습니다.
Vance Shipley

23

HEAD 요청에 대한 응답으로 카운트를 사용자 정의 HTTP 헤더로 리턴 할 수 있습니다. 이렇게하면 클라이언트가 개수 만 원하는 경우 실제 목록을 반환 할 필요가 없으며 추가 URL이 필요하지 않습니다.

또는 끝점에서 끝점까지 제어 된 환경에있는 경우 COUNT와 같은 사용자 지정 HTTP 동사를 사용할 수 있습니다.


4
“사용자 정의 HTTP 헤더”? 그것은 다소 놀랍다는 제목 아래에 올 것이며, 이는 RESTful API가 생각하는 것과는 반대입니다. 궁극적으로 그것은 놀라운 일이 아닙니다.
Donal Fellows

21
@Donal 알고 있습니다. 그러나 모든 좋은 대답은 이미 취해졌습니다. :(
bzlm

1
나도 알고 있지만 때로는 다른 사람들이 대답을하도록해야합니다. 또는 다른 사람보다 최선의 방법으로 수행해야하는 이유에 대한 자세한 설명과 같은 다른 방식으로 기여도를 높이십시오.
Donal Fellows

4
통제 된 환경에서는 개발자의 API 정책에 따라 내부적으로 사용되기 때문에 이는 놀라운 일이 아닙니다. 나는 이것이 어떤 경우에는 좋은 해결책이라고 말하고 여기에 가능한 특이한 해결책의 메모로 여기에 가치가 있다고 말하고 싶습니다.
James Billingham

1
나는 이런 종류의 일에 HTTP 헤더를 사용하는 것을 매우 좋아합니다 (실제로 그것이 어디에 있는지). 이 경우 표준 링크 헤더 가 적합 할 수 있습니다 (Github API가이를 사용함).
Mike Marcacci

11

다음과 같이 헤더를 추가하는 것이 좋습니다.

HTTP/1.1 200

Pagination-Count: 100
Pagination-Page: 5
Pagination-Limit: 20
Content-Type: application/json

[
  {
    "id": 10,
    "name": "shirt",
    "color": "red",
    "price": "$23"
  },
  {
    "id": 11,
    "name": "shirt",
    "color": "blue",
    "price": "$25"
  }
]

자세한 내용은 다음을 참조하십시오.

https://github.com/adnan-kamili/rest-api-response-format

스와 거 파일의 경우 :

https://github.com/adnan-kamili/swagger-response-template


7

"X-"-접두사가 사용되지 않습니다. ( https://tools.ietf.org/html/rfc6648 참조 )

"Accept-Ranges"는 페이지 매김 범위를 매핑하는 가장 좋은 방법으로 나타났습니다. https://tools.ietf.org/html/rfc7233#section-2.3 "범위 단위"는 "바이트"또는 " 토큰". 둘 다 사용자 정의 데이터 유형을 나타내지 않습니다. ( https://tools.ietf.org/html/rfc7233#section-4.2 참조 ) 그래도 여전히

HTTP / 1.1 구현은 다른 단위를 사용하여 지정된 범위를 무시할 수 있습니다.

사용자 지정 범위 단위를 사용하는 것은 프로토콜에 위배되지 않지만 무시 될 수 있습니다.

이런 식으로 Accept-Ranges를 "members"또는 원하는 범위의 단위 유형으로 설정해야합니다. 또한 Content-Range를 현재 범위로 설정하십시오. ( https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12 참조 )

어느 쪽이든, 나는 200 대신 206을 보내도록 RFC7233 ( https://tools.ietf.org/html/rfc7233#page-8 )을 권장합니다 .

모든 전제 조건이 참이면, 서버는
대상 자원에 대한 범위 헤더 필드를 지원하고 지정된 범위가
유효하고 만족할 수 있습니다 (2.1 절에 정의 된대로), 서버는
206 (부분 컨텐츠) 응답을 보내야합니다 섹션 4에 정의 된대로 요청
된 만족스러운
범위에 해당 하는 하나 이상의 부분 표현을 포함하는 페이로드

결과적으로 다음과 같은 HTTP 헤더 필드가 생깁니다.

부분 컨텐츠의 경우 :

206 Partial Content
Accept-Ranges: members
Content-Range: members 0-20/100

전체 내용 :

200 OK
Accept-Ranges: members
Content-Range: members 0-20/20

3

그냥 추가하는 것이 가장 쉬운 것 같습니다

GET
/api/members/count

총 회원 수를 반환


11
좋은 생각이 아닙니다. 고객은 페이지에서 페이지 매김 작성을 요청해야합니다. 첫 번째는 자원 목록을 요청하고 두 번째는 총계를 계산합니다.
Jekis

나는 그것이 좋은 접근법이라고 생각한다 .json으로 결과 목록을 반환하고 클라이언트 측에서 컬렉션의 크기를 검사 할 수 있으므로 그러한 경우는 어리석은 예이다. / members? offset = 10 & limit = 20
Michał Ziobro

1
또한 많은 유형의 페이지 매김에는 카운트가 필요하지 않습니다 (무한 스크롤과 같은)-클라이언트가 필요하지 않을 때 이것을 계산하는 이유
tofarr

2

Members.Count ()를 호출하고 결과를 반환하는 새로운 종점> / api / members / count는 어떻습니까?


27
카운트에 명시적인 엔드 포인트를 부여하면 독립형 주소 지정 가능 리소스가됩니다. 그것은 작동하지만 API를 처음 접하는 사람에게는 흥미로운 질문을 제기 할 것입니다-컬렉션 멤버의 수는 컬렉션과 별도의 리소스입니까? PUT 요청으로 업데이트 할 수 있습니까? 빈 컬렉션이거나 항목이있는 경우에만 존재합니까? 는 IF members컬렉션에 POST 요청에 의해 생성 될 수있다 /api, 할 /api/members/count뿐만 아니라 부작용으로 생성, 또는 내가 그것을 요청하기 전에 그것을 만들 수있는 명시 적 POST 요청을 어떻게해야합니까 수? :-)
Franci Penov

2

때때로 $ resource / AngularJS와 같은 프레임 워크에는 쿼리 결과로 배열이 필요하므로 실제로 다음과 같은 응답을 가질 수 없습니다. {count:10,items:[...]} 이 경우 responseHeaders에 "count"를 저장하는 .

추신 : 실제로 $ resource / AngularJS로 그렇게 할 수 있지만 약간의 조정이 필요합니다.


그 조정은 무엇입니까? 그들은 다음과 같은 질문에 도움이 될 것입니다 : stackoverflow.com/questions/19140017/…
JBCP

Angular는 쿼리 결과로 배열을 요구하지 않습니다. 옵션 객체 속성으로 리소스를 구성하면됩니다.isArray: false|true
Rémi Becheras

0

counts자원으로 고려할 수 있습니다. 그러면 URL은 다음과 같습니다.

/api/counts/member

-1

페이지가 매겨진 데이터를 요청할 때 페이지 크기 매개 변수 값 또는 기본 페이지 크기 값으로 페이지 크기를 알 수 있으므로 모든 데이터가 응답했는지 여부를 알 수 있습니다. 페이지 크기보다 응답이 적은 데이터가 있으면 전체 데이터를 얻습니다. 전체 페이지가 반환되면 다른 페이지를 다시 요청해야합니다.

count (또는 countOnly 매개 변수가있는 동일한 엔드 포인트)에 대해 별도의 엔드 포인트를 선호합니다. 제대로 시작된 진행률 표시 줄을 표시하여 최종 사용자에게 오랜 시간이 걸리는 프로세스를 준비 할 수 있기 때문입니다.

각 응답에서 datasize를 반환하려면 pageSize, offset도 언급해야합니다. 솔직히 말하면 가장 좋은 방법은 요청 필터를 반복하는 것입니다. 그러나 응답은 매우 복잡해졌습니다. 따라서 전용 엔드 포인트를 반환 카운트를 선호합니다.

<data>
  <originalRequest>
    <filter/>
    <filter/>
  </originalReqeust>
  <totalRecordCount/>
  <pageSize/>
  <offset/>
  <list>
     <item/>
     <item/>
  </list>
</data>

내 Couleage는 기존 엔드 포인트보다 countOnly 매개 변수를 선호합니다. 따라서 지정된 경우 응답에 메타 데이터 만 포함됩니다.

끝점? 필터 = 값

<data>
  <count/>
  <list>
    <item/>
    ...
  </list>
</data>

엔드 포인트? filter = value & countOnly = true

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