2017-10-06 187 views
2

我的代碼有兩個mixin,BasicAuthMixinJWTAuthMixin如下所述。姑且認爲self.authenticate方法返回並不會引發任何異常:Django調用基於Mixin的另一個類基於Mixin

from django.http import JsonResponse 
from django.utils.decorators import method_decorator 
from django.views.decorators.csrf import csrf_exempt 
from django.views.generic import View 

class BasicAuthMixin(View): 

    """ 
    Add this mixin to the views where Basic Auth is required. 
    """ 
    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     try: 
      self.authenticate(request) 
     except: 
      return JsonResponse({'status': 403, 'message': 'Forbidden'}, status=403, content_type='application/json') 
     return super(BasicAuthMixin, self).dispatch(request, *args, **kwargs) 

class JWTAuthMixin(View): 
    """ 
    Add this mixin to the views where JWT based authentication is required. 
    """ 
    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     try: 
      self.authenticate(request) 
     except: 
      return JsonResponse({'status': 403, 'message': 'Forbidden'}, status=403, content_type='application/json') 
     return super(JWTAuthMixin, self).dispatch(request, *args, **kwargs) 

這些混入在根據需要的認證的意見被使用。

的實際問題,從這裏開始:我試圖創建另一個混入AllAuthMixin,其中包括在任何視圖中會自動決定哪些混入需要被稱爲基於認證報頭時提供:

class AllAuthMixin(View): 

    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     auth = request.META.get('HTTP_AUTHORIZATION') or '' 
     if auth.startswith('Bearer'): 
      return JWTAuthMixin.as_view()(request, *args, **kwargs) 
     elif auth.startswith('Basic'): 
      return BasicAuthMixin.as_view()(request, *args, **kwargs) 
     raise Exception('Unauthorized Access to Saurav APIs', 403) 

一旦我有AllAuthMixin在任何觀點說/測試它實際上調用適當的混入,但返回不允許的方法(GET):/測試

我調試,發現不允許的方法錯誤消息從以下行來,如果我使用基本身份驗證:

return super(BasicAuthMixin, self).dispatch(request, *args, **kwargs) 

以下說明了一個非常簡單的例子來叫我的看法與基本身份驗證:

>>> import requests 
>>> requests.get('http://127.0.0.1:8000/test', auth=('UserName', 'Password')) 
<Response [405]> 

我不知道我在做什麼錯在這裏。任何人都可以請幫我找出問題或任何其他方式來實現這一點。我想要的是重用已經聲明的mixin:BasicAuthMixn和JWTAuthMixin。

回答

2

這裏有一個設計問題,這兩個mixins都是通過攔截dispatch方法並調用super來實現的。你實施AllAuthMixin的方式也叫dispatch意味着你需要讓他們都在其MRO和「詭計」super選擇適當的不是一個好主意。

實施AllAuthMixin的另一種方法是不叫dispatch但實例化,並呼籲他們authenticate

class AllAuthMixin(View): 
    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     auth = request.META.get('HTTP_AUTHORIZATION') or '' 
     try: 
      if auth.startswith('Bearer'): 
       JWTAuthMixin().authenticate(request) # raises on failed auth 
      elif auth.startswith('Basic'): 
       BasicAuthMixin().authenticate(request) 
     except: 
      raise Exception('Unauthorized Access to Saurav APIs', 403) 

     return super(AllAuthMixin, self).dispatch(request, *args, **kwargs) 

重用代碼將驗證單獨部署到自己的類,使個別混入一個更好的方式利用它們。這樣你就可以更好地分離關注點。

喜歡的東西:

class BasicAuth(object): 
    def authenticate(self, request): 
     # raise if not authed 
     print("Basic auth") 

class JWTAuth(object): 
    def authenticate(self, request): 
     # raise if not authed 
     print("JWT auth") 

class AuthMixin(View): 
    def authenticate(self, request): 
     raise NotImplementedError('Implement in subclass') 

    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     try: 
      self.authenticate(request) 
     except: 
      return JsonResponse({'status': 403, 'message': 'Forbidden'}, status=403) 

     return super(AuthMixin, self).dispatch(request, *args, **kwargs) 

class BasicAuthMixin(BasicAuth, AuthMixin): 
    pass 

class JWTAuthMixin(JWTAuth, AuthMixin): 
    pass 

class AllAuthMixin(AuthMixin): 
    def authenticate(self, request): 
     auth = request.META.get('HTTP_AUTHORIZATION') or '' 
     try: 
      if auth.startswith('Bearer'): 
       return JWTAuth().authenticate(request) 
      elif auth.startswith('Basic'): 
       return BasicAuth().authenticate(request) 
     except: 
      return JsonResponse({'status': 403, 'message': 'Other'}, status=403) 

class SomeView(AllAuthMixin, View): 
    def get(self, request): 
     return JsonResponse({'status': 200, 'message': 'OK'}) 

- 原來的答覆 -

你打電話as_viewAllAuthMixin每個混合料攪拌,通過調用as_view()(request, *args, *kwargs)你勉強地混入向響應請求,但由於它沒有get方法,所以返回405方法不允許,如in the docs所述。

你應該打電話dispatch,也使從兩個孩子混入AllAuthMixin繼承正確地傳遞給selfdispatch。像這樣:

class AllAuthMixin(JWTAuthMixin, BasicAuthMixin): 
    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     auth = request.META.get('HTTP_AUTHORIZATION') or '' 
     if auth.startswith('Bearer'): 
      return JWTAuthMixin.dispatch(self, request, *args, **kwargs) 
     elif auth.startswith('Basic'): 
      return BasicAuthMixin.dispatch(self, request, *args, **kwargs) 
     raise Exception('Unauthorized Access to Saurav APIs', 403) 

class SomeView(AllAuthMixin, View): 
    def get(self, request): 
     return JsonResponse({'status': 200, 'message': 'OK'}) 
+0

讓我們[繼續聊天討論](http://chat.stackoverflow.com/rooms/156110/discussion-between-saurav-kumar-and-yeray-diaz-diaz)。 –

+0

**調度**每個mixin的方法正在做很多我在這裏沒有提到的事情;如驗證,引發異常,以適當的響應代碼返回適當的json響應。不調用調度方法,導致我在** AllAuthMixin **中寫入重複代碼。但那很好! **你最近的建議起作用**,至少我不打擾現有的代碼。非常感謝您寶貴的時間。非常感謝 :) –