RESTful API에서 페이지 매김을 지원하고 싶습니다.
내 API 메서드는 .NET을 통해 제품의 JSON 목록을 반환해야합니다 /products/index
. 그러나 잠재적으로 수천 개의 제품이 있으며 페이지를 넘기고 싶으므로 내 요청은 다음과 같아야합니다.
/products/index?page_number=5&page_size=20
하지만 내 JSON 응답은 어떤 모습이어야합니까? API 소비자는 일반적으로 응답에서 페이지 매김 메타 데이터를 기대합니까? 아니면 다양한 제품 만 필요합니까? 왜?
Twitter의 API에 메타 데이터가 포함되어있는 것 같습니다 : https://dev.twitter.com/docs/api/1/get/lists/members (예제 요청 참조).
메타 데이터 사용 :
{
"page_number": 5,
"page_size": 20,
"total_record_count": 521,
"records": [
{
"id": 1,
"name": "Widget #1"
},
{
"id": 2,
"name": "Widget #2"
},
{
"id": 3,
"name": "Widget #3"
}
]
}
제품 배열 (메타 데이터 없음) :
[
{
"id": 1,
"name": "Widget #1"
},
{
"id": 2,
"name": "Widget #2"
},
{
"id": 3,
"name": "Widget #3"
}
]
답변
ReSTful API는 주로 다른 시스템에서 사용하기 때문에 응답 헤더에 페이징 데이터를 넣습니다. 그러나 일부 API 소비자는 응답 헤더에 직접 액세스 할 수 없거나 API를 통해 UX를 구축 할 수 있으므로 요청시 JSON 응답에서 메타 데이터를 검색하는 방법을 제공하는 것이 장점입니다.
구현에는 기계가 읽을 수있는 메타 데이터가 기본으로 포함되어야하고 요청시 사람이 읽을 수있는 메타 데이터가 포함되어야한다고 생각합니다. 사람이 읽을 수있는 메타 데이터는 원하는 경우 모든 요청과 함께 반환 될 수 있으며, 가급적이면 include=metadata
또는 같은 쿼리 매개 변수를 통해 필요에 따라 반환 될 수 있습니다.include_metadata=true
.
특정 시나리오에서는 레코드와 함께 각 제품의 URI를 포함합니다. 이렇게하면 API 소비자가 개별 제품에 대한 링크를 쉽게 만들 수 있습니다. 또한 페이징 요청의 한계에 따라 합리적인 기대치를 설정했습니다. 페이지 크기에 대한 기본 설정을 구현하고 문서화하는 것이 허용되는 방법입니다. 예를 들어 GitHub의 API 는 기본 페이지 크기를 최대 100 개의 레코드 30 개로 설정하고 API를 쿼리 할 수있는 횟수에 대한 속도 제한을 설정합니다. API에 기본 페이지 크기가있는 경우 쿼리 문자열은 페이지 색인 만 지정할 수 있습니다.
사람이 읽을 수있는 시나리오에서로 이동할 때 /products?page=5&per_page=20&include=metadata
응답은 다음과 같을 수 있습니다.
{
"_metadata":
{
"page": 5,
"per_page": 20,
"page_count": 20,
"total_count": 521,
"Links": [
{"self": "/products?page=5&per_page=20"},
{"first": "/products?page=0&per_page=20"},
{"previous": "/products?page=4&per_page=20"},
{"next": "/products?page=6&per_page=20"},
{"last": "/products?page=26&per_page=20"},
]
},
"records": [
{
"id": 1,
"name": "Widget #1",
"uri": "/products/1"
},
{
"id": 2,
"name": "Widget #2",
"uri": "/products/2"
},
{
"id": 3,
"name": "Widget #3",
"uri": "/products/3"
}
]
}
컴퓨터에서 읽을 수있는 메타 데이터의 경우 응답에 링크 헤더 를 추가 합니다.
Link: </products?page=5&perPage=20>;rel=self,</products?page=0&perPage=20>;rel=first,</products?page=4&perPage=20>;rel=previous,</products?page=6&perPage=20>;rel=next,</products?page=26&perPage=20>;rel=last
(링크 헤더 값은 urlencoded 여야합니다)
… 그리고 total-count
원하는 경우 사용자 지정 응답 헤더가있을 수 있습니다.
total-count: 521
링크 헤더가 내가있는 페이지와 페이지 당 수를 알려주고 배열의 레코드 수를 빠르게 검색 할 수 있기 때문에 인간 중심 메타 데이터에 공개 된 다른 페이징 데이터는 기계 중심 메타 데이터에 불필요 할 수 있습니다. . 따라서 총 개수에 대한 헤더 만 만들 것입니다. 나중에 언제든지 마음을 바꾸고 더 많은 메타 데이터를 추가 할 수 있습니다.
제쳐두고, 내가 /index
당신의 URI에서 제거되었음을 알 수 있습니다 . 일반적으로 허용되는 규칙은 ReST 엔드 포인트가 컬렉션을 노출하도록하는 것입니다. 데 /index
그보다 약간 끝 muddies에.
이것들은 API를 사용 / 생성 할 때 제가 좋아하는 몇 가지 것입니다. 도움이 되었기를 바랍니다.
답변
REST 서비스를 사용하기 위해 여러 라이브러리를 작성한 사람으로서 메타 데이터로 결과를 래핑하는 것이 왜 좋은지에 대한 클라이언트 관점을 제공하겠습니다.
- 총 개수가없는 경우 클라이언트는 아직 모든 항목을받지 못했고 결과 집합을 통해 계속 페이징해야한다는 것을 어떻게 알 수 있습니까? 다음 페이지로 미리보기를 수행하지 않은 UI에서 최악의 경우 실제로 더 이상 데이터를 가져 오지 않은 다음 / 추가 링크로 표시 될 수 있습니다.
- 응답에 메타 데이터를 포함하면 클라이언트가 더 적은 상태를 추적 할 수 있습니다. 이제 응답에 요청 상태를 재구성하는 데 필요한 메타 데이터가 포함되어 있으므로 REST 요청을 응답과 일치시킬 필요가 없습니다 (이 경우 데이터 세트에 대한 커서).
- 상태가 응답의 일부인 경우 동일한 데이터 세트에 대해 여러 요청을 동시에 수행 할 수 있으며 요청을 수행 한 순서가 아닐 수도있는 모든 순서로 요청을 처리 할 수 있습니다.
그리고 제안 : Twitter API 처럼 page_number를 직선 인덱스 / 커서로 바꿔야합니다. 그 이유는 API를 통해 클라이언트가 요청 당 페이지 크기를 설정할 수 있기 때문입니다. 반환 된 page_number는 클라이언트가 지금까지 요청한 페이지 수입니까, 아니면 마지막으로 사용 된 page_size가 주어진 페이지 수입니까 (거의 확실하게 나중이지만 이러한 모호함을 모두 피하지 않는 이유)?
답변
동일한 헤더를 추가하는 것이 좋습니다. 헤더에 메타 데이터를 이동하면 같은 봉투를 치우는 데 도움이 result
, data
또는 records
및 응답 본문은 우리가 필요로하는 데이터가 들어 있습니다. 페이지 매김 링크도 생성하는 경우 링크 헤더 를 사용할 수 있습니다 .
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
swagger 파일의 경우 :
답변
백엔드 API 새 속성을 응답 본문에 추가하기 만하면됩니다. .net 코어 예에서 :
[Authorize]
[HttpGet]
public async Task<IActionResult> GetUsers([FromQuery]UserParams userParams)
{
var users = await _repo.GetUsers(userParams);
var usersToReturn = _mapper.Map<IEnumerable<UserForListDto>>(users);
// create new object and add into it total count param etc
var UsersListResult = new
{
usersToReturn,
currentPage = users.CurrentPage,
pageSize = users.PageSize,
totalCount = users.TotalCount,
totalPages = users.TotalPages
};
return Ok(UsersListResult);
}
{
"usersToReturn": [
{
"userId": 1,
"username": "nancycaldwell@conjurica.com",
"firstName": "Joann",
"lastName": "Wilson",
"city": "Armstrong",
"phoneNumber": "+1 (893) 515-2172"
},
{
"userId": 2,
"username": "zelmasheppard@conjurica.com",
"firstName": "Booth",
"lastName": "Drake",
"city": "Franks",
"phoneNumber": "+1 (800) 493-2168"
}
],
// metadata to pars in client side
"currentPage": 1,
"pageSize": 2,
"totalCount": 87,
"totalPages": 44
}
답변
일반적으로 나는 간단한 방법으로 무엇을하든간에, 예를 들어 “localhost / api / method / : lastIdObtained / : countDateToReturn”과 같은 restAPI 끝점을 이러한 매개 변수로 생성합니다. 간단한 요청으로 수행 할 수 있습니다. 예를 들어 서비스에서. .그물
jsonData function(lastIdObtained,countDatetoReturn){
'... write your code as you wish..'
and into select query make a filter
select top countDatetoreturn tt.id,tt.desc
from tbANyThing tt
where id > lastIdObtained
order by id
}
Ionic에서는 아래에서 위로 스크롤하면 0 값을 전달하고 답을 얻으면 마지막으로 얻은 ID 값을 설정하고 위에서 아래로 슬라이드하면 얻은 마지막 등록 ID를 전달합니다.