Django----djagorestframwork使用

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请求,响应内容')	
	

  

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----delete10:返回错误信息
restful-api:API与用户的通行协议,总是使用HTTPs协议api:---------------------------------------------------------------------------------------接口用途:1:为别人提供服务----------发送短信2:前后端分离--------------前后端分离规范:1:url+apihttps://api.example.com------------------------------------------------------------尽量将API部署在专用域名(会存在跨域问题)https://example.org/api/-----------------------------------------------------------API很简单2:名词资源名必须是名词,不能是动词.......3:版本URL,-------------------------------------------------------------------------------如:https://api.example.com/v1/请求头------------------------------------------------------------------------------跨域时,引发发送多次请求4:提交方式------------------------------------------------------------methodGET:-------------------------------------------------------------------------------从服务器取出资源(一项或多项)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.html7: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):-------------------------------------------------viewclass APIView(View):------------------------------------------------APIviewclass GenericAPIView(views.APIView):--------------------------------GenericAPIViewclass GenericViewSet(ViewSetMixin, generics.GenericAPIView)---------GenericViewSetclass 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)点击进入Request2:----再查看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 = requestself.parsers = parsers or ()self.authenticators = authenticators or ()self.negotiator = negotiator or self._default_negotiator()self.parser_context = parser_contextself._data = Emptyself._files = Emptyself._full_data = Emptyself._content_type = Emptyself._stream = Empty
if self.parser_context is None:self.parser_context = {}self.parser_context['request'] = selfself.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._negotiatorparser_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_classesclass APIView(View):# The following policies may be set at either globally, or per-view.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSESparser_classes = api_settings.DEFAULT_PARSER_CLASSESauthentication_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_CLASSmetadata_class = api_settings.DEFAULT_METADATA_CLASSversioning_class = api_settings.DEFAULT_VERSIONING_CLASS..............------------authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES由此可以看出:它默认的authentication_classes是从它的配置文件中读出来的。现在就可以去看看它配置文件中的类了。 -------------进入api_settings可以看到api_settings=APISettingsapi_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 objectuser = getattr(request._request, 'user', None)
# Unauthenticated, CSRF validation not requiredif not user or not user.is_active:return None
self.enforce_csrf(request)
# CSRF passed with authenticated userreturn (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 messageraise 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 suppliedusing 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 passwordwith 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 theauthentication 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):passdef post(self,request,*args,**kwargs):pass6:---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,---------进入QueryParameterVersioningclass QueryParameterVersioning(BaseVersioning):"""GET /something/?version=0.1 HTTP/1.1Host: example.comAccept: 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_VERSIONdefault_version = api_settings.DEFAULT_VERSION ---------进入配置找DEFAULT_VERSION,可以知道我们可以在setting中自己配置---------is_allowed_version是允许的版本,也可自己在seettings中配置。流程相似     -------from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning---------QueryParameterVersioningclass QueryParameterVersioning(BaseVersioning):"""GET /something/?version=0.1 HTTP/1.1Host: example.comAccept: 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反向生成urldef 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---------URLPathVersioningclass URLPathVersioning(BaseVersioning):"""To the client this is the same style as `NamespaceVersioning`.The difference is in the backend - this implementation usesDjango'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.1Host: example.comAccept: 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 kwargskwargs[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反向生成urldef reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):if request.version is not None:kwargs = {} if (kwargs is None) else kwargskwargs[self.version_param] = request.version
return super(URLPathVersioning, self).reverse(viewname, args, kwargs, request, format, **extra) -------自定制settingsREST_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 authenticatedby 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_settingsapi_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 emptypermission_classes list, but it's useful because it makes the intentionmore explicit."""
def has_permission(self, request, view):return True),-------------可以看出AllowAny继承了BasePermission,由此我们可以知道必须执行一个AllowAny自己有执行自己的,自己没有没有执行父类的,都返回Trueclass 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 methodif 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 403auth_header = self.get_authenticate_header(self.request)
if auth_header:exc.auth_header = auth_headerelse: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 = Truereturn response10:---在前面,如果initial方法执行完成没有抛出异常,则根据反射执行自定义的请求方法,然后返回响应信息如果initial方法抛出异常则执行handle_exception  方法处理抛出的异常,也返回响应信息等到上面的过程执行完成后,再执行finalize_response方法把最终的响应信息返回给客户端的浏览器  def finalize_response(self, request, response, *args, **kwargs):# Make the error obvious if a proper response is not returnedassert 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_rendererresponse.accepted_media_type = request.accepted_media_typeresponse.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 responsedef dispatch(self, request, *args, **kwargs):self.args = argsself.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 = requestself.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 methodif 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.pyfrom django.conf.urls import url, includefrom web.views.s1_api import TestViewurlpatterns = [url(r'^test/', TestView.as_view()),]views.pyfrom rest_framework.views import APIViewfrom rest_framework.response import Responseclass 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/9460317.html