RESTful 검색 / 필터링을 설계하는 방법은 무엇입니까? [닫은]


456

현재 PHP에서 RESTful API를 디자인하고 구현하고 있습니다. 그러나 초기 디자인을 구현하지 못했습니다.

GET /users # list of users
GET /user/1 # get user with id 1
POST /user # create new user
PUT /user/1 # modify user with id 1
DELETE /user/1 # delete user with id 1

지금까지 꽤 표준 적인가?

내 문제는 첫 번째 문제입니다 GET /users. 요청 본문에서 매개 변수를 보내 목록을 필터링하는 것을 고려하고있었습니다. 이것은 다음과 같이 슈퍼 긴 URL을 얻지 않고도 복잡한 필터를 지정할 수 있기를 원하기 때문입니다.

GET /users?parameter1=value1&parameter2=value2&parameter3=value3&parameter4=value4

대신 나는 다음과 같은 것을 원했습니다.

GET /users
# Request body:
{
    "parameter1": "value1",
    "parameter2": "value2",
    "parameter3": "value3",
    "parameter4": "value4"
}

훨씬 더 읽기 쉽고 복잡한 필터를 설정할 수 있습니다.

어쨌든 file_get_contents('php://input')요청에 대한 GET요청 본문을 반환하지 않았습니다 . 나는 또한 시도했지만 http_get_request_body(), 내가 사용하는 공유 호스팅에는 없습니다 pecl_http. 어쨌든 도움이 될지 확실하지 않습니다.

나는 이 질문을 발견 하고 GET이 요청 본문을 가지고 있지 않다는 것을 깨달았습니다. 그것은 결정적이지 못했지만 반대했습니다.

그래서 지금 무엇을 해야할지 모르겠습니다. RESTful 검색 / 필터링 기능을 어떻게 설계합니까?

나는 사용할 수 있다고 가정 POST하지만 그다지 RESTful하지는 않습니다.



60
조심해!!! GET 메소드는 IDEMPOTENT 여야하며 "캐시 가능"해야합니다. 본문에 정보를 보내는 경우 시스템은 어떻게 요청을 캐시 할 수 있습니까? HTTP는 요청 본문이 아닌 URL 만 사용하여 GET 요청을 캐시 할 수 있습니다. 예를 들어, 다음 두 요청 : example.com {test : "some"} example.com {anotherTest : "some2"}는 캐시 시스템에서 동일하게 간주됩니다. 둘 다 정확히 동일한 URL
을가집니다

15
추가하기 위해서는 / user (단일 사용자)가 아닌 / users (컬렉션)에 POST해야합니다.
Mladen B.

1
고려해야 할 또 다른 요점은 대부분의 앱 서버에 URL을 기록하는 액세스 로그가 있으므로 그 사이에있을 수 있습니다. 따라서 GET에 의도하지 않은 정보 유출이있을 수 있습니다.
user3206144

답변:


397

RESTful 검색을 구현하는 가장 좋은 방법은 검색 자체를 리소스로 간주하는 것입니다. 그런 다음 검색을 작성 중이므로 POST 동사를 사용할 수 있습니다. POST를 사용하기 위해 문자 그대로 데이터베이스에 무언가를 만들 필요는 없습니다.

예를 들면 다음과 같습니다.

Accept: application/json
Content-Type: application/json
POST http://example.com/people/searches
{
  "terms": {
    "ssn": "123456789"
  },
  "order": { ... },
  ...
}

사용자 관점에서 검색을 작성 중입니다. 이것의 구현 세부 사항은 관련이 없습니다. 일부 RESTful API는 지속성이 필요하지 않을 수도 있습니다. 그것은 구현 세부 사항입니다.


209
검색 엔드 포인트에 POST 요청을 사용하는 데있어 한 가지 중요한 제한 사항은 책갈피를 지정할 수 없다는 것입니다. 검색 결과 (특히 복잡한 쿼리)를 즐겨 찾기에 추가하면 매우 유용 할 수 있습니다.
couchand

73
POST를 사용하여 검색하면 REST 캐시 제한 조건이 깨질 수 있습니다. whatisrest.com/rest_constraints/cache_excerps
Filipe

55
검색은 본질적으로 일시적입니다. 동일한 매개 변수를 사용하여 두 검색 사이에서 데이터가 진화하므로 GET 요청이 검색 패턴에 명확하게 매핑되지 않는다고 생각합니다. 대신 검색 요청은 POST (/ Resource / search) 여야하며, 검색을 저장하고 검색 결과 (예 : / Resource / search / iyn3zrt)로 리디렉션 할 수 있습니다. 그렇게하면 GET 요청이 성공하고 이해됩니다.
sleblanc

32
게시물이 검색에 적합한 방법이라고 생각하지 않습니다. 일반적인 GET 요청에 대한 데이터도 시간이 지남에 따라 달라질 수 있습니다.
경이

82
이것은 절대적으로 가능한 최악의 답변입니다. 나는 그것이 너무 많은 공언을 가지고 있다고 믿을 수 없다. 이 답변은 이유를 설명합니다 : programmers.stackexchange.com/questions/233164/…
richard

141

GET 요청에서 요청 본문을 사용하는 경우 캐시 시스템은 URL 만 사용하므로 GET 요청을 캐시 할 수 없으므로 REST 원칙을 위반하는 것입니다.

더 나쁜 것은 URL에 사용자를이 페이지로 리디렉션하는 데 필요한 모든 정보가 포함되어 있지 않기 때문에 URL에 책갈피를 지정할 수 없습니다.

요청 본문 매개 변수 대신 URL 또는 쿼리 매개 변수를 사용하십시오.

예 :

/myapp?var1=xxxx&var2=xxxx
/myapp;var1=xxxx/resource;var2=xxxx 

실제로 HTTP RFC 7231은 다음과 같이 말합니다.

GET 요청 메시지 내의 페이로드에는 정의 된 의미가 없습니다. GET 요청에서 페이로드 본문을 전송하면 일부 기존 구현에서 요청을 거부 할 수 있습니다.

자세한 내용은 여기참조하십시오


29
내 실수에서 배운다-나는 수용 된 답변의 제안 (POSTing json)을 사용하여 API를 설계했지만 URL 매개 변수로 이동하고 있습니다. 생각보다 북마크 기능이 더 중요 할 수 있습니다. 제 경우에는 특정 검색어 (광고 캠페인)로 트래픽을 보내야했습니다. 또한 히스토리 API를 사용하면 URL 매개 변수에 더 적합합니다.
Jake

2
사용 방법에 따라 다릅니다. 해당 매개 변수를 기반으로 페이지를로드하는 URL에 링크하는 경우 의미가 있지만 기본 페이지가 필터 매개 변수를 기반으로 데이터를 가져 오기 위해 AJAX 호출을 수행하는 경우 어쨌든 북마크 할 수 없습니다. 아약스 호출 및 베어링이 없습니다. 당연히, 당신이 갈 때 필터를 빌드하고 그것을 ajax 호출에 POST하는 URL을 북마크 할 수 있으며 잘 작동합니다.
Daniel Lorenz

@DanielLorenz 최상의 사용자 환경을 유지하려면이 경우에도 History API를 통해 URL을 변경해야합니다. 웹 사이트에서 브라우저 뒤로 기능을 사용하여 이전 페이지로 이동할 수없는 경우에는 참을 수 없습니다. 그리고 표준 서버 측에서 생성 된 페이지 인 경우 책갈피 가능하게 만드는 유일한 방법은 GET 요청을 사용하는 것입니다. 좋은 ol '쿼리 매개 변수가 최상의 솔루션 인 것 같습니다.
Nathan

@Nathan이 답변을 잘못 읽은 것 같습니다. get에서 쿼리 문자열 매개 변수를 사용하는 것에 대해 이야기했습니다. GET 호출에서 본문 매개 변수를 사용하면 안됩니다. 쿼리 문자열을 사용하거나 북마크 할 수있는 GET에 대해 자세히 이야기 한 다음 페이지 시작시 해당 매개 변수를 사용하여 POST에 필터를 작성하고 해당 매개 변수를 사용하여 데이터를 가져올 수 있습니다. 그 시나리오에서 역사는 여전히 잘 작동 할 것입니다.
Daniel Lorenz

@DanielLorenz 아 괜찮습니다. 나는 당신이 말한 것을 오해 한 것 같습니다.
Nathan

70

리소스 필터링 / 검색은 RESTful 방식으로 구현 될 수있는 것 같습니다. 아이디어는 /filters/or 라는 새로운 엔드 포인트를 도입하는 것입니다 /api/filters/.

이 엔드 포인트 필터를 사용 하는 것은 자원으로 간주 될 수 있으므로 POST메소드 를 통해 작성됩니다 . 물론이 방법으로 body를 사용하여 모든 매개 변수를 전달할 수있을뿐만 아니라 복잡한 검색 / 필터 구조를 만들 수 있습니다.

이러한 필터를 만든 후 검색 / 필터 결과를 얻을 수있는 두 가지 가능성이 있습니다.

  1. 201 Created상태 코드 와 함께 고유 한 ID를 가진 새로운 리소스가 반환됩니다 . 그런 다음이 ID를 사용하여 다음 과 같은 GET요청을 할 수 있습니다 /api/users/.

    GET /api/users/?filterId=1234-abcd
    
  2. 를 통해 새 필터를 만든 후에는을 가리키는 헤더 와 함께 POST응답하지 않고 201 Created한 번에 응답 합니다 . 이 리디렉션은 기본 라이브러리를 통해 자동으로 처리됩니다.303 SeeOtherLocation/api/users/?filterId=1234-abcd

두 시나리오 모두 필터링 된 결과를 얻으려면 두 가지 요청이 필요합니다. 이는 특히 모바일 애플리케이션의 단점으로 간주 될 수 있습니다. 모바일 애플리케이션의 경우 단일 POST호출을 사용 합니다 /api/users/filter/.

생성 된 필터를 유지하는 방법?

그것들은 DB에 저장되어 나중에 사용할 수 있습니다. 또한 일부 임시 저장소 (예 : redis)에 저장할 수 있으며 TTL이 만료 된 후 만료되어 제거됩니다.

이 아이디어의 장점은 무엇입니까?

필터, 필터링 된 결과는 캐시 가능하며 북마크가 가능합니다.


2
글쎄, 이것은 받아 들일만한 대답이어야합니다. REST 원칙을 위반하지 않으며 리소스에 대해 오랫동안 복잡한 쿼리를 수행 할 수 있습니다. 멋지고 깨끗하며 북마크와 호환됩니다. 유일한 추가 단점은 생성 된 필터에 대한 키 / 값 쌍과 이미 언급 된 두 가지 요청 단계를 저장해야한다는 것입니다.
dantebarba

2
이 방법의 유일한 관심사는 쿼리에 날짜-시간 필터가 있거나 지속적으로 변경되는 값인 경우입니다. 그런 다음 db (또는 캐시)에 저장할 필터 수는 셀 수 없습니다.
Rvy Pandey

17

요청 매개 변수를 사용해야하지만 원하는 HTTP 헤더가 없으면 원하는 작업을 수행해야합니다. HTTP 사양은 명시 적으로 GET은 몸을 가질 수 없습니다, 말을하지 않습니다. 그러나이 백서 에서는 다음과 같이 설명합니다.

관례 적으로 GET 메소드를 사용할 때 자원을 식별하는 데 필요한 모든 정보가 URI로 인코딩됩니다. 클라이언트가 URI의 쿼리 부분이 아닌 HTTP 엔터티 본문에서 서버에 데이터를 제공하는 안전한 상호 작용 (예 : 검색)에 대한 HTTP / 1.1의 규칙은 없습니다. 이는 안전한 작업을 위해 URI가 길 수 있음을 의미합니다.


5
ElasticSearch는 또한 body와 함께 GET을 수행하고 잘 작동합니다!
Tarun Sapra

예. 그러나 웹 구현에서는 서버 구현이 제어되지 않을 수 있습니다.
user432024

7

laravel / php 백엔드를 사용함에 따라 다음과 같은 경향이 있습니다.

/resource?filters[status_id]=1&filters[city]=Sydney&page=2&include=relatedResource

PHP는 []매개 변수를 자동으로 배열로 변환 하므로이 예제 $filter에서는 페이지 및 필자가로드하려는 관련 리소스와 함께 필터의 배열 / 객체를 보유하는 변수로 끝납니다 .

다른 언어를 사용하는 경우 이는 여전히 좋은 규칙 일 수 있으며 구문 분석기를 작성 []하여 배열 로 변환 할 수 있습니다 .


이 방법은 훌륭해 보이지만 URL에 대괄호를 사용하는 데 문제가있을 수 있습니다. URL에서 어떤 문자를 사용할 수 있는지
Sky

2
@sky 이것은 인코딩 URI에 의해 피할 수 []. 이러한 문자의 인코딩 된 표현을 사용하여 쿼리 매개 변수를 그룹화하는 것은 잘 알려진 관행입니다. JSON : API 사양 에서도 사용됩니다 .
jelhan

6

초기 API가 완전히 RESTful인지 아닌지 (특히 알파 단계에있을 때) 너무 걱정하지 마십시오. 백엔드 배관을 먼저 작동 시키십시오. 광범위한 종류의 테스트 ( "베타")를 위해 충분히 안정적인 것을 얻을 때까지 반복해서 수정하여 URL 변환 / 재 작성을 수행 할 수 있습니다.

URI에 대한 위치 및 규칙에 따라 매개 변수가 인코딩되는 URI를 정의 할 수 있으며, 항상 무언가에 맵핑 할 경로가 앞에 붙습니다. PHP를 모르지만 그러한 기능이 존재한다고 가정합니다 (웹 프레임 워크가있는 다른 언어로 존재 함).

즉. 상점 # 1의 i = 1..4에 대해 param [i] = value [i]를 사용하여 "사용자"검색 유형을 수행하십시오 (URI 쿼리 매개 변수의 약어로 value1, value2, value3, ... 사용).

1) GET /store1/search/user/value1,value2,value3,value4

또는

2) GET /store1/search/user,value1,value2,value3,value4

또는 다음과 같이 (추천하지는 않지만 나중에 자세히 설명)

3) GET /search/store1,user,value1,value2,value3,value4

옵션 1을 사용하면 접두사가 붙은 모든 URI /store1/search/user를 검색 처리기 (또는 PHP 지정)에 기본적으로 매핑 하여 store1에서 자원을 검색하도록 기본 설정합니다 (와 동일) /search?location=store1&type=user.

API에 의해 문서화되고 시행되는 규칙에 따라 매개 변수 값 1-4는 쉼표로 구분되어 순서대로 표시됩니다.

옵션 2는 검색 유형 (이 경우 user)을 위치 매개 변수 # 1로 추가합니다. 어느 쪽이든 옵션은 단지 외관상의 선택입니다.

옵션 3도 가능하지만 마음에 들지 않습니다. 특정 리소스 내에서 검색 기능은 검색 자체보다 URI 자체에 표시되어야한다고 생각합니다 (URI에서 검색이 리소스 내에서 특정적임을 명확하게 나타내는 것처럼).

URI에서 매개 변수를 전달하는 것보다이 방법의 장점은 검색이 URI의 일부라는 것입니다 (따라서 검색은 자원, 시간이 지남에 따라 내용이 변할 수있는 자원으로 처리됩니다). 매개 변수 순서는 필수라는 단점이 있습니다. .

이와 같은 작업을 수행하면 GET을 사용할 수 있으며 읽기 전용 리소스가됩니다 (POST 또는 PUT을 사용할 수 없기 때문에 GET을 수행하면 업데이트 됨). 호출 될 때만 존재하는 자원이기도합니다.

또한 일정 시간 동안 결과를 캐싱하거나 DELETE를 사용하여 캐시를 삭제함으로써 더 많은 의미를 추가 할 수 있습니다. 그러나 이것은 사람들이 일반적으로 DELETE를 사용하는 것과 반대가 될 수 있습니다 (사람들은 일반적으로 캐싱 헤더로 캐싱을 제어하기 때문에).

어떻게 결정 하느냐는 디자인 결정일 것이지만, 이것이 내가 갈 길입니다. 완벽하지는 않으며, 이렇게하는 것이 최선의 방법이 아닌 경우가있을 것입니다 (특히 매우 복잡한 검색 기준).


7
Yo, 만약 당신이 (누구든지, 누구든지) 내 대답을 내리기 위해 승인하는 것이라면, 당신이 정확히 동의하지 않는 것을 나타내는 의견을 말하면 자아를 아프게 할 것입니까? 나는 그것이 interweebz 인 것을 알고 있지만 ...;)
luis.espinal

107
나는 공감하지는 않았지만 "현재 RESTful API를 설계하고 구현하고있다"는 질문으로 시작하고 당신의 대답은 "초기 API가 완전히 RESTful인지 아닌지 너무 걱정하지 마십시오"라는 느낌으로 시작합니다. 나에게 잘못. API를 디자인하는 경우 API를 디자인하는 것입니다. 문제는 API를 설계해야하는지 여부가 아니라 API를 최상으로 설계하는 방법을 묻는 것입니다.
gardarh

14
API 시스템입니다. 백엔드 배관이 아닌 API를 먼저 작업하십시오. 첫 번째 구현은 모의 일 수 있습니다. HTTP에는 매개 변수를 전달하는 메커니즘이 있으므로 재발 명되었지만 더 나쁩니다 (이름 값 쌍 대신 정렬 된 매개 변수). 따라서 하향 투표.
Steven Herod

14
@gardarh-그렇습니다. 잘못 느껴지지만 때로는 실용적입니다. 기본 목표는 비즈니스 환경에 적합한 API를 설계하는 것입니다. 완전한 RESTFULL 접근 방식이 현재 비즈니스에 적합한 경우 사용하십시오. 그렇지 않은 경우에는 가지 마십시오. 즉, 특정 비즈니스 요구 사항을 충족하는 API를 설계하십시오. 기본 요구 사항으로 RESTfull을 만들려고 시도하는 것은 "X / Y 문제에서 어댑터 패턴을 어떻게 사용합니까?"를 묻는 것과 크게 다르지 않습니다. 실제적이고 귀중한 문제를 해결하지 않는 한 경적 패러다임을 밟지 마십시오.
luis.espinal

1
리소스를 상태 모음으로 간주하고 매개 변수를 해당 상태의 표현을 조작하는 수단으로 사용합니다. 노브와 스위치를 사용하여 리소스가 표시되는 방식을 조정하고 (특정 비트를 표시 / 숨기거나 다르게 정렬하는 등) 이러한 컨트롤은 매개 변수입니다. 실제로 다른 리소스 (예 : '/ 앨범'대 '/ 아티스트') 인 경우 경로에 표시되어야합니다. 어쨌든 그것은 직관적입니다.
Eric Elliott

2

참고 :이 내용은 약간 늦었지만 관심있는 사람에게는 적합합니다. RESTful 방법에 따라 HTTP 사양이 명확하지 않으므로 자체 필터링 전략을 구현해야합니다. 모든 필터 매개 변수를 URL 인코딩으로 제안하고 싶습니다.

GET api/users?filter=param1%3Dvalue1%26param2%3Dvalue2

나는 그것이 추악하다는 것을 알고 있지만 그것을하는 가장 편안한 방법이라고 생각하고 서버 측에서 쉽게 파싱해야합니다. :)

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