HTTP

[API] 2. RESTFUL API - RESPONSE STATUS CODE + 객체 활용 응답 처리(데코레이터, 클래스)

foxlee 2021. 11. 1. 17:02

이전글 -

2020.11.01 - [Web/HTTP] - [API] 1. RESTful API란?

코드

status = {
    "OK": 200,  # GET - DATA OR EMPTY LIST
    "CREATED": 201,  # POST,
    "ACCEPTED": 202,  # POST - ASYNC
    "NO CONTENT": 204,  # DELETE, PUT
    "BAD REQUEST": 400,  # GENERAIL FAIL - CLIENT ERROR
    "NO AUTH": 401,  # LOGIN NEEDED
    "FORBIDDEN": 403,  # ADMIN ONLY
    "NOT FOUND": 404,  # NOT FOUND BY IDENTIFIER
    "SEVER ERROR": 500,  # SEVER ERROR
}

message = {
    "kr": {
        "OK": "성공",  # GET - DATA OR EMPTY LIST
        "CREATED": "생성",  # POST,
        "ACCEPTED": "요청 성공",
        "NO CONTENT": "요청 성공",  # DELETE, PUT
        "BAD REQUEST": "잘못된 요청입니다.",  # GENERAIL FAIL - CLIENT ERROR
        "NO AUTH": "로그인이 필요합니다.",  # LOGIN NEEDED
        "FORBIDDEN": "접근 권한이 없습니다.",  # ADMIN ONLY
        "NOT FOUND": "원하시는 데이터를 찾지 못했습니다.",  # NOT FOUND BY IDENTIFIER
        "SEVER ERROR": "죄송합니다. 다음에 다시 시도해주세요.",  # SEVER ERROR
    },
    "en": {
        "OK": "Success",  # GET - DATA OR EMPTY LIST
        "CREATED": "Created",  # POST
        "ACCEPTED": "Request accepted",
        "NO CONTENT": "Request has succeeded",  # DELETE, PUT
        "BAD REQUEST": "BAD REQUEST",  # GENERAIL FAIL - CLIENT ERROR
        "NO AUTH": "Please login first",  # LOGIN NEEDED
        "FORBIDDEN": "No permission",  # ADMIN ONLY
        "NOT FOUND": "Couldn't find what you want",  # NOT FOUND BY IDENTIFIER
        "SEVER ERROR": "Oops, something went wrong",  # SEVER ERROR
    },
}
# core/response.py
from core.constants import response

def return_404_for_no_auth(f):
    def wrapper(*args, **kwargs):
        user = None
        # ..... 유저 세션 체크 로직
		if user is None: 
            response = CustomeResponse()
			return response.send(response_type="NO_AUTH")
        return f(*args, **kwargs, auth_user=user)
        
    # ..... wrapper 관련 코드
    return wrapper


def return_500_for_sever_error(f):
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except:
		    # ..... 에러 처리 관련 코드
            response = CustomeResponse()
            return response.send(
                response_type="SEVER_ERROR",
            )
    
    # ..... wrapper 관련 코드
    return wrapper


class CustomeResponse:
    headers = {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Headers": "*",
        "Access-Control-Allow-Credentials": "True",
    }

    def send(
        self,
        result=None,
        response_type=None,
        lang="en",
        additional_message=None,
    ):

        status = response.status[response_type]
        message = response.message[lang][response_type]

        if additional_message is not None:
            message += f"({additional_message})"

        response_body = {"result": result, "message": message}

        json_encode = json.JSONEncoder().encode
        return Response(
            json_encode(response_body),
            status=status,
            headers=self.headers,
            mimetype="application/json",
        )
from core.response import (
    CustomeResponse,
    return_500_for_sever_error,
    return_404_for_no_auth,
)

@api.route("/<int:id_>")
@api.param("id_", "The action identifier")
class Action(Resource, CustomeResponse):
    @return_500_for_sever_error
    def get(self, id_):
        if action := get_action(id_):
            return self.send(response_type="SUCCESS", result=action)
        return self.send(response_type="NOT_FOUND")

    @api.doc("update action name")
    @api.expect(parser_post, parser_auth)
    @return_404_for_no_auth
    @return_500_for_sever_error
    def put(self, id_, **kwargs):
        if get_action(id_):
            if kwargs["auth_user"].is_admin():
                args = parser_post.parse_args()
                update_action(id_, args["name"])
                return self.send(response_type="NO_CONTENT")
            return self.send(response_type="FORBIDDEN")
        return self.send(response_type="NOT_FOUND")

    @api.doc("delete an action")
    @api.expect(parser_auth)
    @return_404_for_no_auth
    @return_500_for_sever_error
    def delete(self, id_, **kwargs):
        if get_action(id_):
            if kwargs["auth_user"].is_admin():
                delete_action(id_)
                return self.send(response_type="NO_CONTENT")
            return self.send(response_type="FORBIDDEN")
        return self.send(response_type="NOT_FOUND")

GET - 리소스 조회

  • 리소스들을 전체를 가져오거나 특정 조건(필터)에 맞춰서 리소스들을 반환한다.
  • 데이터가 없는 경우에는 빈 배열([])를 반환한다.
  • 데이터가 있던 없던 성공 시 200
  • 필수 조건 또는 조건 값 등의 맞지 않는 경우 400
  • 특정 Identifier(식별자)로 조회하는 경우에 없으면 404 (NOT FOUND)
  • 서버 에러 500

POST - 새로운 리소스 생성

  • 새로운 리소스를 생성한다.
  • 데이터가 정상적으로 생성 경우에는 리소스 아이디를 반환한다.
  • 데이터가 정상적으로 생성되면 201, 비동기 작업 시 202
  • 생성시 필요한 데이터가 없는 경우, 유니크한 값이 이미 존재하는 경우 등 400 (클라이언트에서 다른 값을 보내줘야함)
  • 서버 에러 500

PUT - 이미 존재하는 리소스 업데이트 

  • 기존의 리소스를 업데이트한다.
  • 특정 Identifier(식별자)로 없으면 404 (NOT FOUND)
  • 기존에 데이터에 식별자 제외 전체를 업데이트 하는게 기본/ 예외는 있을 수 있음
  • 업데이트 성공 시 204 - NO CONTENT 업데이트 실패시 400
  • 서버 에러 500
  • GET POST DELETE 으로는 어울리지 않는 작업들은 PUT으로 요청

DELETE - 이미 존재하는 리소스를 삭제

  • 기존의 리소스를 삭제한다.
  • 특정 Identifier(식별자)로 없으면 404 (NOT FOUND)
  • 업데이트 성공 시 204 - NO CONTENT
  • 서버 에러 500