restful(表者征状态转移,面向资源编程)------------------------------------------->约定
从资源的角度审视整个网络,将分布在网络中某个节点的资源通过url进行标识,客户端通过url获取资源的表征,
获得这些表征使应用转变状态。-----------------------------------------------------------是一种软件架构风格。
所有数据是通过网络获取的是操作的数据(增删改查),都是资源-------------------------------互联网上的一切东西都视为资源。 restf规则:
API与用户的通信协议,使用的是http协议
1:域名尽量部署在专有域名之下,若API很简单,不会进一步扩展,可以考虑放在主域名下。
2:应将api的版本号放入url,还可以将版本号放入Http请求头信息中,但不如放在url中方便。
3:在RESTful架构中,每个网址代表一种资源(resource),所以网址中应该有动词,应该使用名词,
而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
4:用于区别url接口应将API加入到url.
5: 如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
6: 服务器向用户返回的状态码和提示信息。
8: 如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
9: 请求方式的不同,进行不同的操作。post----get----put----patch----delete
10:返回错误信息 restful-api:
API与用户的通行协议,总是使用HTTPs协议
api:---------------------------------------------------------------------------------------接口
用途:
1:为别人提供服务----------发送短信
2:前后端分离--------------前后端分离
规范:
1:url+api
https://api.example.com------------------------------------------------------------尽量将API部署在专用域名(会存在跨域问题)
https://example.org/api/-----------------------------------------------------------API很简单
2:名词
资源名必须是名词,不能是动词.......
3:版本
URL,-------------------------------------------------------------------------------如:https://api.example.com/v1/
请求头------------------------------------------------------------------------------跨域时,引发发送多次请求
4:提交方式------------------------------------------------------------method
GET:-------------------------------------------------------------------------------从服务器取出资源(一项或多项)
POST:------------------------------------------------------------------------------在服务器新建一个资源
PUT:-------------------------------------------------------------------------------在服务器更新资源(客户端提供改变后的完整资源)
PATCH :----------------------------------------------------------------------------在服务器更新资源(客户端提供改变的属性)
DELETE:----------------------------------------------------------------------------从服务器删除资源
5:json数据------------------------------------------------------------返回json数据
6:status--------------------------------------------------------------状态码
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
7:aypermedia link-----------------------------------------------------返回链接
Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "List of zoos",
"type": "application/vnd.yourformat+json"
}}
8:错误处理
错误处理,状态码是4xx时,应返回错误信息,error当做key。
{
error: "Invalid API key"
} 为什么做前后端分离?
数据的解耦,提高开发效率。 安装:
pip3 install djangorestframework -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com 继承关系:
class View(object):-------------------------------------------------view class APIView(View):------------------------------------------------APIview class GenericAPIView(views.APIView):--------------------------------GenericAPIView class GenericViewSet(ViewSetMixin, generics.GenericAPIView)---------GenericViewSet class ModelViewSet(mixins.CreateModelMixin,-------------------------ModelViewSet(增删改查,genericViewset)
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet): Django Rest Framework 的的请求生命周期:
hTTP请求 —> wsgi —> 中间件 —> 路由分发 —> 执行对应类的dispatch方法 —> 视图函数 —>返回
采用CBV的请求方式。 源码剖析
接收HTTP请求---->wsgi----->中间件------->路由分发---------->执行对应类的dispatch方法------->视图函数------>返回 首先执行 as_view 我们可以知道,as_view调用了dispatch.
执行:------------------------>执行对应类的dispatch方法:---------------------dispatch
一:第一步对------------------------------------------------------------------request二次封装 1:--查看initialize_request方法,可以知道这个方法接收客户端的request请求,再重新封装成新的request。
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request) return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
点击进入Request
2:----再查看Request方法的源码,可以知道这个Request类是rest framework中定义的一个类
----Rquest类,这来类是经过Request处理过的request已经不再是客户端发送过来的那个request了
class Request(object):
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty if self.parser_context is None:
self.parser_context = {}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET force_user = getattr(request, '_force_auth_user', None)
force_token = getattr(request, '_force_auth_token', None)
if force_user is not None or force_token is not None:
forced_auth = ForcedAuthentication(force_user, force_token)
self.authenticators = (forced_auth,)
3:----在initialize_request方法中,有一个方法处理过request,来看看get_parser_context方法的源码
在这里,view的值是self,代指的是UsersView这个对象,所以get_parser_context方法把UsersView这个类封装进来然后返回
所以get_parser_context方法最后返回的当前对象以及当前对象所传的参数,经过initialize_request函数处理之后的request,现在就变成了
Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
def get_parser_context(self, http_request):
return {
'view': self, #代指的是UsersView这个对象
'args': getattr(self, 'args', ()),
'kwargs': getattr(self, 'kwargs', {})
}
4:----现在再来看看Request的其他参数代指的是什么
get_parsers------------------根据字面意思,是解析get请求的意思
def get_parsers(self):
return [parser() for parser in self.parser_classes]
get_content_negotiator-------选择相关
def get_content_negotiator(self):
if not getattr(self, '_negotiator', None):
self._negotiator = self.content_negotiation_class()
return self._negotiator
parser_context---------------封闭self和self的参数
parser_context = self.get_parser_context(request) *get_authenticators----------------------认证相关,在get_authenticators这个方法中循环了self.authentication_classes返回了一个列表对象,
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
----------他是self.找的,所有它先去我们写的那个类中去找authentication_classes,我们写的类中没有才去父类中找,因此我们就可以自定义这个列表类了。
进入APIview类找------------------authentication_classes
class APIView(View):
# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES #认证
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES #权限
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES #分流 content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
..............
------------authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES由此可以看出:
它默认的authentication_classes是从它的配置文件中读出来的。现在就可以去看看它配置文件中的类了。
-------------进入api_settings可以看到api_settings=APISettings
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
-----------进入APISettings,默认的authentication_classes是从它的配置文件中读出来的。现在就可以去看看它配置文件中的类了
DEFAULTS = {
# Base API policies
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_THROTTLE_CLASSES': (),
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None, # Generic view behavior
'DEFAULT_PAGINATION_CLASS': None,
'DEFAULT_FILTER_BACKENDS': (), # Schema
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', # Throttling
'DEFAULT_THROTTLE_RATES': {
'user': None,
'anon': None,
},
'NUM_PROXIES': None, # Pagination
'PAGE_SIZE': None, # Filtering
'SEARCH_PARAM': 'search',
'ORDERING_PARAM': 'ordering', # Versioning
'DEFAULT_VERSION': None,
'ALLOWED_VERSIONS': None,
'VERSION_PARAM': 'version', # Authentication
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None, # View configuration
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description', # Exception handling
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'NON_FIELD_ERRORS_KEY': 'non_field_errors', # Testing
'TEST_REQUEST_RENDERER_CLASSES': (
'rest_framework.renderers.MultiPartRenderer',
'rest_framework.renderers.JSONRenderer'
),
'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', # Hyperlink settings
'URL_FORMAT_OVERRIDE': 'format',
'FORMAT_SUFFIX_KWARG': 'format',
'URL_FIELD_NAME': 'url', # Input and output formats
'DATE_FORMAT': ISO_8601,
'DATE_INPUT_FORMATS': (ISO_8601,), 'DATETIME_FORMAT': ISO_8601,
'DATETIME_INPUT_FORMATS': (ISO_8601,), 'TIME_FORMAT': ISO_8601,
'TIME_INPUT_FORMATS': (ISO_8601,), # Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True, # Browseable API
'HTML_SELECT_CUTOFF': 1000,
'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...", # Schemas
'SCHEMA_COERCE_PATH_PK': True,
'SCHEMA_COERCE_METHOD_NAMES': {
'retrieve': 'read',
'destroy': 'delete'
},
}
-----------找到DEFAULT_AUTHENTICATION_CLASSES可以看出他有两个类是在authentication中:SessionAuthentication,BasicAuthentication,
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
--------------from rest_framework import authentication进入
--------------SessionAuthentication
class SessionAuthentication(BaseAuthentication):
"""
Use Django's session framework for authentication.
""" def authenticate(self, request):
"""
Returns a `User` if the request session currently has a logged in user.
Otherwise returns `None`.
""" # Get the session-based user from the underlying HttpRequest object
user = getattr(request._request, 'user', None) # Unauthenticated, CSRF validation not required
if not user or not user.is_active:
return None self.enforce_csrf(request) # CSRF passed with authenticated user
return (user, None) def enforce_csrf(self, request):
"""
Enforce CSRF validation for session based authentication.
"""
reason = CSRFCheck().process_view(request, None, (), {})
if reason:
# CSRF failed, bail with explicit error message
raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)
--------------BasicAuthentication
class BasicAuthentication(BaseAuthentication):
"""
HTTP Basic authentication against username/password.
"""
www_authenticate_realm = 'api' def authenticate(self, request):
"""
Returns a `User` if a correct username and password have been supplied
using HTTP Basic authentication. Otherwise returns `None`.
"""
auth = get_authorization_header(request).split() if not auth or auth[0].lower() != b'basic':
return None if len(auth) == 1:
msg = _('Invalid basic header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid basic header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg) try:
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
except (TypeError, UnicodeDecodeError, binascii.Error):
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg) userid, password = auth_parts[0], auth_parts[2]
return self.authenticate_credentials(userid, password, request) def authenticate_credentials(self, userid, password, request=None):
"""
Authenticate the userid and password against username and password
with optional request for context.
"""
credentials = {
get_user_model().USERNAME_FIELD: userid,
'password': password
}
user = authenticate(request=request, **credentials) if user is None:
raise exceptions.AuthenticationFailed(_('Invalid username/password.')) if not user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) return (user, None) def authenticate_header(self, request):
return 'Basic realm="%s"' % self.www_authenticate_realm
--------------可以看出他们都继承了一个BaseAuthentication的类并且都实现了authenticate方法。
class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
""" def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request):
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
pass
--------------进入authenticate,由此可以看出BaseAuthentication类其实是一个接口类,让继承它的类必须实现authenticate方法。
最后就是在request对象中的authenticatotes中封装了一些关于认证的对。
def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
raise NotImplementedError(".authenticate() must be overridden.")
-----------自己添加配置文件-------settings:若将自己定义的认证类添加的返回的列表,就通过seettings的配置走自己的定义的认证类
EST_FRAMEWORK = {
'UNAUTHENTICATED_USER': None,
'UNAUTHENTICATED_TOKEN': None,
"DEFAULT_AUTHENTICATION_CLASSES": [
# "app01.utils.MyAuthentication",
],
'DEFAULT_PERMISSION_CLASSES':[ ],
'DEFAULT_THROTTLE_RATES':{
'wdp_anon':'5/minute',
'wdp_user':'10/minute', }
} 5:---再来看看UsersView这个类中的get方法和post方法----------------------------------------------------------------UserView
可以看到get方法的参数中有一个request,通过前面可以知道这个request已经不是最开始时到达服务端的request了
这个request方法中已经被REST framework封装了解析,认证和选择等相关的方法
def get(self,request,*args,**kwargs):
pass
def post(self,request,*args,**kwargs):
pass
6:---efault_response_headers这个方法从它的注释可以看出已经被丢弃了. 二:初始化--------------------------------------------------------------------获取版本-----认证-----权限-----分流
7:---再来看initial这个方法
def initial(self, request, *args, **kwargs):
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
#执行内容协商并将接受的信息存储在请求上
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
#如果正在使用版本控制,请确定API版本。
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
#确保传入的请求是允许的。
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
----先执行get_format_suffix-------------------------------------------------------------来获取客户端所发送的url的后缀 ----然后执行perform_content_negotiation方法,--------------------------------------------这个方法的主要作用是执行内容选择,并把服务端接收到的信息保存在request中 获取版本----然后再执行determine_version方法---------------------------------------------如果url中有版本信息,就获取发送到服务端的版本,返回一个元组
-------version,schemas是执行determine_version获得的,versioning_scheme是设置类的对象也就是QueryParameterVersioning。
request.version就是QueryParameterVersioning执行etermine_version
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
--------执行determine_version,如果versioning是空的,就返回两个空。,不为空走versioning设置值。
def determine_version(self, request, *args, **kwargs):
if self.versioning_class is None:
return (None, None)
scheme = self.versioning_class()
return (scheme.determine_version(request, *args, **kwargs), scheme)
---------走versiong_class设置值,走api_settings配置找DEFAULT_VERSIONING_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
----------走api_settings寻找配置
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
---------api_settings = APISettings,走APISettings找DEFAULT_VERSIONING_CLASS。默认为空,需自己设置。
'DEFAULT_VERSIONING_CLASS': None,
---------进入QueryParameterVersioning
class QueryParameterVersioning(BaseVersioning):
"""
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in query parameter.') def determine_version(self, request, *args, **kwargs):
version = request.query_params.get(self.version_param, self.default_version)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
url = super(QueryParameterVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
if request.version is not None:
return replace_query_param(url, self.version_param, request.version)
return url
---------获取version,version_param就是配置
def determine_version(self, request, *args, **kwargs):
version = request.query_params.get(self.version_param, self.default_version)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version
---------进入version_param,在配置中找VERSION_PARAM
version_param = api_settings.VERSION_PARAM
---------由配置可知VERSION_PARAM等于version.
'VERSION_PARAM': 'version',
---------default_version=配置中的DEFAULT_VERSION
default_version = api_settings.DEFAULT_VERSION
---------进入配置找DEFAULT_VERSION,可以知道我们可以在setting中自己配置
---------is_allowed_version是允许的版本,也可自己在seettings中配置。流程相似
-------from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning
---------QueryParameterVersioning
class QueryParameterVersioning(BaseVersioning):
"""
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in query parameter.') def determine_version(self, request, *args, **kwargs):
version = request.query_params.get(self.version_param, self.default_version)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
url = super(QueryParameterVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
if request.version is not None:
return replace_query_param(url, self.version_param, request.version)
return url
--------执行determine_version获取版本
def determine_version(self, request, *args, **kwargs):
version = request.query_params.get(self.version_param, self.default_version)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version
--------执行reverse反向生成url
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
url = super(QueryParameterVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
if request.version is not None:
return replace_query_param(url, self.version_param, request.version)
return url
---------URLPathVersioning
class URLPathVersioning(BaseVersioning):
"""
To the client this is the same style as `NamespaceVersioning`.
The difference is in the backend - this implementation uses
Django's URL keyword arguments to determine the version. An example URL conf for two views that accept two different versions. urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
] GET /1.0/something/ HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in URL path.') def determine_version(self, request, *args, **kwargs):
version = kwargs.get(self.version_param, self.default_version)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None:
kwargs = {} if (kwargs is None) else kwargs
kwargs[self.version_param] = request.version return super(URLPathVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
---------自己在settings中配置
REST_FRAMEWORK = {
'VERSION_PARAM':'version',
'DEFAULT_VERSION':'v1',
'ALLOWED_VERSIONS':['v1','v2'],
# 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning"
'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning"
}
--------执行determine_version获取版本
def determine_version(self, request, *args, **kwargs):
version = kwargs.get(self.version_param, self.default_version)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version
---------执行reverse反向生成url
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None:
kwargs = {} if (kwargs is None) else kwargs
kwargs[self.version_param] = request.version return super(URLPathVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
-------自定制settings
REST_FRAMEWORK = {
'VERSION_PARAM':'version',
'DEFAULT_VERSION':'v1',
'ALLOWED_VERSIONS':['v1','v2'],
# 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning"
# 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning"
} 认证-----执行完上面的方法,再执行perform_authentication方法来进行认证操作----------------执行认证功能,确认进行后续操作的用户是被允许的,
perform_authentication方法返回经过认证的用户对象,
传入的request是重新封装过的。
def perform_authentication(self, request):
request.user
------然后就在request.user中执行authenticate这个方法进行认证
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user 检查权限-----执行check_permissions方法--------------------------------------------------如果用户通过认证,检查用户是否有权限访问url中所传的路径,
如用用户访问的是没有没有权限的路径,则会抛出异常.
def check_permissions(self, request):
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
------循环,执行get_permissions返回一个列表对象。
def get_permissions(self):
return [permission() for permission in self.permission_classes]
--------执行permission_classes,self是当前类,所以是去当前类中找,当前类中没有去父类(APIView)中找,所以可以自己定制。
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
----------可以看出是从api_settings中找到,我们执行api_settings
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
-------------进入APISettings,在DEFAULTS配置文件中找DEFAULT_PERMISSION_CLASSES,若自定制,自己在setting中配置。
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
-------------根据配置文件可知:我们走permissions的AllowAny类。
from rest_framework.permissions import AllowAny进入
-------------AllowAny执行了has_permission,返回True.
class AllowAny(BasePermission):
"""
Allow any access.
This isn't strictly required, since you could use an empty
permission_classes list, but it's useful because it makes the intention
more explicit.
""" def has_permission(self, request, view):
return True
),
-------------可以看出AllowAny继承了BasePermission,由此我们可以知道必须执行一个AllowAny
自己有执行自己的,自己没有没有执行父类的,都返回True
class BasePermission(object):
"""
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 检查限制访问(分流)-----就会执行check_throttles方法--------------------------------------作用是检查用户是否被限制了访问主机的次数
self.check_throttles(request) 如果用户访问服务器的次数超出设定值,则会抛出一个异常
---------例如,如果想限制一个ip地址每秒钟只能访问几次,一个小时之内最多可以访问多少次,就可以在settings.py文件中进行配置
def check_throttles(self, request):
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
self.throttled(request, throttle.wait())
----------循环执行,执行get_throttles,返回一个列表对象
def get_throttles(self):
"""
Instantiates and returns the list of throttles that this view uses.
"""
return [throttle() for throttle in self.throttle_classes]
----------执行throttle_classes,self是当前类,请求过来首先在自己的类中找,没有去父类中找(APIView)
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
----------可以看出是从api_settings中找到,我们执行api_settings
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
----------进入APISettings,在DEFAULTS配置文件中找DEFAULT_PERMISSION_CLASSES,若自定制,自己在setting中配置
'DEFAULT_THROTTLE_CLASSES': (),
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None,
----------根据配置文件可知:
from rest_framework.permissions import AllowAny进入 三:执行对应的视图函数
8:---initial这个方法执行完成后,request.method.lower把请求的方法转换成小写
Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed response = handler(request, *args, **kwargs)
9:---再通过通过反射的方式来执行UsersView类中的get或post等自定义方法要注意的是,在执行initial方法之前,使用了try/except方法来进行异常处理
如果执行initial方法的时候出现错误,就调用handle_exception来处理initial方法抛出的异常,返回正确的响应信息
def handle_exception(self, exc):
if isinstance(exc, (exceptions.NotAuthenticated,
exceptions.AuthenticationFailed)):
# WWW-Authenticate header for 401 responses, else coerce to 403
auth_header = self.get_authenticate_header(self.request) if auth_header:
exc.auth_header = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN exception_handler = self.get_exception_handler() context = self.get_exception_handler_context()
response = exception_handler(exc, context) if response is None:
self.raise_uncaught_exception(exc) response.exception = True
return response 10:---在前面,如果initial方法执行完成没有抛出异常,则根据反射执行自定义的请求方法,然后返回响应信息如果initial方法抛出异常则执行handle_exception
方法处理抛出的异常,也返回响应信息等到上面的过程执行完成后,再执行finalize_response方法把最终的响应信息返回给客户端的浏览器
def finalize_response(self, request, response, *args, **kwargs):
# Make the error obvious if a proper response is not returned
assert isinstance(response, HttpResponseBase), (
'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` '
'to be returned from the view, but received a `%s`'
% type(response)
) if isinstance(response, Response):
if not getattr(request, 'accepted_renderer', None):
neg = self.perform_content_negotiation(request, force=True)
request.accepted_renderer, request.accepted_media_type = neg response.accepted_renderer = request.accepted_renderer
response.accepted_media_type = request.accepted_media_type
response.renderer_context = self.get_renderer_context() # Add new vary headers to the response instead of overwriting.
vary_headers = self.headers.pop('Vary', None)
if vary_headers is not None:
patch_vary_headers(response, cc_delim_re.split(vary_headers)) for key, value in self.headers.items():
response[key] = value return response def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# ####################### 第一步 request二次封装 #######################
"""
return Request(
request,
parsers=self.get_parsers(), 解析相关 对象列表
authenticators=self.get_authenticators(), 认证相关 对象列表
negotiator=self.get_content_negotiator(), 选择相关 选择对象
parser_context=parser_context 解析内容
)
"""
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate? # ####################### 第二步 初始化 #######################
"""
2.1 获取版本
返回(scheme.determine_version(request, *args, **kwargs), scheme)
request.version, request.versioning_scheme =版本号,检查版本的对象
2.2 认证
self.perform_authentication(request)
调用request.user方法
2.3 检查权限
self.check_permissions(request)
获取权限的对象列表
执行对象.has_permission方法 返回True有权限,返回False没有权限,抛出异常,message定制错误信息。
2.4 检查限制访问
self.check_throttles(request)
获取限制类的对象列表
执行对象.allow_request(request, self) 返回True可以访问,返回False限制访问。 """
try:
self.initial(request, *args, **kwargs)
# ####################### 第三步 执行对应的视图函数 ####################### # Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc:
response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response 基本流程
1:请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发。
2:重要的功能是在APIView的dispatch中触发。
url.py
from django.conf.urls import url, include
from web.views.s1_api import TestView
urlpatterns = [
url(r'^test/', TestView.as_view()),
]
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
def dispatch(self, request, *args, **kwargs):
"""
请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法
注意:APIView中的dispatch方法有好多好多的功能
"""
return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs):
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

  

Django----djagorest-framwork源码剖析的更多相关文章

  1. Django Rest Framework源码剖析(八)-----视图与路由

    一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使 ...

  2. Django Rest Framework源码剖析(三)-----频率控制

    一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...

  3. 跨站请求伪造(csrf),django的settings源码剖析,django的auth模块

    目录 一.跨站请求伪造(csrf) 1. 什么是csrf 2. 钓鱼网站原理 3. 如何解决csrf (1)思路: (2)实现方法 (3)实现的具体代码 3. csrf相关的装饰器 (1)csrf_p ...

  4. Django Rest Framework源码剖析(七)-----分页

    一.简介 分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案.当然django rest framework提供 ...

  5. Django Rest Framework源码剖析(六)-----序列化(serializers)

    一.简介 django rest framework 中的序列化组件,可以说是其核心组件,也是我们平时使用最多的组件,它不仅仅有序列化功能,更提供了数据验证的功能(与django中的form类似). ...

  6. Django Rest Framework源码剖析(五)-----解析器

    一.简介 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数据格式,比如xml,所以需要解析这类数 ...

  7. Django Rest Framework源码剖析(四)-----API版本

    一.简介 在我们给外部提供的API中,可会存在多个版本,不同的版本可能对应的功能不同,所以这时候版本使用就显得尤为重要,django rest framework也为我们提供了多种版本使用方法. 二. ...

  8. Django Rest Framework源码剖析(二)-----权限

    一.简介 在上一篇博客中已经介绍了django rest framework 对于认证的源码流程,以及实现过程,当用户经过认证之后下一步就是涉及到权限的问题.比如订单的业务只能VIP才能查看,所以这时 ...

  9. Django REST framework 源码剖析

    前言 Django REST framework is a powerful and flexible toolkit for building Web APIs. 本文由浅入深的引入Django R ...

  10. Django Rest Framework源码剖析(一)-----认证

    一.简介 Django REST Framework(简称DRF),是一个用于构建Web API的强大且灵活的工具包. 先说说REST:REST是一种Web API设计标准,是目前比较成熟的一套互联网 ...

随机推荐

  1. 详细解读Android中的搜索框(三)—— SearchView

    本篇讲的是如何用searchView实现搜索框,其实原理和之前的没啥差别,也算是个复习吧. 一.Manifest.xml 这里我用一个activity进行信息的输入和展示,配置方式还是老样子,写一个输 ...

  2. 【FTP】FTP服务器的搭建

    记录一下FTP服务器的搭建首先打开 程序和功能>打开或关闭Windows功能 进入到Windows功能界面:勾选FTP服务器.然后再在IIS界面,新建一个网站.右键网站,选择“添加到FTP发布” ...

  3. 译: 2. RabbitMQ Spring AMQP 之 Work Queues

    在上一篇博文中,我们写了程序来发送和接受消息从一个队列中. 在这篇博文中我们将创建一个工作队列,用于在多个工作人员之间分配耗时的任务. Work Queues 工作队列(又称:任务队列)背后的主要思想 ...

  4. vertx插件使用vertx-maven-plugin

    http://search.maven.org http://search.maven.org/#artifactdetails%7Cio.fabric8%7Cvertx-maven-plugin%7 ...

  5. java框架篇---hibernate之连接池

    Hibernate支持第三方的连接池,官方推荐的连接池是C3P0,Proxool,以及DBCP.在配置连接池时需要注意的有三点: 一.Apche的DBCP在Hibernate2中受支持,但在Hiber ...

  6. HDOJ 1393 Weird Clock(明确题意就简单了)

    Problem Description A weird clock marked from 0 to 59 has only a minute hand. It won't move until a ...

  7. iOS系统及客户端软件测试的基础介绍

    iOS系统及客户端软件测试的基础介绍 iOS现在的最新版本iOS5是10月12号推出,当前版本是4.3.5 先是硬件部分,采用iOS系统的是iPad,iPhone,iTouch这三种设备,其中iPho ...

  8. 基于Extjs 4.2的通用权限管理系统,通用后台模板,EF+MVC+Extjs 4.2

    基于Extjs 4.2的通用权限管理系统,通用后台. 我们的宗旨:珍爱生命,拒绝重复!Don't Repeat Yourself!!! 本案例采用EntityFramework+MVC4.0+Extj ...

  9. MSP MCU I2C入门指南

    这是一份介绍性指南,指导你如何用超低功耗MSP微控制器 (MCU) 开始一个与I2C通信有关的项目: 简介 I2C(或称为I2C,集成电路总线)是一种两线制通信形式,主要用来在短距离.电路板间的应用中 ...

  10. ELK+Filebeat+Kafka+ZooKeeper 构建海量日志分析平台

    日志分析平台,架构图如下: 架构解读 : (整个架构从左到右,总共分为5层) 第一层.数据采集层 最左边的是业务服务器集群,上面安装了filebeat做日志采集,同时把采集的日志分别发送给两个logs ...