DRF-RequestsAndResponses
Request objects
DRF提供了Request
请求来扩展Django通用的HttpRequest
,并且提供了更为灵活的请求解析。
Request parsing
DRF框架使用中间件实现了将客户端发过来的请求从Django的HttpRequest
解析成了DRF的Request
,所以我们一般只需要了解如何使用这个Request
.
.data
Request
类的核心功能是request.data
属性,与request.POST
类似,但更适用于Web APIs
。
request.POST # Only handles form data. Only works for 'POST' method.
request.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.
.query_params
request.query_params
与Django自带的request.GET
类似。
为了在代码中清晰起见, 我们建议使用request.query_params
来代替 Django 的标准request.GET
。
.parsers
APIView
类或者@api_view
装饰器会确保这个属性会被自动设置成Parser
实例的列表。当然,这个实例的列表取决于view的parser_classes
或者配置文件settings.py
中的DEFAULT_PARSER_CLASSES
设置。
Note: 如果客户端发送了错误格式的内容,访问
request.data
可能会抛出ParseError
异常。默认的APIView
类或@api_view
装饰器会捕获这个异常,然后返回400 Bad Request
。
Context negotiation
Request
公开了一些属性来允许我们决定内容协商阶段的结果。
.accepted_renderer
内容协商阶段选择的渲染器实例
.accepted_media_type
一个字符串, 表示内容协商阶段接受的媒体类型
Authentication
DRF框架提供了灵活的、面向每个请求的的认证,使我们能够:
对 API 的不同部分使用不同的身份验证策略;
支持使用多个身份验证策略;
提供与传入请求关联的用户和令牌信息。
.user
request.user
通常返回django.contrib.auth.models.User
的一个实例, 当然,这取决于使用的认证策略。
如果请求未经验证,那么,request.user
默认是django.contrib.auth.models.AnonymousUser
的一个实例。
.auth
request.auth
返回任何额外的身份验证上下文信息。
request.auth
的确切行为取决于所使用的身份验证策略, 但它通常可能是对请求进行身份验证的令牌的实例。
如果请求未经身份验证, 或者不存在其他上下文, 则request.auth
的默认值为 None
。
.authenticators
APIView
类或者@api_view
装饰器会确保这个属性会被自动设置成Authentication
实例的列表。当然,这个实例的列表取决于view的pauthentication_classes
或者配置文件settings.py
中的DEFAULT_AUTHENTICATORS
设置。
Browser enhancements
DRF框架支持一些浏览器增强功能, 如基于浏览器的 put
、patch
和 delete
表单。
.method
request.method
返回请求HTTP
方法的大写字符串。
明确地支持基于浏览器的 put
、patch
和 delete
表单方法。
.content_type
request.content_type
,返回表达HTTP请求Body媒体类型的字符串对象。没有提供媒体类型的话,则返回空字符串。
通常不需要直接访问请求的内容类型, 因为其一般依赖于DRF框架的默认请求分析行为。
如果我们确实需要访问请求的内容类型, 则应优先使用request.content_type
而不是request.META.get('HTTP_CONTENT_TYPE')
, 因为它为器的非表单内容提供透明的支持。
.stream
request.stream
返回表示请求正文内容的流。
Response objects
DRF同样提供了Response
,继承自Django的SimpleTemplateResponse
。
return Response(data)
来看看其部分源码:
class Response(SimpleTemplateResponse):
"""
An HttpResponse that allows its data to be rendered into
arbitrary media types.
"""
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
"""
Alters the init arguments slightly.
For example, drop 'template_name', and instead use 'data'.
Setting 'renderer' and 'media_type' will typically be deferred,
For example being set automatically by the `APIView`.
"""
super(Response, self).__init__(None, status=status)
if isinstance(data, Serializer):
msg = (
'You passed a Serializer instance as data, but '
'probably meant to pass serialized `.data` or '
'`.error`. representation.'
)
raise AssertionError(msg)
self.data = data
self.template_name = template_name
self.exception = exception
self.content_type = content_type
if headers:
for name, value in six.iteritems(headers):
self[name] = value
可以看到构造返回的Reponsse
时,可指定:
- data: 具体返回的数据;
- status: 返回的状态,具体对应DRF
status
模块的状态码,默认返回200; - template_name:如果选择了
HTMLRenderer
,则可用此参数指定模板名; - headers: Http headers 字典;
- content_type:response的内容类型。
Attributes
.data
response
的未渲染的序列化数据。
.status_code
HTTP response
的数字状态码.
.content
response
渲染后的内容。.render()
方法必须先调用,然后才能访问.content
.
.template_name
模板名称 (如果提供)。仅在response
接受的渲染器是 HTMLRenderer
或其他一些自定义模板渲染器时才需要。
.accepted_renderer
将被用来渲染response
的渲染器实例。
由 APIView
或@api_view
在从view
返回response
之前自动设置.
.accepted_media_type
内容协商阶段选择的媒体类型.
由 APIView
或@api_view
在从view
返回response
之前自动设置.
.renderer_context
就要传递给渲染器的.render()
方法的额外上下文信息字典。
由 APIView
或@api_view
在从view
返回response
之前自动设置.
Standard HttpResponse attributes
Response
,继承自SimpleTemplateResponse
,所有常用的属性和方法也可在response
上使用。例如, 您可以以标准方式在响应上设置标头:
response = Response()
response['Cache-Control'] = 'no-cache'
.render()
和TemplateResponse
一样,这个方法用于将response
序列化的数据渲染成最后的responsse context
。当.render
被调用时,实际上会去调用accepted_renderer
上的render(data, accepted_media_type, renderer_context)``response context
方法。
Pulling it all together
让我们再用之前章节基于函数的视图的例子来看看request
和response
的使用:
@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)
上面的例子中,reques.data
可以处理传入的 json
请求, 当然也可以处理其他格式的请求。同时返回的结果也不再需要JSONResponse
来转换成json
格式。
最后让我们调用接口,来看看返回的结果是怎么样的?
# 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"
}