REST API 오류 응답 모델 및 오류 코드 시스템을 작성하는 가장 좋은 방법은 무엇입니까?


15

내 REST 구현은 다음 구조의 JSON에서 오류를 반환합니다.

{
 "http_response":400,
 "dev_message":"There is a problem",
 "message_for_user":"Bad request",
 "some_internal_error_code":12345
}

속성 (dev_message, message_for_user, some_internal_error_code)에 필요한 값을 전달하고 반환 할 수있는 특수 응답 모델을 만드는 것이 좋습니다. 코드에서는 다음과 유사합니다.

$responseModel = new MyResponseModel(400,"Something is bad", etc...);

이 모델은 어떻게 생겼습니까? 텍스트 정보 만 전달하는 successResponse ()와 같은 메소드를 구현해야하며 코드는 기본적으로 200입니다. 나는 이것에 붙어있다. 그리고 이것은 내 질문의 첫 부분입니다.이 모델을 구현해야합니까?이 좋은 연습입니까? 지금은 코드에서 직접 배열을 반환하기 때문입니다.

두 번째 부분은 오류 코드 시스템에 관한 것입니다. 오류 코드는 설명서에 설명되어 있습니다. 그러나 내가 겪고있는 문제는 코드에 있습니다. 오류 코드를 관리하는 가장 좋은 방법은 무엇입니까? 모델 안에 써야합니까? 아니면 이것을 처리하기 위해 별도의 서비스를 만드는 것이 더 좋을까요?

업데이트 1

응답을 위해 모델 클래스를 구현했습니다. 그것은 Greg의 대답과 같은 논리이지만, 추가 로 모델에서 쓰여진 오류를 하드 코딩 했으며 여기에 다음과 같이 보입니다.

    class ErrorResponse
    {
     const SOME_ENTITY_NOT_FOUND = 100;
     protected $errorMessages = [100 => ["error_message" => "That entity doesn't exist!"]];

     ...some code...
    }

내가 왜 그랬어? 그리고 무엇을 위해?

  1. 코드가 멋지게 보입니다. return new ErrorResponse(ErrorResponse::SOME_ENTITY_NOT_FOUND );
  2. 오류 메시지를 쉽게 변경할 수 있습니다. 모든 메시지는 컨트롤러 / 서비스 / 등 대신 또는 한 곳에 배치됩니다.

이를 개선하기위한 제안 사항이 있으면 의견을 말하십시오.

답변:


13

이 상황에서는 항상 인터페이스를 먼저 생각한 다음 지원하기 위해 PHP 코드를 작성하십시오.

  1. REST API이므로 의미있는 HTTP 상태 코드는 필수입니다.
  2. 클라이언트와 일관되고 유연한 데이터 구조를 보내고 싶습니다.

잘못 될 수있는 모든 것과 HTTP 상태 코드를 생각해 봅시다 :

  • 서버에서 오류가 발생 함 (500)
  • 인증 실패 (401)
  • 요청한 자원을 찾을 수 없습니다 (404).
  • 수정 한 데이터가로드 된 후 변경되었습니다 (409)
  • 데이터 저장시 유효성 검사 오류 (422)
  • 클라이언트가 요청 비율을 초과했습니다 (429)
  • 지원되지 않는 파일 형식 (415)

나중에 조사 할 수있는 다른 것들도 있습니다.

대부분의 실패 조건에 대해 하나의 오류 메시지 만 리턴됩니다. 422 Unprocessable Entity나는 "유효성 검사 오류"를 사용했습니다 반응은 하나 이상의 오류 --- 양식 필드 당 하나 이상의 오류를 반환 할 수 있습니다.

오류 응답을위한 유연한 데이터 구조가 필요합니다.

예를 들어 가지고는 500 Internal Server Error:

HTTP/1.1 500 Internal Server Error
Content-Type: text/json
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

{
    "errors": {
        "general": [
            "Something went catastrophically wrong on the server! BWOOP! BWOOP! BWOOP!"
        ]
    }
}

서버에 무언가를 게시하려고 할 때 간단한 유효성 검사 오류와 대조하십시오.

HTTP/1.1 422 Unprocessable Entity
Content-Type: text/json
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

{
    "errors": {
        "first_name": [
            "is required"
        ],
        "telephone": [
            "should not exceed 12 characters",
            "is not in the correct format"
        ]
    }
}

여기서 핵심은 컨텐츠 유형 text/json입니다. 이를 통해 클라이언트 애플리케이션은 JSON 디코더로 응답 본문을 디코딩 할 수 있습니다. 예를 들어 내부 서버 오류가 발생하지 않고 일반 "문제가 발생했습니다"웹 페이지가 대신 전달되는 경우 text/html; charset=utf-8클라이언트 응용 프로그램이 응답 본문을 JSON으로 디코딩하지 않도록 콘텐츠 유형이되어야합니다 .

이것은 JSONP 응답 을 지원해야 할 때까지 모두 찾고 멋지게 보입니다 . 200 OK실패하더라도 응답을 반환해야합니다 . 이 경우 클라이언트가 JSONP 응답을 요청하고 있음을 감지하고 (보통이라는 URL 요청 매개 변수를 감지하여 callback) 데이터 구조를 약간 변경해야합니다.

(GET / posts / 123? callback = displayBlogPost)

<script type="text/javascript" src="/posts/123?callback=displayBlogPost"></script>

HTTP/1.1 200 OK
Content-Type: text/javascript
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

displayBlogPost({
    "status": 500,
    "data": {
        "errors": {
            "general": [
                "Something went catastrophically wrong on the server! BWOOP! BWOOP! BWOOP!"
            ]
        }
    }
});

그런 다음 클라이언트의 웹 브라우저에서 응답 처리기 displayBlogPost는 단일 인수를 허용하는 전역 JavaScript 함수를 가져야합니다 . 이 함수는 응답이 성공적인지 확인해야합니다.

function displayBlogPost(response) {
    if (response.status == 500) {
        alert(response.data.errors.general[0]);
    }
}

그래서 우리는 고객을 돌 보았습니다. 이제 서버를 관리하겠습니다.

<?php

class ResponseError
{
    const STATUS_INTERNAL_SERVER_ERROR = 500;
    const STATUS_UNPROCESSABLE_ENTITY = 422;

    private $status;
    private $messages;

    public function ResponseError($status, $message = null)
    {
        $this->status = $status;

        if (isset($message)) {
            $this->messages = array(
                'general' => array($message)
            );
        } else {
            $this->messages = array();
        }
    }

    public function addMessage($key, $message)
    {
        if (!isset($message)) {
            $message = $key;
            $key = 'general';
        }

        if (!isset($this->messages[$key])) {
            $this->messages[$key] = array();
        }

        $this->messages[$key][] = $message;
    }

    public function getMessages()
    {
        return $this->messages;
    }

    public function getStatus()
    {
        return $this->status;
    }
}

그리고 서버 오류가 발생했을 때 이것을 사용하려면 :

try {
    // some code that throws an exception
}
catch (Exception $ex) {
    return new ResponseError(ResponseError::STATUS_INTERNAL_SERVER_ERROR, $ex->message);
}

또는 사용자 입력을 확인할 때 :

// Validate some input from the user, and it is invalid:

$response = new ResponseError(ResponseError::STATUS_UNPROCESSABLE_ENTITY);
$response->addMessage('first_name', 'is required');
$response->addMessage('telephone', 'should not exceed 12 characters');
$response->addMessage('telephone', 'is not in the correct format');

return $response;

그런 다음 반환 된 응답 객체를 가져 와서 JSON으로 변환하고 즐거운 방식으로 응답을 보내는 무언가가 필요합니다.


답변 주셔서 감사합니다! 비슷한 솔루션을 구현했습니다. 혼자서 메시지를 전달하지 않는 유일한 차이점은 이미 설정되어 있습니다 (업데이트 된 질문 참조).
Grokking

-2

나는 비슷한 것을 직면하고 있었고, 3 가지 일을했습니다.

  1. ABCException이라는 ExceptionHandler를 작성했습니다.

Java & Spring을 사용하고 있기 때문에

나는 그것을 다음과 같이 정의했다.

 public class ABCException extends Exception {
private String errorMessage;
private HttpStatus statusCode;

    public ABCException(String errorMessage,HttpStatus statusCode){
            super(errorMessage);
            this.statusCode = statusCode;

        }
    }

그런 다음 필요에 따라 호출하십시오.

throw new ABCException("Invalid User",HttpStatus.CONFLICT);

REST 기반 웹 서비스를 사용하는 경우 컨트롤러에서 ExceptionHandler를 만들어야합니다.

@ExceptionHandlerSpring을 사용 하는 경우 주석 달기


프로그래머는 대한 개념 질문과 답변을하는 것으로 설명 할 것을 . 설명 대신 코드 덤프를 던지는 것은 IDE에서 화이트 보드로 코드를 복사하는 것과 같습니다. 친숙하고 때로는 이해할 수 있지만 이상하게 느껴집니다. 화이트 보드에는 컴파일러가 없습니다
gnat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.