博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Django----djagorest-framwork源码剖析
阅读量:5138 次
发布时间:2019-06-13

本文共 38737 字,大约阅读时间需要 129 分钟。

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
[v1|v2]+)/users/$', users_list, name='users-list'), url(r'^(?P
[v1|v2]+)/users/(?P
[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请求,响应内容')

  

转载于:https://www.cnblogs.com/w-s-l123/p/9459188.html

你可能感兴趣的文章
vue
查看>>
创建vue项目
查看>>
javascript let
查看>>
elasticsearch
查看>>
elasticsearch 映射 dynamic用法
查看>>
php-fpm的参数优化
查看>>
vim 使用
查看>>
shell 监控
查看>>
ssh 免密码登录
查看>>
elasticsearch sql查询
查看>>
基于mysqld_multi实现MySQL 5.7.24多实例多进程配置
查看>>
shell top
查看>>
es6
查看>>
filebeat
查看>>
es6
查看>>
filebeat
查看>>
授权某个数据库某个表权限
查看>>
elasticsearch 安装
查看>>
vue 结构赋值
查看>>
mac必装软件
查看>>