django之FBV与CBV
一、FBV
FBV(function base views) 就是在视图里使用函数处理请求。
1、在urls.py定义路由
from django.urls import path from app01 import views urlpatterns = [ path('user/', views.user), #定义请求路由 ]
2、在视图中处理请求
from django.shortcuts import render,HttpResponse # Create your views here. def user(request): #处理GET请求 if request.method == 'GET': return HttpResponse('GET') # 处理POST请求 if request.method == 'POST': return HttpResponse('POST')
注意:在处理请求时对不同的请求方法进行判断处理的。
二、CBV
CBV(class base views) 就是在视图里使用类处理请求,与FBV较大的不同点就是能够根据请求的的方法自己自动执行对应的方法。
1、在urls.py定义路由
from django.urls import path from app01 import views urlpatterns = [ path('user/', views.UserView.as_view()), ]
2、在视图中处理请求
from django.shortcuts import render,HttpResponse from django.views import View class UserView(View): def get(self,request,*args, **kwargs): #获取数据 return HttpResponse('GET') def post(self,request,*args, **kwargs): #创建数据 return HttpResponse('POST') def put(self,request,*args, **kwargs): #更新全部数据 return HttpResponse('put') def delete(self,request,*args, **kwargs): #删除数据 return HttpResponse('delete') def patch(self,request,*args, **kwargs): #更新局部数据,例如:用户信息的name这样的某几列 return HttpResponse('patch')
在上述CBV中会根据用户请求方法自动去匹配对应的处理方法。其中CBV中提供的接收请求方式有:
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
3、CBV源码
CBV中是如何根据用户的请求方法执行对应的方法呢?CBV中使用的就是反射的机制。
首先,路由中永远都是一个url对应一个函数的形式,所以,可以看到views.UserView.as_view()执行的就是UserView中的as_view方法,如果自己没有就去父类中寻找。也就是
去View类中寻找。
def as_view(cls, **initkwargs): """Main entry point for a request-response process.""" ... def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) ... return view
可以看到在as_view中返回的是view,所以看看view方法,它返回的就是dispatch方法,这样实际上执行as_view方法得到的实际上就是dispatch方法的返回值。
在dispatch方法中就进行了请求的分发:
def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. 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 return handler(request, *args, **kwargs) #执行方法,拿到返回值,每个方法返回的结果是什么,就会得到什么
总结:
CBV是基于反射实现的,根据不同的请求方式执行不同的方法。
源码流程:路由 -> as_view方法 -> view方法 -> dispatch方法
4、扩展
如果想实现在请求之前做些操作,那么自己可以重写dispatch方法
class UserView(View): def dispatch(self, request, *args, **kwargs): print('处理方法之前...') # 将方法执行的结果返回,两种调用父类的方式 # ret = super(UserView, self).dispatch(request, *args, **kwargs) ret = super().dispatch(request, *args, **kwargs) print(ret) print('处理方法之后...') return ret def get(self,request,*args, **kwargs): #获取数据 return HttpResponse('GET') def post(self,request,*args, **kwargs): #创建数据 return HttpResponse('POST')
执行结果:
处理方法之前... <HttpResponse status_code=200, "text/html; charset=utf-8"> //处理方法返回的结果 处理方法之后...
三、FBV与CBV中使用csrf
django的csrf的实现方式:csrf是基于中间件实现的
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', #进行csrf认证的中间件 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
并且是在中间件的process_view函数中实现的:
- 通过检查视图是否免认证(是否存在@csrf_exempt)
- 从请求体或者cookie中获取csrftoken进行验证,
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): return None # Assume that anything not defined as 'safe' by RFC7231 needs protection if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. # It comes after the creation of CSRF cookies, so that # everything else continues to work exactly the same # (e.g. cookies are sent, etc.), but before any # branches that call reject(). return self._accept(request) if request.is_secure(): # Suppose user visits http://example.com/ # An active network attacker (man-in-the-middle, MITM) sends a # POST form that targets https://example.com/detonate-bomb/ and # submits it via JavaScript. # # The attacker will need to provide a CSRF cookie and token, but # that's no problem for a MITM and the session-independent # secret we're using. So the MITM can circumvent the CSRF # protection. This is true for any HTTP connection, but anyone # using HTTPS expects better! For this reason, for # https://example.com/ we need additional protection that treats # http://example.com/ as completely untrusted. Under HTTPS, # Barth et al. found that the Referer header is missing for # same-domain requests in only about 0.2% of cases or less, so # we can use strict Referer checking. referer = request.META.get('HTTP_REFERER') if referer is None: return self._reject(request, REASON_NO_REFERER) referer = urlparse(referer) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. if referer.scheme != 'https': return self._reject(request, REASON_INSECURE_REFERER) # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match # match on host:port. If not, obey the cookie rules (or those # for the session cookie, if CSRF_USE_SESSIONS). good_referer = ( settings.SESSION_COOKIE_DOMAIN if settings.CSRF_USE_SESSIONS else settings.CSRF_COOKIE_DOMAIN ) if good_referer is not None: server_port = request.get_port() if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) else: # request.get_host() includes the port. good_referer = request.get_host() # Here we generate a list of all acceptable HTTP referers, # including the current host since that has been validated # upstream. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) good_hosts.append(good_referer) if not any(is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, reason) csrf_token = request.META.get('CSRF_COOKIE') if csrf_token is None: # No CSRF cookie. For POST requests, we insist on a CSRF cookie, # and in this way we can avoid all CSRF attacks, including login # CSRF. return self._reject(request, REASON_NO_CSRF_COOKIE) # Check non-cookie token for match. request_csrf_token = "" if request.method == "POST": try: request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') except IOError: # Handle a broken connection before we've completed reading # the POST data. process_view shouldn't raise any # exceptions, so we'll ignore and serve the user a 403 # (assuming they're still listening, which they probably # aren't because of the error). pass if request_csrf_token == "": # Fall back to X-CSRFToken, to make things easier for AJAX, # and possible for PUT/DELETE. request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') request_csrf_token = _sanitize_token(request_csrf_token) if not _compare_salted_tokens(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) return self._accept(request)
那么在FBV和CBV中如何进行使用呢?
1、FBV
(1)全站使用csrf认证,个别函数免认证
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', #全站使用csrf认证 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
from django.views.decorators.csrf import csrf_exempt # Create your views here. #该函数无需认证 @csrf_exempt def user(request): #处理GET请求 if request.method == 'GET': return HttpResponse('GET') # 处理POST请求 if request.method == 'POST': return HttpResponse('POST')
(2)全站不使用csrf认证,个别函数认证
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', #全站不进行csrf认证 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] from django.views.decorators.csrf import csrf_exempt,csrf_protect #该函数认证 @csrf_protect def user(request): #处理GET请求 if request.method == 'GET': return HttpResponse('GET') # 处理POST请求 if request.method == 'POST': return HttpResponse('POST')
2、CBV
与FBV有所不同,需要对dispatch函数加上装饰器,当然也是可以进行全站或者局部认证的。
from django.utils.decorators import method_decorator @method_decorator(csrf_exempt,name='dispatch') #加上csrf免认证装饰器 class UserView(View): def get(self,request,*args, **kwargs): #获取数据 return HttpResponse('GET') def post(self,request,*args, **kwargs): #创建数据 return HttpResponse('POST') def put(self,request,*args, **kwargs): #更新全部数据 return HttpResponse('put') def delete(self,request,*args, **kwargs): #删除数据 return HttpResponse('delete') def patch(self,request,*args, **kwargs): #更新局部数据,例如:用户信息的name这样的某几列 return HttpResponse('patch')
当然另一种方式是必须写dispatch方法,然后加上装饰器
from django.utils.decorators import method_decorator class UserView(View): @method_decorator(csrf_exempt)#加上csrf免认证装饰器 def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get(self,request,*args, **kwargs): #获取数据 return HttpResponse('GET') def post(self,request,*args, **kwargs): #创建数据 return HttpResponse('POST')