文章目录
1. DRF是如何实现authentication
的?
authentication(认证)
:所谓认证就是身份的认证,是否登陆,确定你是谁。
Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The permission and throttling policies can then use those credentials to determine if the request should be permitted.
就简单的authentication而言,是验证用户名和密码是否都能匹配上。如果是SessionAuthentication,TokenAuthentication则是分别增加了csrf的验证,token的验证。
不要忘记,身份验证本身不会允许或不允许request,它仅会标识发出request的凭据,也就是返回request.user和request.auth。
如果认证通过,返回值写入request.user
和request.auth
中。
2. DRF是如何实现permisson
验证的?
permission(权限)
:确定你可以访问哪些接口。
DRF利用authentication
后返回的request.user
和request.auth
数据进行权限验证。
在DRF中有两种permission
,分别是view level permission
和object level permission
。处于性能原因,并没有对所有的请求做object level permission。
# rest_framework/permissions.py
class BasePermission(metaclass=BasePermissionMetaclass):
"""
A base class from which all permission classes should inherit.
"""
def has_permission(self, request, view):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
def has_object_permission(self, request, view, obj):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
每一个Permission
类都是一种权限的配置模式,需要重写has_permission
或者has_object_permission
方法来决定如何决定request
是否具有permission
。
3. DRF是如何实现throtting
的?
throttling(限流)
:限制访问接口的频率等。
throttling
与permission
类似,它决定是否应授权request
。 限流用于控制客户端可以向API发出的request
的速率(一段时间内只能请求多少次),或者带宽等。
其机制
是:
在 Throttle
中有一个self.history
(一个dict
,规定时间间隔内客户端的访问时间戳的列表),key为客户端ip,value为客户端请求的timestamp
的列表,这样就能限制客户端在规定时间间隔内的请求频率了。如果有爬虫来频繁的访问你的API,这样就可以限制其访问次数,降低服务器的压力。
4. DRF是如何实现parser
的?
parser
:解析器。
在DRF中,parser的作用是将前端传过来的数据转换成python数据格式。
根据request header
中的 Content-Type
决定使用什么parser
来解析数据。
以JSONParser
为例:
# rest_framework/parsers.py
class JSONParser(BaseParser):
"""
Parses JSON-serialized data.
"""
media_type = 'application/json'
renderer_class = renderers.JSONRenderer
strict = api_settings.STRICT_JSON
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as JSON and returns the resulting data.
"""
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
try:
decoded_stream = codecs.getreader(encoding)(stream)
parse_constant = json.strict_constant if self.strict else None
return json.load(decoded_stream, parse_constant=parse_constant)
except ValueError as exc:
raise ParseError('JSON parse error - %s' % str(exc))
5. DRF是如何实现serialization
的?
在Django REST Framework
中通过serializer
对客户端传递过来的数据进行 反序列化
,或者对查询的出来的 model
对象进行序列化
。
serializing
:将对象 -> python数据格式(如:dict)deserializing
:将python数据格式 -> 对象
# models.py
from datetime import datetime
class Comment(object):
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
# serializers.py
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
# python3 manage.py shell_plus
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
from rest_framework.renderers import JSONRenderer
json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
上面的例子中,通过CommentSerializer
实例将comment这个model
实例实例对象进行序列化
,序列化
的结果是一个dict
。然后通过JSONRenderer
中的render()
实例方法将这个序列化后的数据转换成json
数据格式,最终传递给客户端。
前端传入数据创建一个model或是修改model的过程如下图所示:
graph TD
前端数据 --> Router
Router --> View 或 ViewSet
view --> parser
parser --> serializer.is_valid方法验证数据
serializer.is_valid方法验证数据 --> serilaizer.save方法
serilaizer.save方法 --> Model.objects.create方法
Model.objects.create方法--> 将对model实例对象的更改应用到db中
6. DRF是如何实现renderer
的?
renderer
:渲染器
renderer
用于将serialize(序列化)
后的response
转换为特定的媒体类型,这个媒体类型通常为客户端发起请求时请求头中的Accept
的值所决定的。
以JSONRenderer
为例:
# rest_framework/renderers.py
class JSONRenderer(BaseRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Render `data` into JSON, returning a bytestring.
"""
if data is None:
return b''
renderer_context = renderer_context or {}
indent = self.get_indent(accepted_media_type, renderer_context)
if indent is None:
separators = SHORT_SEPARATORS if self.compact else LONG_SEPARATORS
else:
separators = INDENT_SEPARATORS
ret = json.dumps(
data, cls=self.encoder_class,
indent=indent, ensure_ascii=self.ensure_ascii,
allow_nan=not self.strict, separators=separators
)
# We always fully escape \u2028 and \u2029 to ensure we output JSON
# that is a strict javascript subset.
# See: http://timelessrepo.com/json-isnt-a-javascript-subset
ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029')
return ret.encode()
实际就是通过json.dumps()
将python数据格式转换成了json格式。
7. DRF是如何实现validation
的?
validation
也就是验证。
DRF对前端传递过来的数据需要验证之后才能应用到model
上。而验证是针对这些需要验证的field
而言的。以EmailField
字段为例:
# rest_framework/fields.py
# EmailField 中的validator是 EmailValidator,当前端传递过来的数据需要验证时候,调用EmailValidator的实例对象进行验证
class EmailField(CharField):
default_error_messages = {
'invalid': _('Enter a valid email address.')
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
validator = EmailValidator(message=self.error_messages['invalid'])
self.validators.append(validator)
看看Django中的EmailValidator
# django/core/validators.py
@deconstructible
class EmailValidator:
......此处省略若干代码......
def __call__(self, value):
if not value or '@' not in value:
raise ValidationError(self.message, code=self.code)
user_part, domain_part = value.rsplit('@', 1)
if not self.user_regex.match(user_part):
raise ValidationError(self.message, code=self.code)
if (domain_part not in self.domain_whitelist and
not self.validate_domain_part(domain_part)):
# Try for possible IDN domain-part
try:
domain_part = punycode(domain_part)
except UnicodeError:
pass
else:
if self.validate_domain_part(domain_part):
return
raise ValidationError(self.message, code=self.code)