Урок 2: Запросы и ответы
Начиная с этого момента мы переходим к изучению основ REST-фреймворка. Давайте представим несколько основных строительных блоков.
Объекты запроса
REST фреймворк предоставляет объект Request, который расширяет обычный HttpRequest, и обеспечивает более гибкий синтаксический анализ запроса. Основной функциональностью объекта 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.
Объекты ответа
REST фреймворк также предоставляет объект Response, который является типом TemplateResponse, принимающим неотформатированный контент и использующим переговорный протокол контента для определения правильного типа контента, который нужно вернуть клиенту.
return Response(data) # Renders to content type as requested by the client.
Коды состояния
Использование числовых кодов состояния HTTP в ваших представлениях не всегда обеспечивает чтение, и легко упустить из виду, если вы ошиблись с кодом ошибки. REST фреймворк предоставляет более явные идентификаторы для каждого кода состояния, такие как HTTP_400_BAD_REQUEST в модуле status. Рекомендуется использовать их вместо числовых идентификаторов.
Оборачивание представлений API
REST фреймворк предоставляет два обёртки, которые вы можете использовать для написания представлений API.
- Декоратор
@api_viewдля работы с представлениями на основе функций. - Класс
APIViewдля работы с представлениями на основе классов.
Эти обёртки предоставляют несколько функций, такие как гарантирование того, что вы получаете экземпляры Request в своём представлении, и добавление контекста к объектам Response для выполнения переговорного протокола контента.
Обёртки также обеспечивают поведение, такое как возвращение ответов 405 Method Not Allowed при необходимости и обработку любых исключений ParseError , возникающих при доступе к 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 форм. Мы также используем именованные коды состояния, что делает значения ответа более очевидными.
Вот представление для отдельного фрагмента в модуле views.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)
Всё это должно быть очень знакомо - это не сильно отличается от работы с обычными представлениями Django.
Обратите внимание, что мы больше не явно связываем наши запросы или ответы с конкретным типом контента. request.data может обрабатывать входящие json запросы, но также может обрабатывать и другие форматы. Аналогично, мы возвращаем объекты ответа с данными, но позволяем REST фреймворку отобразить ответ в правильный тип контента для нас.
Добавление необязательных суффиксов формата к нашим URL
Чтобы воспользоваться тем, что наши ответы больше не привязаны к одному типу контента, добавим поддержку суффиксов форматов к нашим конечным точкам API. Использование суффиксов форматов даёт нам URL, которые явно ссылаются на определённый формат, и означает, что наш API сможет обрабатывать URL, такие как http://example.com/api/items/4.json.
Начните с добавления ключевого аргумента format в оба представления, как показано ниже.
def snippet_list(request, format=None):
и
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)
Мы необязательно должны добавить эти дополнительные шаблоны 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
Аналогично, мы можем управлять форматом запроса, который отправляем, с помощью заголовка 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 к запросам http выше, вы сможете увидеть тип запроса в заголовках запроса.
Теперь откройте API в веб-браузере, перейдя по адресу http://127.0.0.1:8000/snippets/.
Возможность просмотра
Поскольку API выбирает тип контента ответа на основе запроса клиента, по умолчанию он будет возвращать HTML-версию ресурса, когда этот ресурс запрашивается веб-браузером. Это позволяет API возвращать полностью просматриваемое в веб-браузере представление HTML.
Возможность просмотра API — это очень удобная функция, которая значительно упрощает разработку и использование вашего API. Она также значительно снижает порог вхождения для других разработчиков, желающих исследовать и работать с вашим API.
Более подробную информацию о функции просматриваемого API и способах его настройки можно найти в разделе просматриваемого API.
Что дальше?
В части 3 руководства мы начнём использовать представления на основе классов и увидим, как обобщённые представления уменьшают объём необходимого кода.
Copyright © 2011–present Encode OSS Ltd.
Licensed under the BSD License.
https://www.django-rest-framework.org/tutorial/2-requests-and-responses/