[http] 나머지 컬렉션에서의 페이징

JSON 문서 ( CouchDB 또는 Persevere 생각)에 직접 REST 인터페이스를 노출하는 데 관심이 있습니다. 내가 겪고있는 문제 GET는 컬렉션이 큰 경우 컬렉션 루트 에서 작업 을 처리하는 방법 입니다.

예를 들어 척하는 것처럼 Questions각 행이 문서로 노출되는 StackOverflow의 테이블을 노출하고 있습니다 (필요한 테이블이 아니라 크기가 큰 ‘문서’집합의 구체적인 예). 컬렉션에서 사용할 수있게 될 것이다 /db/questions일반적인 CRUD API를 사용하여 GET /db/questions/XXX, PUT /db/questions/XXX, POST /db/questions놀이입니다. 전체 컬렉션을 가져 오는 표준 방법은 GET /db/questions각 행을 순식간에 JSON 객체로 덤프하는 경우 다소 규모가 큰 다운로드가 가능하며 서버 측에서 많은 작업이 수행됩니다.

해결책은 물론 페이징입니다. Dojo는 사용자 정의 범위 단위로 헤더를 사용하는 영리한 RFC2616 호환 확장을 통해 JsonRestStore 에서이 문제점을 해결했습니다 . 결과는 요청 된 범위 만 반환합니다. 쿼리 매개 변수에 비해이 방법의 장점은 쿼리 문자열을 쿼리에 남겨 두는 것입니다 (예 : 또는 예를 들어 , 인코딩 된 예 ).Rangeitems206 Partial ContentGET /db/questions/?score>200%3E

이 접근법은 내가 원하는 행동을 완전히 다룹니다. 문제는 RFC 2616 이 206 응답 (강조 광산)에서 다음을 지정한다는 것입니다.

요청 범위 헤더 필드 (반드시 포함했다 섹션 14.35 ) 원하는 범위를 나타내는, 및 IF-Range 헤더 필드 (포함되어있을 수 부 14.27 요구 조건을하기 참조).

이것은 헤더의 표준 사용법과 관련이 있지만 206 응답이 순진한 클라이언트 / 무작위 사람들이 탐색하는 기본값을 원하기 때문에 문제가됩니다.

솔루션을 찾기 위해 RFC를 자세히 살펴 보았지만 솔루션에 만족하지 않아서 SO가 문제를 해결하는 데 관심이 있습니다.

내가 가진 아이디어 :

  • 돌아 200Content-Range헤더! -나는 이것이 틀렸다고 생각하지 않지만 응답이 부분 내용 일뿐이라는 더 명확한 지표를 선호합니다.
  • 반환400 Range Required -필요한 헤더에 대한 특수 400 응답 코드가 없으므로 기본 오류를 직접 읽고 읽어야합니다. 또한 웹 브라우저 (또는 Resty와 같은 다른 클라이언트)를 통한 탐색이 더 어려워집니다.
  • 쿼리 매개 변수를 사용하십시오 -표준 접근 방식이지만 쿼리를 일시적으로 허용하기를 희망하며 쿼리 네임 스페이스가 줄어 듭니다.
  • 그냥 돌아와 206! -대부분의 고객은 놀라지 않을 것이라고 생각하지만 RFC에서 반드시 필히 반대하지는 않습니다.
  • 사양을 확장하십시오! 반환266 Partial Content -206과 똑같이 동작하지만 Range헤더를 포함해서는 안되는 요청에 대한 응답 입니다. 나는 266이 충돌 문제에 부딪치지 않을 정도로 충분히 높으며 나에게 의미가 있지만 이것이 금기 사항인지 아닌지는 확실하지 않습니다.

나는 이것이 상당히 일반적인 문제라고 생각하고 나는 이것이 일종의 사실상의 방식으로 이루어지기를 원하기 때문에 나 또는 다른 사람이 바퀴를 재발 명하지 않습니다.

컬렉션이 클 때 HTTP를 통해 전체 컬렉션을 노출하는 가장 좋은 방법은 무엇입니까?



답변

내 생각은 HTTP 범위 확장이 사용 사례에 맞게 설계되지 않았으므로 시도하지 않아야한다는 것입니다. 부분적인 응답을 의미 206하고, 206클라이언트가 요청하는 경우에만 보내야합니다.

Atom에서 사용하는 것과 같은 다른 접근 방식을 고려할 수도 있습니다 (설계에 의한 표현이 부분적 일 수 있으며 상태 200및 잠재적으로 페이징 링크 로 리턴 됨 ). RFC 4287RFC 5005를 참조하십시오 .


답변

나는 당신들 중 일부와 동의하지 않습니다. REST 서비스를 위해이 기능을 몇 주 동안 사용해 왔습니다. 내가 한 일은 정말 간단합니다. 내 솔루션은 REST 사람들이 컬렉션이라고 부르는 것에 대해서만 의미가 있습니다.

클라이언트는 컬렉션의 어느 부분이 필요한지 나타 내기 위해 “범위”헤더를 포함해야하거나 요청 된 컬렉션이 너무 커서 단일 라운드 트립으로 검색 할 수 없을 때 413 요청 된 엔티티가 너무 큼 오류를 처리 할 준비가되어 있어야합니다.

서버는 206 PARTIAL CONTENT 응답을 전송합니다. 전송 된 리소스 부분을 지정하는 Content-Range 헤더와 컬렉션의 현재 버전을 식별하는 ETag 헤더가 있습니다. 나는 보통 페이스 북과 같은 ETag {last_modification_timestamp}-{resource_id}를 사용하며 컬렉션의 ETag는 가장 최근에 수정 된 리소스의 ETag라고 생각합니다.

컬렉션의 특정 부분을 요청하려면 클라이언트는 “Range”헤더를 사용해야하며, 동일한 컬렉션의 다른 부분을 얻기 위해 이전에 수행 된 요청에서 얻은 컬렉션의 ETag로 “If-Match”헤더를 채워야합니다. 따라서 서버는 요청 된 부분을 보내기 전에 컬렉션이 변경되지 않았는지 확인할 수 있습니다. 최신 버전이 존재하면 클라이언트가 콜렉션을 처음부터 검색하도록 초대하기 위해 412 PRECONDITION FAILED 응답이 리턴됩니다. 이는 현재 요청 된 파트 이전 또는 이후에 일부 자원이 추가되거나 제거되었을 수 있기 때문에 필요합니다.

캐시를 최적화하기 위해 Last-Modified / If-Unmodified-Since와 함께 ETag / If-Match를 사용합니다. 브라우저와 프록시는 캐싱 알고리즘을 위해 둘 중 하나 또는 둘 모두에 의존 할 수 있습니다.

검색 / 필터 쿼리를 포함하지 않으면 URL이 깨끗해야한다고 생각합니다. 당신이 그것에 대해 생각하면, 검색은 컬렉션의 부분보기에 지나지 않습니다. cars / search? q = BMW 유형의 URL 대신 더 많은 cars? manufacturer = BMW가 표시됩니다.


답변

여전히 응답 코드 Accept-RangesContent-Ranges함께 리턴 할 수 있습니다 200. 이 두 응답 헤더 는 응답 코드가 명시 적으로 제공 하는 것과 동일한 정보 를 유추 할 수 있는 충분한 정보를 206제공합니다.

Range페이지 매김에 사용 200하고 평범 하게 for 를 반환하도록 합니다 GET.

이 100 % 편안하고 느낌 더 어려워을 검색하지 않습니다.

편집 : 나는 이것에 관한 블로그 게시물을 썼습니다 : http://otac0n.com/blog/2012/11/21/range-header-i-choose-you.html


답변

응답 페이지가 두 개 이상이고 전체 컬렉션을 한 번에 제공하지 않으려면 여러 선택이 가능합니까?

에 대한 요청에서 URL 목록이 포함 된 JSON 객체 또는 HTML 페이지뿐만 아니라 각 페이지로 이동하는 방법을 지정하는 헤더 와 함께 /db/questions반환 합니다.300 Multiple ChoicesLink

Link: <>; rel="http://paged.collection.example/relation/paged"
Link: <>; rel="http://paged.collection.example/relation/paged"
...

Link각 결과 페이지에 대해 하나의 헤더가 있으며 (빈 문자열은 현재 URL을 의미하고 URL은 각 페이지마다 동일하며 방금 다른 범위로 액세스 됨) 관계는 다음 Link사양에 따라 사용자 지정으로 정의됩니다 . 이 관계는 귀하의 관습 266또는 위반 사항을 설명합니다 206. 이러한 헤더는 모두 컴퓨터에서 읽을 수있는 버전입니다. 모든 예제에는 이해 클라이언트가 필요하기 때문입니다.

( “범위”라우트를 고수하는 경우 2xx설명 된대로 자신의 리턴 코드가 여기에서 가장 좋은 동작이라고 생각합니다. 애플리케이션에 대해이 작업을 수행해야하며 [ “HTTP 상태 코드는 확장 가능합니다. “], 그럴만한 이유가 있습니다.)

300 Multiple Choices또한 사용자 에이전트가 선택할 수있는 방법을 본문에 제공해야합니다. 고객이 이해하고 있다면 Link헤더를 사용해야합니다 . 사용자가 수동으로 탐색하는 경우 URL을 기반으로 특정 페이지 렌더링을 처리 할 수있는 특수한 “페이징 된”루트 리소스에 대한 링크가있는 HTML 페이지일까요? /humanpage/1/db/questions아니면 그런 끔찍한?


Richard Levasseur의 게시물에 대한 의견은 추가 옵션 인 Accept헤더 (14.1 절)를 상기시킵니다. oEmbed 스펙이 나왔을 때 HTTP를 사용하여 왜 완전히 수행되지 않았는지 궁금해하고 대안을 작성했습니다.

300 Multiple ChoicesLink초기 순진 HTTP에 대한 헤더와 HTML 페이지 GET, 오히려 사용 범위보다, 새로운 페이징 관계가 사용 정의가 Accept헤더를. 후속 HTTP 요청은 다음과 같습니다.

GET /db/questions HTTP/1.1
Host: paged.collection.example
Accept: application/json;PagingSpec=1.0;page=1

Accept헤더는 해당 유형 (페이지 번호)에 대한 허용 내용 유형 (당신의 JSON 리턴), 플러스 확장 매개 변수를 정의 할 수 있습니다. 내 oEmbed 쓰기에서 내 노트를 찢어 내면 (여기에 링크 할 수 없으며 내 프로파일에 나열 할 것입니다) page매개 변수의 의미 를 재정의해야 할 경우 매우 명시 적이며 여기에 사양 / 관련 버전을 제공 할 수 있습니다 앞으로.


답변

편집하다:

그것에 대해 조금 더 생각한 후에 Range 헤더가 페이지 매김에 적합하지 않다는 것에 동의합니다. 논리는 Range 헤더는 응용 프로그램이 아니라 서버의 응답을위한 것입니다. 100MB의 결과를 제공했지만 서버 (또는 클라이언트)가 한 번에 1MB 만 처리 할 수 ​​있다면 Range 헤더의 용도입니다.

또한 리소스의 하위 집합이 자체 리소스 (관계형 대수와 유사)라는 의견에 동의하므로 URL로 표현할 가치가 있습니다.

그래서 기본적으로 헤더 사용에 대한 원래의 대답 (아래)을 언급합니다.


나는 당신이 당신의 자신의 질문에 대답했다고 생각합니다. 콘텐츠 범위와 함께 200 또는 206을 반환하고 선택적으로 쿼리 매개 변수를 사용하십시오. 사용자 에이전트와 컨텐츠 유형을 스니핑하고 그에 따라 쿼리 매개 변수를 확인합니다. 그렇지 않으면 범위 헤더가 필요합니다.

본질적으로 상충되는 목표가 있습니다. 사용자가 브라우저를 사용하여 탐색 (맞춤 헤더를 쉽게 허용하지 않음)하거나 헤더를 설정할 수있는 특수 클라이언트를 사용하도록 강요 할 수 있습니다 (탐색 할 수 없음).

요청에 따라 특수 클라이언트를 제공 할 수 있습니다-일반 브라우저처럼 보이는 경우 페이지를 렌더링하고 필요한 헤더를 설정하는 작은 ajax 앱을 보내십시오.

물론, URL이 이런 종류의 일에 필요한 모든 상태를 포함해야하는지에 대한 논쟁도 있습니다. 일부에서는 헤더를 사용하여 범위를 지정하는 것이 “안식하지 않은”것으로 간주 될 수 있습니다.

또한 서버가 “Can-Specify : Header1, header2″헤더로 응답 할 수 있고 웹 브라우저가 UI를 제공하여 사용자가 원하는 경우 값을 채울 수 있다면 좋을 것입니다.


답변

Atom Feed Protocol과 같은 모델을 사용하면 정상적인 HTTP 모델 콜렉션과이를 조작하는 방법 (제대로 WebDAV를 의미 함)이 있으므로이 모델을 사용하는 것이 좋습니다.

콜렉션 모델 및 REST 조작을 정의 하는 Atom Publishing Protocol이 있으며 RFC 5005-Feed Paging and Archiving 을 사용하여 대규모 콜렉션을 페이징 할 수 있습니다 .

Atom XML에서 JSON 컨텐츠로 전환해도 아이디어에 영향을 미치지 않습니다.


답변

여기서 진짜 문제는 413-Requested Entity Too Large에 직면했을 때 자동 리디렉션을 수행하는 방법을 알려주는 사양이 없다는 것입니다.

나는 최근 에이 같은 문제로 어려움을 겪고 있었고 RESTful Web Services 책 에서 영감을 찾았습니다 . 개인적으로 헤더 요구 사항으로 인해 206이 적절하지 않다고 생각합니다. 저의 생각은 저를 300으로 이끌어주었습니다. 다른 MIME 유형에 더 적합하다고 생각했기 때문에 377 페이지 부록 B의 주제에 대해 Richardson과 Ruby가 말한 내용을 찾아 보았습니다. 표현은 기본적으로 300이어야한다는 개념을 무시하고 200으로 다시 보냅니다.

그것은 또한 우리가 원자에서 가지고있는 다음 자원들에 대한 링크라는 개념과 함께 떨립니다. 내가 구현 한 솔루션은 내가 보내고 있던 json 맵에 “다음”및 “이전”키를 추가하여 수행했습니다.

나중에 내가 생각하기 시작한 것은 307-/ db / questions / 1,25와 같은 링크로 307-임시 리디렉션을 보내는 것입니다.이 링크는 원래 URI를 표준 자원 이름으로 남겨 둡니다. 적절하게 명명 된 종속 자원. 이것은 413에서보고 싶은 행동이지만 307은 좋은 타협으로 보입니다. 실제로 코드에서 아직 시도하지 않았습니다. 더 좋은 방법은 리디렉션이 가장 최근에 질문 한 실제 ID가 포함 된 URL로 리디렉션하는 것입니다. 예를 들어, 각 질문에 정수 ID가 있고 시스템에 100 개의 질문이 있고 가장 최근에 열 개의 질문을 표시하려는 경우 / db / questions에 대한 요청은 / db / questions / 100,91로 307해야합니다.

질문 해 주셔서 감사합니다. 당신은 내가 그것에 대해 생각하는 데 며칠을 보낸 견과가 아니라고 확인했습니다.