[drf] Tutorial 2: Requests and Response

Hyeseong·2021년 2월 18일
0

DRF

목록 보기
2/4

Tutorial 2: Requests and Response

Responses

이 시점부터 REST 프레임 워크의 핵심을 다룰 것입니다. 몇 가지 필수 구성 요소를 소개하겠습니다.

Request objects

REST framework는 일반적인 HttpRequest보다 더 확장된 Request를 도입했는데요. 또한 유연한 request parsing기능도 제공합니다. Request객체의 핵심 기능은 request.data속성으로 request.POST와 비슷 하지만 웹 API 작업에 더 유용합니다.

request.POST  # Only handles form data.  Only works for 'POST' method.
request.data  # Handles arbitrary data.  Works for 'POST', 'PUT' and 'PATCH' methods.

Response objects

REST 프레임 워크는 또한 렌더링되지 않은 콘텐츠의 TemplateResponse를 가져오고 content negotiation을 통해 클라이언트에 반환 할 올바른 콘텐츠 형식을 결정하는 Response하는 객체를 도입했습니다.

return Response(data)  # Renders to content type as requested by the client.

Status codes

뷰에서 숫자 HTTP 상태 코드를 사용한다고해서 항상 명확하게 읽을 수있는 것은 아니며 오류 코드가 잘못 표시되면 알아 차리지 못하기 쉽습니다. REST 프레임 워크는 모듈 HTTP_400_BAD_REQUEST에서 와 같이 각 상태 코드에 대해보다 명시적인 식별자 status를 제공 합니다. 숫자로 사용하는 것보다 전체적 명시해주는 것이 좋습니다.

Wrapping API views

REST framework는 API views 작성시 2가지 wrapper를 제공합니다.

  1. @api_view FBV기반
  2. APIView CBV 기반

위 래퍼들의 특징은

  • 뷰에 Request인스턴스를 받았는지 안받았는지 확인하는 역활
  • content negotiation 수행을 위한Response 객체에 context를 추가

wrapper는 405 Method Not Allowed 응답과 request.data로 잘못 입력된 경우 ParseError 예외도 발생 시키기는 등의 행동도 제공해요.

Pulling it all together

기존 tutorial 1 에서 다루었던 코드를 위에서 언급한 개념을 갖고 리팩토링 해볼게요.

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer


@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    List all code snippets, or create a new snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

코드가 더 간결해젔습니다. 또한 Forms API와 매우 유사합니다.
그리고 눈에 띄는 점이 오류 코드가 숫자가 아닌 문자가 같이 들어가 있어서 한눈에 무슨 오류인지 빡~! 하고 알 수 있네요.

아래는 단건 처리에 대한 뷰로직이에요.

@api_view(['GET','PUT','DELETE'])
def snippet_detail(request, pk):
    """
    Retrieve, update or delete a code snippet.
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

더 이상 특정 콘텐츠 유형에 대한 요청이나 응답을 명시 적으로 묶지 않습니다. request.data들어오는 json요청을 처리 할 수 있지만 다른 형식도 처리 할 수 있습니다. 마찬가지로 데이터와 함께 응답 객체를 반환하지만 REST 프레임 워크가 응답을 올바른 콘텐츠 유형으로 렌더링하도록 허용합니다.\

Adding optional format suffixes to our URLs

응답이 더 이상 단일 콘텐츠 유형에 고정되지 않는다는 사실을 활용하기 위해 API 엔드 포인트에 형식 접미사에 대한 지원을 추가하겠습니다. 형식 접미사를 사용하면 주어진 형식을 명시 적으로 참조하는 URL이 제공되며 API가 http://example.com/api/items/4.json 과 같은 URL을 처리 할 수 ​​있습니다 .

format키워드 인자를 뷰의 리스트 메서드의 인자로 넣어볼게요.

def snippet_list(request, format=None):

and

def snippet_detail(request, pk, format=None):

이제 snippets/urls.py파일을 약간 업데이트하여 format_suffix_patterns기존 URL에 추가로 추가합니다.

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    path('snippets/', views.snippet_list),
    path('snippets/<int:pk>', views.snippet_detail),
]

urlpatterns = format_suffix_patterns(urlpatterns)

urlpatterns에 별도로 더 추가하는건 없습니다. format_suffix_patterns메서드 안에 그냥 넣어주고 다시 동일한 변수명을 재설정해주면 끝이에요.

어떻게 생겨 먹었을까?

튜토리얼 1 부 에서했던 것처럼 명령 줄에서 API를 테스트합니다 . 유효하지 않은 요청을 보내면 더 좋은 오류 처리가 가능하지만 모든 것이 거의 비슷하게 작동합니다.

이전과 마찬가지로 모든 스니펫 목록을 얻을 수 있습니다.

http http://127.0.0.1:8000/snippets/

HTTP/1.1 200 OK
...
[
  {
    "id": 1,
    "title": "",
    "code": "foo = \"bar\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  },
  {
    "id": 2,
    "title": "",
    "code": "print(\"hello, world\")\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  }
]

Accept헤더 를 사용하여 반환되는 응답의 형식을 제어 할 수 있습니다 .

http http://127.0.0.1:8000/snippets/ Accept:application/json  # Request JSON
http http://127.0.0.1:8000/snippets/ Accept:text/html         # Request HTML

suffix,접미사를 사용해 볼게요.

http http://127.0.0.1:8000/snippets.json  # JSON suffix
http http://127.0.0.1:8000/snippets.api   # Browsable API suffix

마찬가지로 Content-Type헤더를 사용하여 보내는 요청의 형식을 제어 할 수 있습니다 .

# POST using form data
http --form POST http://127.0.0.1:8000/snippets/ code="print(123)"

{
  "id": 3,
  "title": "",
  "code": "print(123)",
  "linenos": false,
  "language": "python",
  "style": "friendly"
}

# POST using JSON
http --json POST http://127.0.0.1:8000/snippets/ code="print(456)"

{
    "id": 4,
    "title": "",
    "code": "print(456)",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

위에서 사용한 명령어에 --debug switch를 http request 위에 넣어줄게요.

다음 튜토에서는?

CBV를 시작하면서, generic views가 어떻게 상당한양의 코드를 줄이는지 확앤해 볼게요.

profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글