development

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

big-blog 2020. 7. 5. 07:37
반응형

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


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

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

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

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

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


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

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


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

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

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

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


나는 최근에 이것과 다른 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헤더 와 결합시키는 이유 .

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

컨텐츠 범위

Content-Range: items 0-49/234

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

봉투

Many APIs, including the one from our favorite Q&A website use an envelope, a wrapper around the data that is used to add meta information about the data. Also, OData and JsonApi standards both use a response envelope.

The big downside to this (imho) is that processing the response data becomes more complex as the actual data has to be found somewhere in the envelope. Also there are many different formats for that envelope and you have to use the right one. It is telling that the response envelopes from OData and JsonApi are wildly different, with OData mixing in metadata at multiple points in the response.

Separate endpoint

I think this has been covered enough in the other answers. I did not investigate this much because I agree with the comments that this is confusing as you now have multiple types of endpoints. I think it's nicest if every endpoint represents a (collection of) resource(s).

Further thoughts

We don't only have to communicate the paging meta information related to the response, but also allow the client to request specific pages/ranges. It is interesting to also look at this aspect to end up with a coherent solution. Here too we can use headers (the Range header seems very suitable), or other mechanisms such as query parameters. Some people advocate treating pages of results as separate resources, which may make sense in some use cases (e.g. /books/231/pages/52. I ended up selecting a wild range of frequently used request parameters such as pagesize, page[size] and limit etc in addition to supporting the Range header (and as request parameter as well).


You could return the count as a custom HTTP header in response to a HEAD request. This way, if a client only wants the count, you don't need to return the actual list, and there's no need for an additional URL.

(Or, if you're in a controlled environment from endpoint to endpoint, you could use a custom HTTP verb such as COUNT.)


Alternative when you don't need actual items

Franci Penov's answer is certainly the best way to go so you always return items along with all additional metadata about your entities being requested. That's the way it should be done.

but sometimes returning all data doesn't make sense, because you may not need them at all. Maybe all you need is that metadata about your requested resource. Like total count or number of pages or something else. In such case you can always have URL query tell your service not to return items but rather just metadata like:

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

or something similar...


I would recommend adding headers for the same, like:

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"
  }
]

For details refer to:

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

For swagger file:

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


As of "X-"-Prefix was deprecated. (see: https://tools.ietf.org/html/rfc6648)

We found the "Accept-Ranges" as being the best bet to map the pagination ranging: https://tools.ietf.org/html/rfc7233#section-2.3 As the "Range Units" may either be "bytes" or "token". Both do not represent a custom data type. (see: https://tools.ietf.org/html/rfc7233#section-4.2) Still, it is stated that

HTTP/1.1 implementations MAY ignore ranges specified using other units.

Which indicates: using custom Range Units is not against the protocol, but it MAY be ignored.

This way, we would have to set the Accept-Ranges to "members" or whatever ranged unit type, we'd expect. And in addition, also set the Content-Range to the current range. (see: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12)

Either way, I would stick to the recommendation of RFC7233 (https://tools.ietf.org/html/rfc7233#page-8) to send a 206 instead of 200:

If all of the preconditions are true, the server supports the Range
header field for the target resource, and the specified range(s) are
valid and satisfiable (as defined in Section 2.1), the server SHOULD
send a 206 (Partial Content) response with a payload containing one
or more partial representations that correspond to the satisfiable
ranges requested, as defined in Section 4.

So, as a result, we would have the following HTTP header fields:

For Partial Content:

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

For full Content:

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

What about a new end point > /api/members/count which just calls Members.Count() and returns the result


Seems easiest to just add a

GET
/api/members/count

and return the total count of members


Sometimes frameworks (like $resource/AngularJS) require an array as a query result, and you can't really have a response like {count:10,items:[...]} in this case I store "count" in responseHeaders.

P. S. Actually you can do that with $resource/AngularJS, but it needs some tweaks.


When requesting paginated data, you know (by explicit page size parameter value or default page size value) the page size, so you know if you got all data in response or not. When there is less data in response than is a page size, then you got whole data. When a full page is returned, you have to ask again for another page.

I prefer have separate endpoint for count (or same endpoint with parameter countOnly). Because you could prepare end user for long/time consuming process by showing properly initiated progressbar.

If you want to return datasize in each response, there should be pageSize, offset mentionded as well. To be honest the best way is to repeat a request filters too. But the response became very complex. So, I prefer dedicated endpoint to return count.

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

Couleage of mine, prefer a countOnly parameter to existing endpoint. So, when specified the response contains metadata only.

endpoint?filter=value

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

endpoint?filter=value&countOnly=true

<data>
  <count/>
  <!-- empty list -->
  <list/>
</data>

참고URL : https://stackoverflow.com/questions/3715981/what-s-the-best-restful-method-to-return-total-number-of-items-in-an-object

반응형