1. 程式人生 > >django rest_framework 框架的使用

django rest_framework 框架的使用

狀態 start hcl 接口 簡單 .html should state any

django 的中間件 csrf

Require a present and correct csrfmiddlewaretoken for POST requests that have a CSRF cookie, and set an outgoing CSRF cookie.

This middleware should be used in conjunction with the {% csrf_token %}
template tag.

  • django 的中間件是基於 post 的請求,當post 請求到來的時候 在csrftoken 中間件中執行 process_view 驗證 token的正確性,如果驗證失敗,則返回forbidden 提示給用戶。

  • 我們可以讓中間件不起作用,首先第一種方法是在配置文件中的settings中註釋掉cscftoken的中間件,這樣其實存在安全隱患
  • 其次我們可以在我們不需要驗證csrftoken的視圖view中加裝飾器來使我們的視圖函數免予csrftoken的校驗,使用如下:

from django.shortcuts import render, HttpResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt, csrf_protect  # csrf_token 單獨示例
from django.utils.decorators import method_decorator

# 基於函數的使用
@csrf_exempt
def index(request):

    # what you want you view do in this
    return HttpResponse("index")

# 基於類的方式 2-1
@method_decorator(csrf_exempt, name="dispatch")
class StudentView(View):
    #方式 2-1
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        # do something  before diaspatch
        ret =  super(StudentView, self).dispatch(request, *args, **kwargs)
        #do somethig after dispatch
        return ret
    def get(self, request, *args, **kwargs):
        return HttpResponse("GET")

    def post(self, request, *args, **kwargs):
        return HttpResponse("POST")

django 的CBV模式

django 中cbv模式中,已類來接受一個請求的到來,通過類繼承 view 類
通過父類的dispatch 方法通過反射 進行請求方法的分發。我們也可以在繼承父類的dispatch 方法 來自定制自己的一些操作,rest-framework 也是基於這個實現的,因此我們先看看下他的源碼部分:

首先是url中的配置:

urlpatterns = [
    re_path('^student/$', views.StudentView.as_view()),
    re_path('^dog/$', views.DogView.as_view()),
]

當請求到來的時候,執行as_view() 方法讓我們來看下 as_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) # 看這裏執行了View的dispatch 方法
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

再來看看 view類的dispatch 方法:註意可定制的操作前面已經給出

    def dispatch(self, request, *args, **kwargs):
      # 這裏的self. http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
        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)

rest_framework 的解讀

首先 rest_framework 是為了遵循RESTful 規範而誕生的框架;
那我們首先需要知道什麽是RESTful 規範

  • REST與技術無關,代表的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯為“表征狀態轉移”

  • REST從資源的角度類審視整個網絡,它將分布在網絡中某個節點的資源通過URL進行標識,客戶端應用通過URL來獲取資源的表征,獲得這些表征致使這些應用轉變狀態

  • REST與技術無關,代表的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯為“表征狀態轉移”

  • 所有的數據,不過是通過網絡獲取的還是操作(增刪改查)的數據,都是資源,將一切數據視為資源是REST區別與其他架構風格的最本質屬性

  • 對於REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture)

接下來讓我們了解下他的API設計規範

  • 1、在url接口中推薦使用Https協議,讓網絡接口更加安全(Https是Http的安全版,即HTTP下加入 SSL層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL(安全套接層協議))

  • 2、url中可以體現這是個API接口 域名規範

    https://api.example.com 盡量將API部署在專用域名(會存在跨域問題)
    https://example.org/api/ API很簡單

  • 3、url中還可以體現版本號,不同的版本可以有不同的接口,使其更加簡潔,清晰

    URL,如:https://api.example.com/v1/

請求頭跨域時, 引發發送多次請求

  • 4、restful 提倡面向資源編程,所以在url接口中盡量要使用名詞,不要使用動詞

    https://api.example.com/v1/zoos

https://api.example.com/v1/animals

https://api.example.com/v1/employees

  • 5、此外url中還可以添加條件去篩選匹配

    過濾,通過在url上傳參的形式傳遞搜索條件

https://api.example.com/v1/zoos?limit=10:指定返回記錄的數量

https://api.example.com/v1/zoos?offset=10:指定返回記錄的開始位置

https://api.example.com/v1/zoos?page=2&per_page=100:指定第幾頁,以及每頁的記錄數

https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序

https://api.example.com/v1/zoos?animal_type_id=1:指定篩選條件

  • 6、可以根據Http不同的method,進行不同的資源操作(5種方法:GET / POST / PUT / DELETE
    / PATCH)

    GET :從服務器取出資源(一項或多項)

POST :在服務器新建一個資源

PUT :在服務器更新資源(客戶端提供改變後的完整資源)

PATCH :在服務器更新資源(客戶端提供改變的屬性)

DELETE :從服務器刪除資源

  • 7、響應式應該包含狀態碼
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
  • 8、應該有返回值,而且格式為統一的json格式
  • 9、應該返回錯誤信息
  • 10、返回結果中要提供幫助鏈接,即API最好做到Hypermedia

django的 rest_framework 組件的使用

1.首先我們的視圖需要時候基於cbv的模式,我們的類需要繼承rest_framework的view的 APIView 父類
如下:

class DogView(APIView):
    def get(self, request, *args, **kwargs):
        return HttpResponse("GET")

    def post(self, request, *args, **kwargs):
        return HttpResponse("POST")

接下來讓我們研究下APIView的dispath 方法:

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        # 對原生的request 加工(豐富)
        """"
        Request(
        request,parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context)
        原生的request = request._request
        """
        # 首先是對原生的request進行了加個處理
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

我們來看下 self.initialize_request 這個方法實現方法

  def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)
        # self.get_authenticators() seteings配置的一個列表
        # authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
        # 對原生的request第二次加工
        return Request(
            request,
            # 封裝了解析器類
            parsers=self.get_parsers(),
            # 封裝了用戶用戶認證的類
            authenticators=self.get_authenticators(),
            # url後綴名的顯示方式 ex--> .json
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
1.首先是用戶認證 的解析

在討論源碼之前我先rest_framework配置放在這裏(後面需要修改的會加上去):

# 可以看這裏

from rest_framework import settings 

# List of settings that may be in string import notation.
IMPORT_STRINGS = (
    'DEFAULT_RENDERER_CLASSES',
    'DEFAULT_PARSER_CLASSES',
    'DEFAULT_AUTHENTICATION_CLASSES',
    'DEFAULT_PERMISSION_CLASSES',
    'DEFAULT_THROTTLE_CLASSES',
    'DEFAULT_CONTENT_NEGOTIATION_CLASS',
    'DEFAULT_METADATA_CLASS',
    'DEFAULT_VERSIONING_CLASS',
    'DEFAULT_PAGINATION_CLASS',
    'DEFAULT_FILTER_BACKENDS',
    'DEFAULT_SCHEMA_CLASS',
    'EXCEPTION_HANDLER',
    'TEST_REQUEST_RENDERER_CLASSES',
    'UNAUTHENTICATED_USER',
    'UNAUTHENTICATED_TOKEN',
    'VIEW_NAME_FUNCTION',
    'VIEW_DESCRIPTION_FUNCTION'
)

封住完request之後接下來我們看看後面的源碼

  # 進行一系列的認證
  #初始化進入驗證,下面來看看initial 這個函數做了啥
    self.initial(request, *args, **kwargs)
#--------------------------------------------------
  def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)
        .......
        
        ##  版本的控制的
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # 認證
        self.perform_authentication(request)
        
        # 權限驗證
        self.check_permissions(request)
        
        # 訪問頻率
        self.check_throttles(request)

接下來討論 認證方法

self.perform_authentication(request)源碼:

View 類
 def perform_authentication(self, request):
        """
        Perform authentication on the incoming request.

        Note that if you override this and simply 'pass', then authentication
        will instead be performed lazily, the first time either
        `request.user` or `request.auth` is accessed.
        """
        request.user

Request 類

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        # 遍歷 setting defaults 的列表 的
        """
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
        
        """
        # 執行每個self.authenticators類中的認證類方法:
        for authenticator in self.authenticators:
            try:
                # 返回值有三種
                """
                1.返回user 與 token 對象(認證成功) 
                2.返回None(不做處理,交個下一個)
                3.拋出認證異常(認證失敗)
                """
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise
        
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()

認證類的寫法:

# Lower 寫法
class MyBasicAuthentication(object):

    def authenticate(self, request):
        token = request._request.GET.get("token")
        if token:
            return ("alex",None)

        raise exceptions.AuthenticationFailed("failed")

    def authenticate_header(self, val):
        pass
"""我們可以繼承他的類"""
# 基於用戶token的認證
from rest_framework.authentication import BasicAuthentication 
默認的幾種用戶認證類 自己寫的可以繼承BasicAuthentication
1.一中是自己定義類然後在view視圖中這要表達:
authentication_classes=[ yourauthclass, ]
2.或者全局配置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":[.......]
}

接下來是用戶權限的控制 這個比較簡單直接上rest的自帶的幾個權限驗證的源碼類自己研究:

from rest_framework.permissions import BasePermission


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

django rest_framework 框架的使用