Fork me on GitHub

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)
process_view

那么在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')

 

posted @ 2019-08-19 19:29  iveBoy  阅读(412)  评论(0编辑  收藏  举报
TOP