RESTful API. 생성 / 업데이트 된 객체를 반환해야합니까?


35

WebApi를 사용하여 RESTful 웹 서비스를 디자인하고 객체를 업데이트 / 생성 할 때 어떤 HTTP 응답 및 응답 본문이 반환되는지 궁금합니다.

예를 들어 POST 메서드를 사용하여 일부 JSON을 웹 서비스에 보낸 다음 객체를 만들 수 있습니다. 그런 다음 HTTP 상태를 생성됨 (201) 또는 확인 (200)으로 설정하고 "새 직원 추가됨"과 같은 메시지를 반환하거나 원래 보낸 개체를 반환하는 것이 가장 좋은 방법입니까?

PUT 방법도 마찬가지입니다. 어떤 HTTP 상태를 사용하는 것이 가장 좋으며 생성 된 객체 또는 메시지 만 반환해야합니까? 어쨌든 사용자는 어떤 객체를 만들거나 업데이트하려고하는지 알고 있다는 사실을 고려하십시오.

이견있는 사람?

예:

새 직원 추가 :

POST /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Name" : "Joe Bloggs",
        "Department" : "Finance"
    }
}

기존 직원 업데이트 :

PUT /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

응답 :

객체가 생성 / 업데이트 된 응답

HTTP/1.1 201 Created
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

메시지만으로 응답 :

HTTP/1.1 200 OK
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Message": "Employee updated"
}

상태 코드만으로 응답 :

HTTP/1.1 204 No Content
Content-Length: 39
Date: Mon, 28 Mar 2016 14:32:39 GMT

2
좋은 질문이지만 "모범 사례"라는 용어를 사용하는 것은이 사이트에서 일종의 금기 메타 입니다. meta.programmers.stackexchange.com/questions/6967/…
스눕

3
약간의 후속 조치로, 모바일 응용 프로그램이 WiFi에있을 때 전체 객체를 반환 할 수 있지만 셀룰러 데이터를 사용할 때 ID 만 반환 할 수 있도록 요청에 플래그를 지정하는 것이 좋습니다. JSON을 오염시키지 않기 위해 사용해야 할 헤더가 있습니까?
앤드류는 모니카 복원 모니카

@AndrewPiliser 흥미로운 아이디어는 개인적으로 하나의 접근 방식을 선택하고 고수하는 것이 가장 좋다고 생각합니다. 그런 다음 응용 프로그램이 개발되거나 더 대중화되면 최적화하십시오
iswinky

@AndrewPiliser 당신의 아이디어는 UPDATE/INSERT ... RETURNINGSQL 에 대한 Postgresql 변형 과 매우 유사합니다 . 특히 새 데이터를 계속 제출하고 업데이트 된 버전 원자를 요청하므로 매우 편리합니다.
beldaz

답변:


31

대부분의 경우와 마찬가지로 다릅니다. 단점은 사용 편의성과 네트워크 크기입니다. 클라이언트가 작성된 자원을 보는 것이 매우 도움이 될 수 있습니다. 마지막 작성 시간과 같이 서버가 채우는 필드가 포함될 수 있습니다. id을 사용 하는 대신을 포함하는 것으로 보이므로 hateoas클라이언트는 방금 사용한 리소스의 ID를보고 싶을 것입니다 POST.

생성 된 리소스를 포함하지 않으면 임의의 메시지를 만들지 마십시오. 2xx 및 위치 필드는 클라이언트가 요청이 올바르게 처리되었음을 확신 할 수있는 충분한 정보입니다.


+1 클라이언트가 URI를 작성하지 못하게하는 증오 목표는 클라이언트가 서버가 제공 한 URL 템플릿을 특정 ID로 채울 수 있도록하여 달성 할 수 있습니다. 예, 클라이언트는 "구성"하지만 "공백 채우기"방식으로 만 구성합니다. 순수한 HATEOAS는 아니지만 목표를 달성하고 (대) 수의 "조치"를 가진 객체로 작업 할 때 대역폭에 덜 민감하지만 객체를 (대규모) 목록에 넣을 때는 언급하지 않아도됩니다.
Marjan Venema

3
"임의의 메시지를 작성하지 마십시오"조언에 +1
HairOfTheDog

"임의의 메시지 없음"이 문자열 메시지 또는 작성된 자원이 아닌 리턴 값 에 초점을 맞추고 있습니까? 나는 우리가 자원 자체가 아닌 생성 된 자원의 id를 반환하는 경우에 초점을 맞추고 있으며 이것이 어디에 적합한 지 궁금합니다.
Flater

12

개인적으로, 나는 항상 만 반환합니다 200 OK.

질문을 인용하려면

어쨌든 사용자는 어떤 객체를 만들거나 업데이트하려고하는지 알고 있다는 사실을 고려하십시오.

클라이언트에게 이미 알고있는 것을 알리기 위해 추가 대역폭을 지불해야하는 이유는 무엇입니까?


1
그것이 내가 생각한 것입니다, 그것이 유효하지 않다면 당신은 유효성 검사 메시지를 반환 할 수 있지만, 그것이 유효하고 생성 / 업데이트되면 HTTP 상태 코드를 확인하고 사용자에게 그 메시지를 보여줍니다. 예를 들어 "Hooray"
iswinky

3
jQuery와 혼동을 피하기 위해 / 에 관한 stackoverflow.com/a/827045/290182 를 참조하십시오 . 200204 No Content
beldaz

10

@iswinky POST와 PUT의 경우 항상 페이로드를 다시 보내려고합니다.

POST의 경우 내부 ID 또는 UUID로 엔티티를 작성할 수 있습니다. 따라서 페이로드를 다시 보내는 것이 좋습니다.

마찬가지로 PUT의 경우 사용자의 일부 필드 (예 : 불변 값)를 무시하거나 PATCH의 경우 다른 사용자도 데이터를 변경했을 수 있습니다.

데이터를 유지 한 상태로 다시 보내는 것은 항상 좋은 생각이며 해가되지 않습니다. 호출자가이 리턴 된 데이터를 필요로하지 않으면 처리하지 않고 statusCode 만 사용합니다. 그렇지 않으면이 데이터를 UI를 업데이트하는 데 사용할 수 있습니다.

페이로드를 다시 보내지 않고 응답 내용이있는 200 또는 응답 내용이없는 204를 수행하는 것은 DELETE의 경우에만 해당됩니다.

편집 : 아래의 의견에 감사드립니다. 나는 여전히 API를 디자인하고 응답을 돌려주는 방식을 지키고 있지만, 내 디자인 생각을 충족시키는 것이 합리적이라고 생각합니다.

a) 페이로드를 다시 보낼 때 실제로 요청에 나온 것과 동일한 페이로드가 아니라 리소스의 데이터를 다시 보내라고합니다. 예 : 생성 페이로드를 보내면 백엔드에서 UUID 및 (아마도) 타임 스탬프 및 일부 (그래프) 연결과 같은 다른 엔티티를 만들 수 있습니다. 응답 으로이 모든 것을 다시 보냅니다 (요청 페이로드뿐만 아니라 무의미합니다).

b) 페이로드가 매우 큰 경우 응답을 보내지 않습니다. 주석에서 이것을 논의했지만주의해야 할 것은 페이로드가 너무 클 필요가 없도록 API 또는 리소스를 디자인하기 위해 최선을 다한다는 것입니다. 각 리소스가 15-20 개의 JSON 속성으로 정의되고 크지 않은 리소스로 작고 관리 가능한 엔터티로 분류하려고합니다.

객체가 매우 크거나 부모 객체가 업데이트되는 경우 중첩 구조를 href로 다시 보냅니다.

결론은 소비자 / UI가 즉각적으로 처리하고 원자 API 작업을 수행하여 UI 게시물을 업데이트하기 위해 2-5 더 많은 API를 가져와야하는 대신 의미있는 데이터를 다시 보내려고한다는 것입니다. 데이터 생성 / 업데이트.

서버 대 서버 API는 이에 대해 다르게 생각할 수 있습니다. 사용자 경험을 주도하는 API에 중점을 둡니다.


페이로드가 큰 경우 전체 페이로드를 다시 보내는 것이 좋지 않은 경우가 많습니다.
beldaz

2
@beldaz는 완전히 동의합니다. REST API 설계를 기반으로 한 YMMV. 나는 일반적으로 매우 큰 객체를 피하고 일련의 하위 리소스 / PUT으로 나눕니다. 페이로드가 매우 큰 경우이 작업을 수행하는 더 좋은 방법이 있으며, 객체 자체 대신 객체에 대한 참조를 반환하는 HATEOAS (Marjan이 위에서 언급 한 것처럼)를 수행하려고합니다.
ksprashu

@ksprashu : "따라서 페이로드를 다시 보내는 것이 합리적입니다."-GUT를 통해 POST의 응답으로 PUT의 응답으로 리소스를 여러 가지 방법으로 얻을 수 있기 때문에 이것이 나쁜 생각이라는 것을 알았습니다. 이는 클라이언트가 잠재적으로 다른 구조로 3 개의 리소스를 얻음을 의미합니다 . 본문없이 URI (위치) 만 반환하는 것처럼 리소스를 얻는 유일한 방법은 GET입니다. 이렇게하면 클라이언트가 항상 일관된 응답을 얻을 수 있습니다.
mentallurg

@mentallurg 나는이 말을하지 않았을 수도 있음을 알고 있습니다. 지적 해 주셔서 감사합니다. 내 답변을 편집했습니다. 이것이 더 의미가 있는지 알려주십시오.
ksprashu

자택 업무용 서비스를 구현하는 한 실제로 중요하지 않습니다. 당신이 원하는대로하십시오. 시간을 절약하십시오.
mentallurg

9

링크 RFC 표준을 참조하면 Post를 사용하여 요청 자원을 성공적으로 저장하면 201 (작성 됨) 상태를 리턴해야합니다. 대부분의 응용 프로그램에서 리소스의 ID는 서버 자체에서 생성되므로 생성 된 리소스의 ID를 반환하는 것이 좋습니다. 전체 오브젝트를 리턴하는 것은 Post 요청에 대한 오버 헤드입니다. 가장 좋은 방법은 새로 만든 리소스 의 URL 위치반환하는 것 입니다.

예를 들어 직원 오브젝트를 데이터베이스에 저장하고 새로 작성된 자원 오브젝트의 URL을 응답으로 리턴하는 다음 예제를 참조 할 수 있습니다.

@RequestMapping(path = "/employees", method = RequestMethod.POST)
public ResponseEntity<Object> saveEmployee(@RequestBody Employee employee) {
        int id = employeeService.saveEmployee(employee);
        URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri();
        return ResponseEntity.created(uri).build();
}

이 나머지 엔드 포인트는 다음과 같이 응답을 리턴합니다.

상태 201-작성

헤더 위치 → http : // localhost : 8080 / employees / 1


멋지고 깨끗함-지금까지 그리고
앞으로도

0

리턴 본문에 페이로드를 HTTP 매개 변수에 조건부로 지정합니다.

불필요한 왕복을 피하기 위해 일종의 콘텐츠를 API 소비자에게 다시 반환하는 것이 가장 좋습니다 (GraphQL이 존재하는 이유 중 하나).

실제로 애플리케이션이 데이터를 많이 사용하고 분산됨에 따라이 지침을 준수하려고합니다.

내 지침 :

POST 또는 PUT 직후에 GET을 요구하는 유스 케이스가있을 때마다 POST / PUT 응답에서 단순히 무언가를 리턴하는 것이 가장 좋은 경우입니다.

이 작업을 수행하는 방법과 PUT 또는 POST에서 반환 할 콘텐츠 유형은 응용 프로그램에 따라 다릅니다. 이제 애플리케이션이 응답 본문에서 "컨텐츠"유형을 매개 변수화 할 수 있다면 흥미로울 것입니다 (새로운 객체의 위치 나 일부 필드 또는 전체 객체 등).

응용 프로그램은 POST / PUT이 응답 본문에 반환 할 "콘텐츠"유형을 제어하기 위해 수신 할 수있는 매개 변수 세트를 정의 할 수 있습니다. 또는 일종의 GraphQL 쿼리를 매개 변수로 인코딩 할 수 있습니다 (이 기능이 유용하지만 유지 보수의 악몽이되는 것을 볼 수 있습니다).

어느 쪽이든, 그것은 나에게 보인다 :

  1. POST / PUT 응답 본문에 무언가를 반환하는 것이 좋습니다.
  2. 이 작업을 수행하는 방법은 응용 프로그램별로 다르며 일반화하기가 거의 불가능합니다.
  3. 기본적으로 큰 "컨텍스트"를 반환하지 않으려 고합니다 (GET에서 POST로 이동하는 모든 이유를 무너 뜨리는 트래픽 소음).

따라서 1) 그렇게하지만 2) 간단하게 유지하십시오.

내가 본 또 다른 옵션은 대체 엔드 포인트를 만드는 사람들입니다 (예 : 본문에 아무것도 반환하지 않는 POST / PUT의 경우 / customers 및 POST / PUT에 대한 / customer_with_details는 / customers에 있지만 응답 본문에는 무언가를 반환합니다).

그러나이 접근법을 피할 것입니다. 다른 유형의 콘텐츠를 합법적으로 반환해야 할 경우 어떻게됩니까? 컨텐츠 유형 당 하나의 엔드 포인트? 확장 가능하거나 유지 관리 할 수 ​​없습니다.

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