REST 웹 애플리케이션의 페이지 매김


329

이것은 이 질문에 대한 보다 일반적인 재구성입니다 (Rails 관련 부분을 제거함).

RESTful 웹 응용 프로그램의 리소스에서 페이지 매김을 구현하는 방법을 잘 모르겠습니다. 내가이라는 리소스를 가지고 있다고 가정하면 products다음 중 어떤 것이 가장 좋은 방법이라고 생각합니까?

1. 쿼리 문자열 만 사용

예. http://application/products?page=2&sort_by=date&sort_how=asc
여기서 문제는 전체 페이지 캐싱을 사용할 수 없으며 URL이 매우 깨끗하고 기억하기 쉽지 않다는 것입니다.

2. 정렬을 위해 페이지를 리소스 및 쿼리 문자열로 사용

예. http://application/products/page/2?sort_by=date&sort_how=asc
이 경우에 볼 수있는 문제는 http://application/products/pages/1사용 sort_by=price하면 완전히 다른 결과를 얻을 수 있고 여전히 페이지 캐싱을 사용할 수 없기 때문에 고유 한 리소스 가 아니라는 것입니다.

3. 페이지를 리소스로 사용하고 정렬을위한 URL 세그먼트

예. http://application/products/by-date/page/2
개인적 으로이 방법을 사용하는 데 아무런 문제가 없지만 누군가는 이것이 좋은 방법이 아니라고 경고했습니다 (그는 이유를주지 않았 으므로 권장하지 않는 이유 를 알고 있다면 알려주십시오)

모든 제안, 의견, 비평은 환영 이상입니다. 감사.


34
이것은 좋은 질문입니다.
Iain Holder

7
보너스 질문 : 사람들은 일반적으로 페이지 크기를 어떻게 지정합니까?
Heiko Rupp

매트릭스 매개 변수를 잊지 마십시오. w3.org/DesignIssues/MatrixURIs.html
CMCDragonkai

답변:


66

버전 3의 문제는 "관점"문제라고 생각합니다. 페이지에서 페이지를 리소스 또는 제품으로 보십니까?

페이지를 리소스로 볼 경우 2 페이지에 대한 쿼리는 항상 2 페이지를 생성하므로 완벽하게 훌륭한 솔루션입니다.

그러나 페이지의 제품을 리소스로 볼 경우 2 페이지의 제품이 변경 될 수있는 문제 (오래된 제품이 삭제되거나 기타)가 발생하는 경우 URI가 항상 동일한 리소스를 반환하지는 않습니다.

예를 들어, 고객이 제품 목록 페이지 X에 대한 링크를 저장하고 다음에 링크를 열면 해당 제품이 더 이상 X 페이지에 없을 수 있습니다.


6
그러나 무언가를 삭제하면 동일한 URI에 다른 것이 없어야합니다. X 페이지의 모든 제품을 삭제하면 X 페이지는 여전히 유효하지만 이제 X + 1 페이지의 제품을 포함 할 수 있습니다. 따라서 "제품 자원보기에서 X 페이지의 URI는 X + 1 페이지의 URI가되었습니다. ".
Fionn

1
> 페이지를 리소스로 볼 경우 페이지 2에 대한 쿼리가 항상 페이지 2를 생성하므로 완벽하게 해결되는 솔루션입니다. 동일한 URL (2 페이지를 언급하는 모든 URL)은 리소스에 관계없이 항상 2 페이지를 생성합니다.
temoto 2009

2
페이지를 리소스로 보는 것은 아마도 새 페이지를 생성하기 위해 POST / foo / page를 소개해야합니다.
temoto

18
귀하의 답변은 "올바른 해결책은 1"입니다.
temoto

2
내 생각에 페이지는 떠 다니는 개념이며 기본 도메인과 관련이 없습니다. 따라서 자원으로 간주해서는 안됩니다. 나는 유동적이며 페이지의 개념이 맥락에 따라 변한다는 의미에서 떠 다니는 것을 의미합니다. API의 한 사용자는 모바일 앱일 수 있으며 페이지 당 2 개의 제품 만 소비 할 수있는 반면, 다른 사용자는 전체 목록을 사용할 수있는 머신 앱일 수 있습니다. 간단히 말해 페이지는 기본 도메인 엔티티 (제품)의 "표현"이며 URL의 일부로 포함되어서는 안됩니다. 쿼리 매개 변수로만 사용하십시오.
Kingz

106

Fionn에 동의하지만 한 단계 더 나아가 페이지가 리소스 가 아니라 요청의 속성 이라고 말합니다 . 따라서 옵션 1 쿼리 문자열 만 선택했습니다. 기분이 좋아. Twitter API 가 편안하게 구성되는 방식이 정말 좋습니다. 너무 간단하지도 않고 복잡하지도 않으며 문서화가 잘되어 있습니다. 더 좋든 나쁘 든 내가 어떤 방식 으로든 다른 방식으로 무언가를 할 때 울타리에있을 때 "가는"디자인입니다.


28
+1 : 쿼리 문자열은 일류 리소스 식별자가 아닙니다. 그들은 단지 자원의 순서와 그룹화에 대한 설명입니다.
S.Lott

1
@ S.Lott 요청 리소스입니다. 당신이 "일류 자원"이라고 부르는 것은 그의 논문의 5.2.1.1 섹션 에서 Fielding에 의해 으로 정의됩니다 . 또한 동일한 섹션에서 Fielding은 리소스의 예로 소스 코드 파일 의 최신 개정판 을 제공합니다 . 어떻게 리소스가 될 수 있지만 최신 10 개의 제품 은 "제품 리소스에 대한 요청의 속성"입니까? 귀하의 견해가 더 실용적이지만 이해하기 어렵다고 생각합니다.
edsioufi 2014 년

내 의견은 URL에서 쿼리 문자열을 사용하는 것에 동의하지 않는다는 것을 의미하지는 않습니다. 페이지를 REST 관점에서 리소스로 간주해야한다고 지적하고 있습니다.
edsioufi 2014 년

37

HTTP에는 페이지 매김에 적합한 Range 헤더가 있습니다. 보낼 수 있습니다

Range: pages=1

첫 페이지 만 갖습니다. 페이지가 무엇인지 다시 생각해야 할 수도 있습니다. 클라이언트가 다른 범위의 항목을 원할 수도 있습니다. 범위 헤더는 주문을 선언하기 위해 작동합니다.

Range: products-by-date=2009_03_27-

해당 날짜보다 최신의 모든 제품을 얻거나

Range: products-by-date=0-2009_11_30

해당 날짜보다 오래된 모든 제품을 가져옵니다. '0'은 아마도 최상의 솔루션은 아니지만 RFC는 범위 시작을 위해 무언가를 원하는 것 같습니다. units = -range_end를 구문 분석하지 않는 HTTP 구문 분석기가 배치되었을 수 있습니다.

헤더가 (허용 가능한) 옵션이 아닌 경우 첫 번째 솔루션 (모두 쿼리 문자열)은 페이지를 처리하는 방법입니다. 그러나 쿼리 문자열 (알파벳 순서로 정렬 (키 = 값) 쌍)을 정규화하십시오. "? a = 1 & b = x"및 "? b = x & a = 1"미분 문제를 해결합니다.


34
헤더는 언뜻보기에는 좋을지 모르지만 페이지 공유를 허용하지 않습니다 (예 : URL 복사). 따라서 아약스 요청의 경우 훌륭한 해결책 일 수 있습니다 (아약스에 의해 수정 된 페이지는 현재 상태에서 공유 할 수 없기 때문에). 나는 정기적 인 페이지 매김에 사용하지 않을 것입니다.
Markus

3
그리고 Range 헤더는 바이트 범위에만 해당됩니다. [HTTP 헤더 사양] ( w3.org/Protocols/rfc2616/rfc2616-sec14.html ), 섹션 14.35를 참조하십시오.
Chris Westin

16
@ChrisWestin w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12 HTTP / 1.1은 Range (섹션 14.35) 및 Content-Range (섹션 14.16) 헤더 필드의 범위 단위를 사용합니다. range-unit = bytes-unit | other-range-unit 아마도 당신은 The only range unit defined by HTTP/1.1 is "bytes". HTTP/1.1 implementations MAY ignore ranges specified using other units.그것이 당신의 진술과 같지 않다고 언급하고있을 것입니다.
temoto

1
@Markus 나는 당신이 나머지 API 리소스를 공유 할 때 유스 케이스를 상상할 수 없다 :)
JakubKnejzlik

@JakubKnejzlik 공유는 문제가되지 않지만 페이징에 HTTP 헤더를 사용하면 페이징에 HATEOAS 링크를 사용할 수 없습니다.
xarx

25

옵션 1은 응용 프로그램에서 페이지 매김을 동일한 리소스의 다른보기를 생성하는 기술로 보는 범위에서 가장 좋습니다.

그러나 URL 체계는 상대적으로 중요하지 않습니다. 모든 REST 애플리케이션이 정의에 따라야하므로 애플리케이션을 하이퍼 텍스트 방식 으로 설계하는 경우 클라이언트가 자체적으로 URI를 구성하지 않습니다. 대신 응용 프로그램에서 클라이언트에 대한 링크를 제공하고 클라이언트가 해당 링크를 따릅니다.

고객이 제공 할 수있는 링크 중 하나는 페이지 매김 링크입니다.

이 모든 것의 유쾌한 부작용은 페이지 매김 URI 구조에 대해 마음이 바뀌고 다음 주에 완전히 다른 것을 구현하더라도 클라이언트가 수정없이 계속 일할 수 있다는 것입니다.


3
REST 웹 서비스의 링크와 같은 하이퍼 미디어 사용에 대한 좋은 알림.
Paul D. Eden

11

나는 항상 옵션 1의 스타일을 사용했다. 캐싱은 필자의 경우 데이터가 자주 바뀌기 때문에 문제가되지 않았다. 페이지 크기를 구성 할 수 있으면 데이터를 다시 캐시 할 수 없습니다.

기억하기 어렵거나 부정한 URL을 찾지 못했습니다. 나에게 이것은 쿼리 매개 변수를 잘 사용합니다. 리소스는 분명히 제품 목록이며 쿼리 매개 변수는 목록을 표시하는 방법-정렬 및 페이지를 알려줍니다.


1
+1 당신이 옳다고 생각하고 쿼리 매개 변수 (옵션 1)와 함께 갈 것입니다
andi

"기억하기 어려운 URL을 찾지 못했습니다". 이 관찰은 일반적으로 단일 책갈피가 하나만 있어야하므로 REST 애플리케이션에서는 쓸모가 없습니다. 사용자 (또는 클라이언트 앱)가 URL을 "기억"하려고하면 API가 편안하지 않다는 좋은 신호입니다.
edsioufi 2014 년

8

옵션 3에 특정 순서로 매개 변수가 있다고 아무도 지적하지 않았습니다. http // application / products / 날짜 / 내림차순 / 이름 / 오름차순 / 페이지 / 2http // application / products / 이름 / 오름차순 / 날짜 / 내림차순 / 페이지 / 2

동일한 리소스를 가리 키지 만 URL이 완전히 다릅니다.

나를 위해 옵션 1은 "내가 원하는 것"" 내가 원하는 것"을 명확하게 분리하기 때문에 가장 수용 가능한 것 같습니다 (물론 롤 사이에 물음표가 있습니다). 전체 페이지 캐싱은 전체 URL을 사용하여 구현할 수 있습니다 (어쨌든 모든 옵션에서 동일한 문제가 발생 함).

URL에서 매개 변수 접근 방식을 사용하면 유일한 장점은 깨끗한 URL입니다. 매개 변수를 인코딩하고 손실없이 디코딩하는 방법을 찾아야합니다. 물론 URLencode / decode로 갈 수는 있지만 URL을 다시 못생긴 것입니다 :)


1
그것들은 두 가지 다른 주문입니다. 첫 번째 날짜는 내림차순으로 정렬되며 오름차순으로 만 묶습니다. 두 번째는 이름 오름차순으로 정렬하고 내림차순으로 만 묶습니다.
Imran Rashid

실제로 여기에 제시된 두 가지 예제 URL은 글쓰기뿐 아니라 의미도 다릅니다. 경로를 나타내므로 나중에 왼쪽에서 처음으로 또는 오른쪽으로 회전하거나 그 반대로 할 때도 동일한 것을 찾을 수 있습니다. 이것을 말했지만, URL 경로 부분으로서의 정렬 매개 변수는 URL 매개 변수에 대한 형식적인 장점을 갖습니다. URL 매개 변수는 전체적인 의미를 변경하지 않고 교환 적으로 교환 할 수 있어야하지만 실제로는 여기에서 언급 된 것처럼 인코딩 트랩이 발생합니다.
Christian Gosch

7

쿼리 매개 변수 오프셋 및 제한을 사용하고 싶습니다.

오프셋 : 컬렉션의 항목 인덱스

한도 : 품목 수.

클라이언트는 다음과 같이 오프셋을 계속 업데이트 할 수 있습니다.

offset = offset + limit

다음 페이지를 위해.

경로는 리소스 식별자로 간주됩니다. 그리고 페이지는 리소스가 아니라 리소스 모음의 하위 집합입니다. 페이지 매김은 일반적으로 GET 요청이므로 쿼리 매개 변수는 헤더가 아니라 페이지 매김에 가장 적합합니다.

참조 : https://metamug.com/article/rest-api-developers-dilemma.html#Requesting-the-next-page


5

이 사이트를 방문한 모범 사례를 찾고 있습니다.

http://www.restapitutorial.com

리소스 페이지에는 저자가 제안한 전체 REST 모범 사례가 포함 된 .pdf를 다운로드 할 수있는 링크가 있습니다. 페이지 매김에 대한 섹션이 있습니다.

필자는 Range 헤더와 query-string 매개 변수를 모두 사용하여 지원을 추가 할 것을 제안합니다.

의뢰

HTTP 헤더 예 :

Range: items=0-24

쿼리 문자열 매개 변수 예 :

GET http://api.example.com/resources?offset=0&limit=25

여기서 offset 은 시작 항목 번호이고 limit 은 반환 할 최대 항목 수입니다.

응답

응답에는 반환되는 항목 수와 아직 검색되지 않은 총 항목 수를 나타내는 Content-Range 헤더가 포함되어야합니다.

HTTP 헤더 예 :

Content-Range: items 0-24/66

Content-Range: items 40-65/*

.pdf에는 좀 더 구체적인 경우에 대한 다른 제안이 있습니다.


4

현재 ASP.NET MVC 앱에서 이와 비슷한 구성표를 사용하고 있습니다.

예 : http://application/products/by-date/page/2

구체적으로 : http://application/products/Date/Ascending/3

그러나 나는 이런 식으로 경로에 페이징 및 정렬 정보를 포함시키는 것에 정말로 만족하지 않습니다.

항목 목록 (이 경우 제품)은 변경 가능합니다. 즉, 다음에 누군가 페이징 및 정렬 매개 변수가 포함 된 URL로 돌아 오면 결과가 변경되었을 수 있습니다. 따라서 http://application/products/Date/Ascending/3정의되지 않은 변경되지 않는 제품 세트를 가리키는 고유 URL 이라는 아이디어 가 사라집니다.


1
여러 열을 기준으로 정렬하는 첫 번째 문제는 필자의 의견으로는 세 가지 방법 모두에 적용됩니다. 따라서 실제로 그들 중 누구에게도 찬사 / 진찰이 아닙니다. 두 번째 문제와 관련하여 어떤 자원 에도 일어날 수 없습니까? 예를 들어 제품을 편집 / 삭제할 수도 있습니다.
andi

URL이 점점 커지고 관리 할 수 ​​없기 때문에 여러 열을 기준으로 정렬하는 것이 실제로 3 가지 방법 모두에 대한 '콘'이라고 생각합니다. 두 번째 문제에서는 일시적인 제품 목록보다 제품 ID와 같은 고유 영구 식별자 사이에 근본적인 개념적 차이가 있다고 생각합니다. 삭제 된 제품의 경우 '시스템에 해당 제품이 없습니다'와 같은 메시지가 해당 제품에 대한 구체적인 정보를 알려줍니다.
Steve Willcock

1
경로에서 모든 페이징 및 정렬 정보를 제거하는 것이 좋습니다. 그리고 POST 매개 변수로 밀어 넣는 것은 좋지 않습니다. 여보세요? 질문은 REST에 관한 것입니다. REST에서 URL을 더 짧게 만들기 위해 POST를 사용하지 않습니다. 동사는 의미가 있습니다.
temoto

1
개인적으로, 요청에 본문이 있으므로 POST 또는 PUT HTTP 메서드가 거의 필요하기 때문에 쿼리에 폼 매개 변수를 사용하지 않습니다. GET은 POST와 PUT이 모두 리소스를 수정하는 것을 의미하므로 사용하기에 더 적절한 방법 인 것 같습니다. 이 때문에 여러 열을 기준으로 정렬해야 할 때 더 많은 쿼리 매개 변수를 URL에 추가합니다.
Paul D. Eden

1

"page"가 실제로 리소스가 아니라는 것을 slf에 동의하는 경향이 있습니다. 반면에 옵션 3은 더 깨끗하고 읽기 쉽고 사용자가 더 쉽게 추측 할 수 있으며 필요한 경우 입력 할 수도 있습니다. 옵션 1과 3 사이를 찢었지만 옵션 3을 사용하지 않는 이유는 없습니다.

또한 멋지게 보이지만 쿼리 문자열이나 URL 세그먼트 대신 숨겨진 매개 변수를 사용하는 한 가지 단점은 사용자가 특정 페이지에 책갈피를 지정하거나 직접 연결할 수 없다는 것입니다. 응용 프로그램에 따라 문제가 될 수도 있고 아닐 수도 있지만 알고 있어야 할 것이 있습니다.


1
추측하기 쉽다는 당신의 언급과 관련하여, 이것은 중요하지 않습니다. 하이퍼 미디어 API를 구축하는 경우 사용자는 URI를 추측해서는 안됩니다.
JR 가르시아

0

전에 솔루션 3을 사용했습니다 (많은 장고 앱을 작성합니다). 그리고 나는 그것에 문제가 있다고 생각하지 않습니다. 다른 두 개와 마찬가지로 생성 가능하며 (대량 긁기 등을 해야하는 경우) 깨끗해 보입니다. 또한 사용자는 URL (공개 앱인 경우)을 추측 할 수 있으며 사람들은 원하는 곳으로 바로 이동할 수 있고 URL 추측은 힘을 실어줍니다.


0

내 프로젝트에서 다음 URL을 사용합니다.

http://application/products?page=2&sort=+field1-field2

이것은 "field1을 기준으로 오름차순으로 정렬 한 다음 field2를 기준으로 내림차순으로 정렬 된 두 번째 페이지를 제공합니다"를 의미합니다. 또는 더 많은 유연성이 필요한 경우 다음을 사용합니다.

http://application/products?skip=20&limit=20&sort=+field1-field2

0

다음 페이지 레코드를 얻기 위해 다음 패턴을 사용합니다. http : // application / products? lastRecordKey =? & pageSize = 20 & sort = ASC

RecordKey는 DB의 순차 값을 보유하는 테이블의 열입니다. DB에서 한 번에 하나의 페이지 데이터 만 가져 오는 데 사용됩니다. pageSize는 가져올 레코드 수를 결정하는 데 사용됩니다. sort는 레코드를 오름차순 또는 내림차순으로 정렬하는 데 사용됩니다.

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