카테고리 없음

drf tutorial 2 - requests and responses

monsangter 2022. 12. 1. 10:47

요청과 응답

 

이제 레스트 프레임워크의 핵심기능을 다루게 된다. 

 

요청 객체

레스트 프레임워크는 일반적 HttpRequest를 확장한 요청 객체를 다룬다, 그리고 더 유연한 요청파싱을 한다.

요청객체의 핵심기능은 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.

응답 객체

레스트 프레임워크는 응답 객체도 도입했다. 템플릿 응답의 일종이며 렌더링되지 않은 컨텐츠를 가져오고 클라이언트에 돌려줄

올바른 콘텐츠 타입을 결정한다.

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

 

상태코드

 

뷰에 숫자로된 http 상태코드를 쓰는 것이 항상 분명함을 제공하는 것은 아니다, 그리고 에러코드를 잘못 작성하더라도 알아채는 건 쉬운게 아니다. 레스트 프레임 워크에서는 각 상태코드별로 좀더분명한 식별자를 제공해준다. 상태모듈에서 http_400_BAD_REQUEST와 같이.

이 해결법을 사용하는게 단순히 상태코드만 적는 것보다는 낫다.

 

api뷰 감싸기.

 

레스트 프레임워크는 api뷰를 작성하는데 필요한 두가지를 제공한다.

 

1. @api_view . 함수 단위 기반 작업을 도와준다.

2. APIView 클래스. 클래스 기반 뷰 작업을 돕는다.

 

이 두가지는 뷰에서 요청 인스턴스를 받거나, 응답객체를 콘텍스트에 추가하는 것을 도움으로써 기능성을 제공하며,

성공적으로 통신이 수행될 수 있다.

 

이 두가지는 적절한 상황일때 405를 돌려주고, 잘못된 인풋으로 request.data를 접근하는 경우등의 파스에러 예외를 다룰 수있게 해준다.

 

두가지 다루기.

이제 이 새로운 콤포넌트들을 활용해 뷰들을 조금씩 바꿔보자.

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)

여기 인스턴스 뷰는 이전 예보다 훨씬 발전된 것이다. 더 정교하며, 현재 코드는 폼 api 작업과 매우 비슷해보인다.

우리는 또한 상태코드를 사용하며, 응답을 좀 더 분명하게 만든다.

이건 view.py 모듈안 개별 스니펫을 위한 뷰가 있다,

 

@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':
        serializer = SnippetSerializer(snippet, data=request.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요청을 처리할 수 있으며, 다른 포맷들도 다룰 수 있다. 비슷하게 우리는 응답 객체를 데이터와 함께 반환하고 있다. 하지만 레스트 프레임워크가 응답을 우리에게 적합한 콘텐츠 타입으로 렌더링 하도록 허용한다.

 

url에 추가적 서픽스 포맷 추가하기.

하나의 콘텐츠타입에 엄청 종속적이지 않다는 이 특성을 활용해보자. api 끝단에 포맷서픽스를 추가해보자.

포맷 서픽스를 사용하는 것은 특정 포맷을 따라는 url을 돌려준다. 이제 우리 api는 http://example.com/api/items/4.json.

과 같은 url을 다룰 수 있다.

다음과 같은 포맷 키워드 인수를 두가지 뷰에 추가하는 것에서 시작한다.

 

def snippet_list(request, format=None):

 

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

이제 snippets/urls.py파일을 조금 업데이트 해주자. 포맷 서픽스 패턴을 현재 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)

우리는 반드시 추가적으로 url패턴을 추가할 필요가 없다. 하지만 특정 포맷을 참조할때 훨씬 간단하고 깔끔한 방법을 제공한다.

 

어떻게 보이나?

 

커맨드라인에서 api를 태스트 해보자. 파트1튜토리얼에서 했던 것처럼. 비슷하다.

다만 이제 우리는 무효한 요청처리에 대한 예외처리를 할 수 있다.

다음과같이 모든 스니펫 리스트들을 얻을 수 있다.

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

포맷 서픽스를 추가함으로써

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

비슷하게 우리는 콘텐츠타입 헤더를 사용해 우리가 보낼 포맷 요청을 제어할 수 있다.

# 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 스위치를 위 http요청에 추가한다면, 리퀘스트 헤더에서 리퀘스트 타입을 볼 수 있을 것이다.

이제 웹 브라우저에서 api를 열어보자. http://127.0.0.1:8000/snippets/  를 열어보자.

 

브라우징 편의성?

 

api는 응답에 대한 

Because the API chooses the content type of the response based on the client request, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a web browser. This allows for the API to return a fully web-browsable HTML representation.

Having a web-browsable API is a huge usability win, and makes developing and using your API much easier. It also dramatically lowers the barrier-to-entry for other developers wanting to inspect and work with your API.

See the browsable api topic for more information about the browsable API feature and how to customize it.

What's next?

In tutorial part 3, we'll start using class-based views, and see how generic views reduce the amount of code we need to write.