REST-본문에 ID를 넣을까요?


96

클라이언트가 ID를 할당 할 수있는 사람들을위한 RESTful 리소스를 원한다고 가정 해 보겠습니다.

사람은 다음과 같습니다. {"id": <UUID>, "name": "Jimmy"}

이제 클라이언트는이를 어떻게 저장 (또는 "PUT")해야합니까?

  1. PUT /person/UUID {"id": <UUID>, "name": "Jimmy"} -이제 우리는 항상 확인해야하는 불쾌한 중복이 있습니다. 본문의 ID가 경로의 ID와 일치합니까?
  2. 비대칭 표현 :
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID 보고 {"id": <UUID>, "name": "Jimmy"}
  3. 본문에 ID 없음-위치에만 ID :
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID 보고 {"name": "Jimmy"}
  4. 의 어떤 종류의 POSTID를이 클라이언트에 의해 생성되기 때문에 좋은 아이디어처럼 보인다.

일반적인 패턴과 해결 방법은 무엇입니까? 위치에만있는 ID는 가장 독단적으로 올바른 방법처럼 보이지만 실제 구현을 더 어렵게 만듭니다.

답변:


62

다른 읽기 / 쓰기 모델을 사용하는 데 아무런 문제가 없습니다. 클라이언트는 서버가 추가 / 계산 된 요소가 포함 된 다른 표현을 반환 할 수있는 하나의 리소스 표현을 작성할 수 있습니다 (또는 완전히 다른 표현도 가능합니다. , 유일한 요구 사항은 PUT가 리소스를 생성하거나 교체해야한다는 것입니다).

그래서 나는 (2)의 비대칭 솔루션을 선택하고 작성할 때 서버 측에서 "불쾌한 중복 검사"를 피할 것입니다.

PUT /person/UUID {"name": "Jimmy"}

GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

2
그리고 타이핑 (정적 또는 동적)을 적용하면 ID가없는 모델을 쉽게 가질 수 없습니다. 따라서 PUT 요청을 위해 URL에서 ID를 제거하는 것이 훨씬 쉽습니다. "안식"하지는 않지만 정확할 것입니다.
이반 Kleshnin

2
idID 및 엔티티 및 추가 변환기와 함께 추가 TO없이 프로그래머에게 너무 큰 오버 헤드를 유지하십시오.
Grigory Kislin

BODY에서 ID를 받으면 어떻게됩니까 예 : PUT / person { "id": 1, "name": "Jimmy"}. 그것은 나쁜 습관일까요?
브루노 산토스

본문에 신분증을 넣어도 괜찮습니다. 정수 대신 ID에 GUID를 사용하십시오. 그렇지 않으면 ID가 중복 될 위험이 있습니다.
Jørn Wildt

이것은 잘못되었습니다. 내 대답을 참조하십시오. PUT에는 전체 리소스가 포함되어야합니다. ID를 제외하고 레코드의 일부만 업데이트하려면 PATCH를 사용하십시오.
CompEng88

27

공개 API 인 경우 답장 할 때는 보수적이어야하지만 자유롭게 수락해야합니다.

즉, 1과 2를 모두 지원해야합니다. 3이 말이 안된다는 데 동의합니다.

1과 2를 모두 지원하는 방법은 요청 본문에 아무것도 제공되지 않은 경우 URL에서 ID를 가져오고 요청 본문에있는 경우 URL의 ID와 일치하는지 확인하는 것입니다. 둘이 일치하지 않으면 400 Bad Request 응답을 반환합니다.

사람 리소스를 반환 할 때는 보수적이어야하며 put에서 선택 사항이더라도 json에 항상 id를 포함하십시오.


3
이것이 허용되는 솔루션이어야합니다. API는 항상 사용자 친화적이어야합니다. 본문에서 선택 사항이어야합니다. POST에서 ID를받지 않아야하며 PUT에서 정의되지 않도록해야합니다. 또한 400 개의 응답 포인트가 올바르게 설정되었습니다.
Michael

약 400 개의 코드는 softwareengineering.stackexchange.com/questions/329229/… 토론을 참조하십시오 . 간단히 말해 400 코드는 422에 비해 부적절하지 않고 덜 구체적입니다.
Grigory Kislin

8

이 문제에 대한 한 가지 해결책은 "애플리케이션 상태의 엔진으로서의 하이퍼 텍스트"또는 "HATEOAS"라는 다소 혼란스러운 개념을 포함합니다. 이는 REST 응답에 하이퍼 링크로 수행 할 사용 가능한 리소스 또는 작업이 포함되어 있음을 의미합니다. REST의 원래 개념의 일부였던이 방법을 사용하면 리소스의 고유 식별자 / ID 자체가 하이퍼 링크입니다. 예를 들어 다음과 같은 것을 가질 수 있습니다.

GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}

그런 다음 해당 리소스를 업데이트하려면 다음을 수행 할 수 있습니다 (의사 코드).

updatedPerson = person.data
updatedPerson.name = "Timmy"
PUT(URI: response.resource, data: updatedPerson)

이것의 한 가지 장점은 클라이언트가 사용자 ID의 서버 내부 표현에 대해 전혀 알 필요가 없다는 것입니다. 클라이언트가 ID를 발견 할 수있는 한 ID는 변경 될 수 있으며 URL 자체도 변경 될 수 있습니다. 예를 들어, 사람들의 컬렉션을 가져올 때 다음과 같은 응답을 반환 할 수 있습니다.

GET /people
{ "people": [
    "/person/1",
    "/person/2"
  ]
}

(물론 응용 프로그램의 필요에 따라 각 사람에 대한 전체 사람 개체를 반환 할 수도 있습니다.)

이 방법을 사용하면 리소스 및 위치 측면에서 개체를 더 많이 생각하고 ID 측면에서는 덜 생각합니다. 따라서 고유 식별자의 내부 표현은 클라이언트 논리에서 분리됩니다. 이것이 REST의 원동력이었습니다. HTTP의 기능을 사용하여 이전에 존재했던 RPC 시스템보다 느슨하게 결합 된 클라이언트-서버 아키텍처를 만드는 것입니다. HATEOAS에 대한 자세한 내용은 Wikipedia 기사 와이 짧은 기사를 참조하십시오 .


4

삽입에서 URL에 ID를 추가 할 필요가 없습니다. 이런 식으로 PUT에서 ID를 보내면 기본 키를 변경하는 UPDATE로 해석 될 수 있습니다.

  1. 끼워 넣다:

    PUT /persons/ 
      {"id": 1, "name": "Jimmy"}
    HTTP/1.1 201 Created     
      {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
    
    GET /persons/1
    
    HTTP/1.1 200 OK
      {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}  
    
  2. 최신 정보

    PUT /persons/1 
         {"id": "2", "name": "Jimmy Jr"} - 
    HTTP/1.1 200 OK
         {"id": "2", "name": "Jimmy Jr", "other_field"="filled_by_server"}
    
    GET /persons/2 
    
    HTTP/1.1 200 OK
         {"id": "2", "name": "Jimmy Jr", "other_field"="updated_by_server"}
    

JSON API는 이 표준을 해결해 새 개체에 대한 링크 삽입 또는 업데이트 된 개체를 반환 몇 가지 문제를 사용합니다. 일부 업데이트 또는 삽입에는 추가 필드를 변경하는 비즈니스 논리가 포함될 수 있습니다.

또한 삽입 및 업데이트 후 get을 피할 수 있음을 알 수 있습니다.


3

이것은 이전에 질문 된 적이 있습니다. 토론은 살펴볼 가치가 있습니다.

RESTful GET 응답이 리소스의 ID를 반환해야합니까?

이것은 "휴식"이 무엇인지 아닌지 에 대한 논쟁에 빠지기 쉬운 질문 중 하나입니다 .

그만한 가치는 일관된 리소스의 관점에서 생각하고 방법간에 디자인을 변경하지 않습니다. 그러나 IMHO는 사용성 관점에서 가장 중요한 것은 전체 API에서 일관성이 있다는 것입니다!


2

다른 작업에 대해 다른 표현 을 사용하는 것은 괜찮지 만 PUT에 대한 일반적인 권장 사항 은 WHOLE 페이로드를 포함하는 것 입니다. 그것은 id또한 거기에 있어야 함을 의미합니다 . 그렇지 않으면 PATCH를 사용해야합니다.

그렇긴하지만 PUT는 대부분 업데이트에 활용 id되어야하며 항상 URL에도 전달되어야 한다고 생각 합니다. 그 결과 PUT를 사용하여 리소스 식별자를 업데이트하는 것은 좋지 않습니다. idURL id이 본문 과 다를 수있는 경우 바람직하지 않은 상황에 처하게됩니다 .

그렇다면 그러한 갈등을 어떻게 해결합니까? 기본적으로 두 가지 옵션이 있습니다.

  • 4XX 예외 발생
  • Warning( X-API-Warnetc) 헤더를 추가하십시오 .

일반적으로 주제는 의견의 문제이기 때문에이 질문에 답할 수있는 한 거의 비슷합니다.


2

참고로 여기에 대한 답변이 잘못되었습니다.

보다:

https://restfulapi.net/rest-api-design-tutorial-with-example/

https://restfulapi.net/rest-put-vs-post/

https://restfulapi.net/http-methods/#patch

놓다

PUT API를 주로 사용하여 기존 리소스를 업데이트합니다 (리소스가없는 경우 API가 새 리소스를 만들지 여부를 결정할 수 있음). PUT API에 의해 새 리소스가 생성 된 경우 원본 서버는 HTTP 응답 코드 201 (Created) 응답을 통해 사용자 에이전트에 알려야하며 기존 리소스가 수정되면 200 (OK) 또는 204 (No Content) 중 하나를 알려야합니다. 요청이 성공적으로 완료되었음을 나타 내기 위해 응답 코드를 전송해야합니다 (SHOULD).

요청이 캐시를 통과하고 Request-URI가 현재 캐시 된 하나 이상의 엔티티를 식별하는 경우 해당 항목은 부실한 것으로 취급해야합니다 (SHOULD). 이 메서드에 대한 응답은 캐시 할 수 없습니다.

이미 리소스 컬렉션의 일부인 단일 리소스를 수정하려는 경우 PUT를 사용합니다. PUT는 리소스 전체를 대체합니다. 요청이 리소스의 일부를 업데이트하는 경우 PATCH를 사용합니다.

반점

HTTP PATCH 요청은 리소스를 부분적으로 업데이트하는 것입니다. PUT 요청도 리소스 엔터티를 수정하여보다 명확하게 표시하는 경우 – PATCH 메서드는 기존 리소스를 부분적으로 업데이트하는 올바른 선택이며 PUT는 리소스 전체를 교체하는 경우에만 사용해야합니다.

따라서 다음과 같이 사용해야합니다.

POST    /device-management/devices      : Create a new device
PUT     /device-management/devices/{id} : Update the device information identified by "id"
PATCH   /device-management/devices/{id} : Partial-update the device information identified by "id"

RESTful 관행은 / {id}에 무엇을 입력하든 상관 없습니다. 레코드의 내용은 페이로드가 제공하는 내용으로 업데이트해야하지만 GET / {id}는 여전히 동일한 리소스에 연결되어야합니다.

즉, PUT / 3은 페이로드 ID를 4로 업데이트 할 수 있지만 GET / 3은 여전히 ​​동일한 페이로드에 연결되어야합니다 (ID가 4로 설정된 페이로드를 반환).

API에 URI와 페이로드에 동일한 식별자가 필요하다고 결정하는 경우, 일치하는지 확인하는 것이 귀하의 작업이지만, 전체가 있어야하는 페이로드에서 ID를 제외하는 경우 PUT 대신 PATCH를 사용하십시오. . 이것은 받아 들여진 대답이 잘못된 곳입니다. PUT는 전체 리소스를 대체해야합니다. 패치는 부분적 일 수 있습니다.


1

다른 접근 방식을 사용하는 데 나쁜 것은 없습니다. 하지만 가장 좋은 방법은 2nd 솔루션 입니다 .

 PUT /person/UUID {"name": "Jimmy"}

 GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

이러한 방식으로 주로 사용되며, 심지어 엔티티 프레임 워크에서도 엔티티가 dbContext에 추가 될 때이 기법을 사용 합니다. 생성 된 ID가없는 클래스는 Entity Framework에서 참조로 생성 된 ID입니다.


1

나는에서이 찾고 있어요 JSON-LD 그게 내가에 설명 된대로 실제 REST 적합성을 달성하기 위해 갈 수있는 좋은 방법이기 때문에보기 / 시맨틱 웹 지점 이 슬라이드 . 그런 관점에서 보면 웹 리소스의 ID (IRI)가 리소스를 조회 / 역 참조하는 데 사용할 수있는 URL과 항상 같아야하므로 옵션 (1)을 선택해야 할 문제가 없습니다. 나는 검증이 실제로 구현하기 어렵거나 계산적으로 강화되지 않는다고 생각합니다. 그래서 나는 이것이 옵션 (2)를 사용하는 타당한 이유라고 생각하지 않습니다. POST (새로 만들기)가 PUT (업데이트 / 바꾸기)와 다른 의미를 가지고 있기 때문에 옵션 (3.)은 실제로 옵션이 아니라고 생각합니다.


0

PATCH / PUT 요청 유형을 조사해야 할 수도 있습니다.

PATCH 요청은 리소스를 부분적으로 업데이트하는 데 사용되는 반면 PUT 요청에서는 서버에서 재정의되는 전체 리소스를 보내야합니다.

URL에 ID가있는 한 리소스를 식별하는 표준 관행이므로 항상 가지고 있어야한다고 생각합니다. Stripe API도 그렇게 작동합니다.

PATCH 요청을 사용하여 ID로 서버의 리소스를 업데이트하여 식별 할 수 있지만 실제 ID는 업데이트하지 않습니다.

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