REST API 모범 사례 : 매개 변수 값 목록을 입력으로 승인하는 방법 [닫기]


409

새로운 REST API를 시작하고 입력 매개 변수의 형식을 지정하는 방법에 대한 우수 사례에 대한 커뮤니티 입력을 원했습니다.

현재 API는 매우 JSON 중심적입니다 (JSON 만 반환). 우리가 XML을 반환해야하는지 여부에 대한 논쟁은 별도의 문제입니다.

API 출력이 JSON 중심이므로 입력이 약간 JSON 중심적 인 경로를 따라 가고 있으며 일반적으로 이상하지만 다소 이상 할 수 있다고 생각했습니다.

예를 들어, 현재 여러 제품을 한꺼번에 가져올 수있는 몇 가지 제품 세부 정보를 얻으려면 다음을 수행하십시오.

http://our.api.com/Product?id=["101404","7267261"]

이것을 다음과 같이 단순화해야합니까?

http://our.api.com/Product?id=101404,7267261

아니면 JSON 입력이 편리합니까? 고통이 더 있습니까?

우리는 두 가지 스타일을 모두 수용하고 싶을 수도 있지만 유연성으로 인해 실제로 혼란과 유지 보수 (유지 관리, 문서 등)가 더 많이 발생합니까?

더 복잡한 경우는 더 복잡한 입력을 제공하려는 경우입니다. 예를 들어 검색시 여러 필터를 허용하려는 경우 :

http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}

필터 유형 (예 : productType 및 color)을 요청 이름으로 다음과 같이 입력하지 않아도됩니다.

http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]

모든 필터 입력을 그룹화하고 싶었 기 때문입니다.

결국, 이것이 정말로 중요합니까? JSON 유형이 너무 많아 입력 유형이 그다지 중요하지 않을 수 있습니다.

API에 대한 AJAX 호출을 수행하는 JavaScript 클라이언트는 JSON 입력을 더 쉽게 사용할 수 있다는 점을 알고 있습니다.

답변:


341

한 걸음 물러서

무엇보다도 REST는 URI를 범용 고유 ID로 설명합니다. 너무 많은 사람들이 URI의 구조와 어떤 URI가 다른 URI보다 "휴식 적"인지에 따라 잡히고 있습니다. 이 주장은 누군가 "Bob"을 명명하는 것이 "Joe"를 명명하는 것보다 낫다고 말하는 것만 큼 성가신 일입니다. 두 사람 모두 "사람을 식별하는"직무를 수행합니다. URI는 보편적으로 고유 한 이름에 지나지 않습니다 .

따라서 REST의 눈 ?id=["101404","7267261"]에는 더 편안한 지 ?id=101404,7267261또는 \Product\101404,7267261다소 쓸데 없는지 에 대한 논쟁 이 있습니다 .

이제 URI를 구성하는 방법이 여러 번 RESTful 서비스의 다른 문제에 대한 좋은 지표가 될 수 있다고 말했습니다. URI에는 일반적으로 몇 가지 붉은 깃발이 있습니다.

제안

  1. 동일한 리소스 및 Content-Location

    우리는 두 가지 스타일을 모두 수용하고 싶을 수도 있지만 유연성으로 인해 실제로 혼란과 유지 보수 (유지 관리, 문서 등)가 더 많이 발생합니까?

    URI는 리소스를 식별합니다. 각 자원에는 하나의 표준 URI 가 있어야 합니다. 이 두 URI를 동일한 리소스를 가리 가질 수 없습니다 것을 의미하지 않는다 하지만 그 일에 대해 갈 수있는 잘 정의 된 방법이 있습니다. JSON 및 목록 기반 형식 (또는 다른 형식)을 모두 사용하기로 결정한 경우 이러한 형식 중 어떤 것이 기본 정식 URI 인지 결정해야합니다 . 동일한 "자원"을 가리키는 다른 URI에 대한 모든 응답에는 Content-Location헤더 가 포함되어야합니다 .

    유추라는 이름을 고수하면서 여러 URI를 갖는 것은 사람들의 별명을 갖는 것과 같습니다. 그것은 완벽하게 수용 가능하고 종종 매우 편리하지만, 닉네임을 사용하는 경우 여전히 그 사람의 이름을 알고 싶어 할 것입니다. 이런 식으로 누군가 "Nichloas Telsa"라는 이름으로 누군가를 언급 할 때, 나는 그들이 "Nick"이라고 언급 한 사람에 대해 이야기하고 있다는 것을 알고 있습니다.

  2. URI에서 "검색"

    더 복잡한 경우는 더 복잡한 입력을 제공하려는 경우입니다. 예를 들어 검색시 여러 필터를 허용하려는 경우 ...

    내 경험의 일반적인 규칙은 URI에 동사가 포함되어 있으면 무언가 꺼져 있다는 표시 일 수 있습니다. URI의 자원을 식별, 그러나 그들은 표시 안 무엇을 우리가 자원하고 있어요. 이것이 바로 "일관된 인터페이스"라는 HTTP 또는 편안한 용어입니다.

    URI에서 동사를 사용하는 것은 비유라는 이름의 유사점을 극복하기 위해 누군가와 상호 작용할 때 누군가의 이름을 변경하는 것과 같습니다. Bob과 상호 작용하는 경우 Bob에게 Hi라고 말하고 싶을 때 Bob의 이름이 "BobHi"가되지 않습니다. 마찬가지로 제품을 "검색"하려는 경우 URI 구조가 "/ Product / ..."에서 "/ Search / ..."로 변경되지 않아야합니다.

초기 질문에 답변

  1. ["101404","7267261"]vs 관련 101404,7267261: 여기에 제 제안은 단순성을 위해 JSON 구문을 피하는 것입니다 (즉, 사용자가 실제로 필요하지 않은 경우 URL 인코딩을 수행하지 않아도 됨). API를 좀 더 유용하게 사용할 수 있습니다. 더 나은 방법은 다른 사람들이 권장 한 application/x-www-form-urlencoded것처럼 최종 사용자에게 가장 친숙 할 수 있는 표준 형식을 사용하는 것입니다 (예 :) ?id[]=101404&id[]=7267261. "pretty"가 아닐 수도 있지만 Pretty URI가 필요한 URI를 의미하지는 않습니다. 그러나 궁극적으로 REST에 대해 말할 때 내 초기 요점을 되풀이하는 것은 중요하지 않습니다. 그것에 너무 많이 머 무르지 마십시오.

  2. 복잡한 검색 URI 예제는 제품 예제와 거의 같은 방식으로 해결할 수 있습니다. application/x-www-form-urlencoded이미 많은 사람들에게 익숙한 표준이므로 형식을 다시 사용하는 것이 좋습니다 . 또한 두 가지를 병합하는 것이 좋습니다.

URI ...

/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}    

URI 인코딩 후 URI ...

/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D

로 변환 할 수 있습니다 ...

/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red

URL 인코딩 요구 사항을 피하고 상황을 좀 더 표준으로 보이게하는 것 외에도 이제 API를 약간 균질화합니다. 사용자는 제품 또는 제품 목록을 검색하려는 경우 (모두 RESTful 용어로 단일 "자원"으로 간주 됨) /Product/...URI에 관심이 있다는 것을 알고 있습니다 .


67
후속 조치를 취하고 []구문이 항상 지원되는 것은 아닙니다 (일반적으로도 URI 사양을 위반할 수도 있음). 일부 HTTP 서버 및 프로그래밍 언어는 이름을 반복하는 것을 선호합니다 (예 :) productType=value1&productType=value2.
nategood

1
이 쿼리의 초기 질문은 다음과 같습니다. "/ Search? term = pumas & filters = {"productType ": ["Clothing ","Bags "],"color ": ["Black ","Red "]}" (productType == clothing || productType == bags) && (color == black || color == red) 해결책 : / Product? term = pumas & productType [] = Clothing & productType [] = Bags & color [] = Black & color [] = 빨간색이 다음과 같이 번역되는 것 같습니다 ... (productType == clothing || productType == bags || color == black || color == red) 또는 둘 중 하나 (productType == clothing && productType == bags && color == black && color == red) 나에게 조금 다른 것 같습니다. 아니면 내가 잘못 이해 했습니까?
Thomas Cheng

2
사후 요청의 입력은 어떻습니까? 리소스를 업데이트하고 있는지 알고 싶었다면 쿼리 / 필터와 데이터를 본문에 표준 형식으로 보내는 것은 나쁜 습관입니까? 예를 들어. api를 사용하여 /user/본문과 사용자와 관련된 데이터를 변경하려면 사용자 { q:{}, d: {} }와 함께 q쿼리로 보내 DB에서 쿼리 d하고 수정 된 데이터로 보내드립니다 .
분자

1
목록이 매우 클 경우 어떻게합니까? URI는 브라우저에 따라 길이가 제한됩니다. 나는 일반적으로 게시물 요청으로 전환하고 본문에 목록을 보냈습니다. 거기에 어떤 제안?
Troy Cosentino

4
매우 커야합니다 ( stackoverflow.com/questions/417142/… 참조). 하지만 ), 가장 극단적 인 경우 요청 본문을 사용해야 할 수도 있습니다. 데이터 검색에 대한 POST 쿼리는 RESTafarians가 토론하기를 좋아하는 것 중 하나입니다.
nategood

233

URL 매개 변수로 값 목록을 전달하는 표준 방법은 값을 반복하는 것입니다.

http://our.api.com/Product?id=101404&id=7267261

대부분의 서버 코드는이 값을 값 목록으로 해석하지만 대부분 단일 값 단순화가 있으므로 찾아야 할 수도 있습니다.

구분 된 값도 괜찮습니다.

JSON을 서버로 보내야하는 경우 URL에서 다른 형식으로 보는 것을 좋아하지 않습니다. 특히, URL에는 크기 제한이 있습니다 (실제로는 이론이 아닌 경우).

내가 복잡한 쿼리를 RESTfully하게 수행하는 방법은 두 단계로 이루어집니다.

  1. POST 쿼리 요구 사항, ID 수신 (필수적으로 검색 기준 자원 작성)
  2. GET 위의 ID를 참조하여 검색
  3. 필요에 따라 필요에 따라 쿼리 요구 사항을 삭제하지만 해당 요구 사항을 재사용 할 수 있습니다.

8
고마워 캐시. 나는 당신과 함께 있고 URL에서 JSON을 보는 것을 정말로 좋아하지 않는다고 생각합니다. 그러나 나는 고유 한 GET 작업 인 검색을 위해 게시물을 작성하는 팬이 아닙니다. 이에 대한 예를들 수 있습니까?
whatupwilly

1
쿼리가 간단한 매개 변수로 작동 할 수 있으면 그렇게하십시오. 출처는 나머지 토론 메일 링리스트에서 온 것입니다 : tech.groups.yahoo.com/group/rest-discuss/message/11578
Kathy Van Stone

2
두 가지 자료 만 보여 주려면 James Westgate의 답변이 더 일반적입니다
Kathy Van Stone

이것이 정답입니다. 가까운 장래에 OData가 지원하는 filter = id in (a, b, c, ...) 등을 보게 될 것입니다.
Bart Calixto

이것이 Akka HTTP의 작동 방식입니다.
Joan

20

먼저:

두 가지 방법으로 할 수 있다고 생각합니다

http://our.api.com/Product/<id> : 하나의 레코드 만 원하면

http://our.api.com/Product : 모든 기록을 원한다면

http://our.api.com/Product/<id1>,<id2> : James가 제안한 것처럼 Product 태그 뒤에 매개 변수가 매개 변수이기 때문에 옵션이 될 수 있습니다.

또는 내가 가장 좋아하는 것은 다음과 같습니다.

당신은 사용 할 수 있습니다 응용 프로그램 상태의 엔진으로 하이퍼 미디어를 편안 WS의 (HATEOAS) 속성과 통화 할 http://our.api.com/Product에 해당 URL을 반환해야 http://our.api.com/Product/<id>하고,이 후 그들에게 전화를.

둘째

URL 호출에서 쿼리를 수행해야 할 때. HATEOAS를 다시 사용하는 것이 좋습니다.

1) 전화 받기 http://our.api.com/term/pumas/productType/clothing/color/black

2) 전화 받기 http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3) (HATEOAS 사용)` http://our.api.com/term/pumas/productType/에 전화를 거십시오 .-> 가능한 모든 의복 URL을 받으십시오-> 원하는 것 (의류 및 가방)을 부르십시오- > 가능한 컬러 URL 수신-> 원하는 컬러 URL 호출


1
나는 며칠 전에 비슷한 상황에 처해 있었고, (HATEOAS) 휴식 API를 조정하여 필터링 된 (큰) 객체 목록을 얻고 두 번째 솔루션을 선택했습니다. 각각에 대해 조금씩 잔인한 API를 회상하지 않습니까?
Samson

시스템에 따라 다릅니다. .... "옵션"이 거의없는 단순한 시스템이라면 아마도 과잉 일 것입니다. 그러나 정말 큰 목록이 있으면 한 번의 큰 호출로 모든 작업을 수행하는 것이 정말 번거로울 수 있습니다 .API가 공개되어있는 것 외에는 사용자에게 복잡해질 수 있습니다 (비공개 목록 인 경우 더 쉬워야합니다 ... 알고있는 사용자에게 알려주세요). 대안으로, 고급 사용자를위한 스타일, HATEOAS 및 "안심하지 않은 배열"호출을 구현할 수 있습니다.
Diego Dias

나는 편안한 API 웹 서비스를 레일에 만들고 있으며 위와 동일한 URL 구조를 따라야합니다 ( our.api.com/term/pumas/productType/clothing/color/black ). 그러나 그에 따라 경로를 구성하는 방법을 잘 모르겠습니다.
rubyist

용어, 제품 유형 및 색상은 컨트롤러입니까? 그렇다면 다음을 수행해야합니다. resources : term do resources : productType do resources : color end end
Diego Dias

productType 및 색상이 매개 변수입니다. 따라서
productType의

12

RFC 6570 을 확인하십시오 . 이 URI 템플리트 스펙은 URL에 매개 변수를 포함하는 방법에 대한 많은 예를 보여줍니다.


1
3.2.8 절이 적용 가능한 것 같습니다. 이것은 제안 된 표준 일 뿐이며 그 시점을 넘어서는 것으로 보이지는 않습니다.
Mike Post

3
@MikePost 이제 IETF가 "표준 트랙"문서에 대한 2 단계 성숙 프로세스로 이동 했으므로 6570이 "인터넷 표준"으로 이동하기 전에 몇 년 동안 이와 같이 유지 될 것으로 예상합니다. tools.ietf.org/html/rfc6410 사양은 매우 안정적이며 많은 구현이 있으며 널리 사용됩니다.
Darrel Miller

아 나는 그 변화를 몰랐다. (또는 TIL IETF가 더 합리적입니다.) 감사합니다!
Mike Post

8

첫 번째 경우 :

일반적인 제품 조회는 다음과 같습니다

http://our.api.com/product/1

그래서 최선의 방법은 당신이 이것을 할 것이라고 생각합니다.

http://our.api.com/Product/101404,7267261

두 번째 경우

querystring 매개 변수를 사용하여 검색하십시오. 를 사용하는 대신 AND 및 OR로 용어를 결합하고 싶습니다 [].

추신 : 이것은 주관적 일 수 있으므로 편안하다고 느끼는 것을하십시오.

URL에 데이터를 넣는 이유는 링크를 사이트에 붙여 넣거나 사용자간에 공유 할 수 있기 때문입니다. 이것이 문제가되지 않으면 반드시 JSON / POST를 대신 사용하십시오.

편집 : 반영 시이 접근법은 복합 키가있는 엔티티에 적합하지만 여러 엔티티에 대한 쿼리에는 적합하지 않다고 생각합니다.


3
물론 두 예제 모두 /에서 URI는 컬렉션이 아닌 리소스를 처리하므로 후행이 없어야합니다.
Lawrence Dol

2
나는 항상 HTTP 동사, REST 사용법에서 특정 작업을 수행하는 것이 었으며 이것이 지침입니다 : GET : 객체 검색 / 읽기, POST 객체 생성, PUT 기존 객체 업데이트 및 DELETE 객체 삭제. 따라서 POST를 사용하여 무언가를 검색하지 않습니다. 특히 객체 목록 (필터)을 원한다면 url 매개 변수 목록 (쉼표로 구분 된 것이 좋습니다)을 사용하여 GET을 수행합니다.
Alex

1

nategood의 답변이 완성되어 귀하의 요구를 기쁘게하는 것처럼 보였습니다. 그래도 여러 (1 이상) 리소스를 식별하는 방법에 대한 의견을 추가하고 싶습니다.

http://our.api.com/Product/101404,7267261

그렇게함으로써 당신은 :

고객을 복잡하게 이 응답을 배열로 해석하도록 강제 를 복잡하게하십시오. 다음 요청을하면 나에게 직관적입니다.http://our.api.com/Product/101404

중복 API 생성 하나의 API로 모든 제품을 가져오고 위의 하나를 여러 개 가져 오기 위해 를 . UX를 ​​위해 사용자에게 두 페이지 이상의 세부 정보를 표시해서는 안되므로 ID를 두 개 이상 사용하는 것은 쓸모없고 순수한 제품 필터링에 사용됩니다.

문제가되지는 않지만 단일 엔터티를 반환하여 (응답에 하나 이상이 포함되어 있는지 확인하여) 서버 측에서 직접 처리하거나 클라이언트가 관리하도록해야합니다.

Amazing 에서 책을 주문하고 싶습니다 . 나는 그것이 어떤 책인지 정확하게 알고 있으며 공포 책을 탐색 할 때 목록에서 볼 수 있습니다.

  1. 10 000 놀라운 라인, 0 놀라운 테스트
  2. 놀라운 몬스터의 귀환
  3. 놀라운 코드를 복제하자
  4. 끝의 놀라운 시작

두 번째 책을 선택하면 목록의 책 부분을 자세히 설명하는 페이지로 리디렉션됩니다.

--------------------------------------------
Book #1
--------------------------------------------
    Title: The return of the amazing monster
    Summary:
    Pages:
    Publisher:
--------------------------------------------

아니면 해당 책의 전체 세부 정보 만 제공하는 페이지에서?

---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------

내 의견

이 리소스의 세부 정보를 얻을 때 unicity가 보장되면 경로 변수에 ID를 사용하는 것이 좋습니다. 예를 들어 아래 API는 특정 리소스에 대한 세부 정보를 얻는 여러 가지 방법을 제안합니다 (제품에 고유 ID가 있고 해당 제품의 사양에 고유 한 이름이 있고 위에서 아래로 탐색 할 수 있다고 가정).

/products/{id}
/products/{id}/specs/{name}

둘 이상의 리소스가 필요한 순간에 더 큰 컬렉션에서 필터링하는 것이 좋습니다. 같은 예를 들면 :

/products?ids=

물론 이것은 부과되지 않았기 때문에 제 의견입니다.

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