rest_framework學習(一)resful規範介紹及Django的CBV介紹

一、什麼是resful規範

resful是一種規範,它是一種面向資源編程的規範,操作只是一種請求方式。

二、規範內容

1.API與用戶的通信協議,總是使用HTTPs協議:https比http安全

2.域名

https://api.example.com 儘量將API部署在專用域名(會存在跨域問題)
https://example.org/api/ API很簡單
例如寫一個查詢所有圖書的api接口:
https://api.example.com/books
https://127.0.0.1/api/books

3.版本:每個接口都應該有版本

URL,如:https://api.example.com/v1/
https://127.0.0.1/api/v2/books(推薦用這種)
請求頭 跨域時,引發發送多次請求

4.路徑,視網絡上任何東西都是資源,均使用名詞表示(可複數)

https://api.example.com/v1/books
https://api.example.com/v1/animals
https://api.example.com/v1/employees
不能這麼寫:
-獲取所有圖書:https://127.0.0.1/api/get_all_books
-新增一本書:https://127.0.0.1/api/add_book
同一都用這個:獲取通過get請求,新增通過post請求
https://api.example.com/v1/books

5.method

GET :從服務器取出資源(一項或多項)
POST :在服務器新建一個資源
PUT :在服務器更新資源(客戶端提供改變後的完整資源)
PATCH :在服務器更新資源(客戶端提供改變的屬性)
DELETE:從服務器刪除資源

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

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

7.狀態碼

請求回去,需要有狀態碼,也可以自定義狀態碼
OK - [GET]:服務器成功返回用戶請求的數據,該操作是冪等的(Idempotent)。
CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
Accepted - [*]:表示一個請求已經進入後臺排隊(異步任務)
NO CONTENT - [DELETE]:用戶刪除數據成功。
INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。
Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。
NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。
Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是隻有XML格式)。
Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。
Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。
INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷髮出的請求是否成功。

更多看這裏:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

8.錯誤處理

應返回錯誤信息,error當做key.
{status:100,error:'錯誤信息寫上'}

9.返回結果

針對不同操作,服務器向用戶返回的結果應該符合以下規範:
GET /books:返回資源對象的列表(數組)
GET /books/1:返回單個資源對象
POST /books:返回新生成的資源對象 -新增,傳數據,一旦新增完成,把新的資源對象返回
PUT /books/1:返回完整的資源對象
PATCH /books/1:返回完整的資源對象
DELETE /books/1:返回一個空文檔

10.Hypermedia API

RESTful API最好做到Hypermedia,即返回結果中提供鏈接,連向其他API
{
        status:100
        msg:成功
        url:127.0.0.1/books/1
}
核心:返回結果中提供鏈接

三、django rest framework

1、安裝

pip install djangorestframework

2、使用

restframework是一個app,需要在settings.py引入該app

INSTALLED_APPS = [
    '''
    'rest_framework',
]

3、restframework和原始django對比

在之前學習的django裏對book表的增刪改查可能會這樣寫

----book表增刪改查
    /books/                 獲取圖書列表
    /books/add/             添加圖書信息
    /books/(\d+)/change/    修改圖書信息
    /books/(\d+)/delete/    刪除圖書信息

按照resful規範,應該把圖書信息看作是一種資源,url裏應當全部是名詞,操作類型應該根據請求方式定義。

----book表增刪改查
    /books/     -----get            獲取圖書列表      -----  返回當前所有數據
    /books/     -----post           添加圖書信息      -----  返回提交數據 
		 
    /books/(\d+)-----get            獲取單個圖書信息  -----  返回當前查看的單條數據 
    /books/(\d+)-----put            修改單個圖書信息  -----  返回更新數據 
    /books/(\d+)-----delete         刪除單個圖書信息  -----  返回空

爲了實現resful規範,就只能使用CBV模式實現視圖函數。

CBV(Class Base View)

django自帶的CBV視圖

urls.py

urlpatterns = [
    '''
    path('url1/', Url1View.as_view(), name="url1"),
]

 views.py

from django.views import View
class Url1View(View):
    def get(self, request):
         pass

    def post(self, request):
        pass

 django自帶的CBV模式請求流程

首先用戶的請求先到urls.py匹配視圖函數,例如用戶請求的是/url1/的話,會先執行Url1View.as_view()方法。Url1View類並沒有.as_view()方法,在它的父類View中有.as_view()方法。View.as_view()源碼是這樣寫的

@classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)
        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函數對象 。在我們使用FBV模式的時候,url.py文件中是這樣配置url的

from app import views
urlpatterns = [
    '''
    path('url1/', views.Url1), name="url1"),
]

 url後指向的是一個函數對象,使用django自帶的CBV模式在這裏是一樣的,url指向的是一個函數對象,這個函數對象是View.as_view()中定義的.view()方法。所以接收到用戶請求,就會執行.view()方法。可以看到.view()方法返回的是self.dispatch(request, *args, **kwargs)。Url1View類中沒有.dispatch()這個方法,最後在View類中找到了這個方法,先看這個方法的源碼

    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:
        # http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

 可以看到.dispatch()是根據request請求的方式,找到與請求方式同名的views函數並返回執行結果,如果找不到則報405的錯誤提示。

總體過程如圖

以上就是django自帶CBV模式執行流程。

 restframework的CBV模式的執行過程

restframework的的視圖函數要繼承rest_framework.views.APIView類,與自帶的CBV中不同的是執行的.as_view()方法是APIView類中的,執行的.dispatch()方法是APIView類中的。APIView類重寫了這個流程中的.as_view()、.dispatch()方法。

下面看APIView.as_view()源碼。

@classmethod
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation

        view = super(APIView, cls).as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)

可以看到返回的依然是View類中的.as_view()執行的結果,即View.as_view()中定義的view()函數對象。前面已經說過了.view()函數,它的返回值就是.dispatch()的返回值。

再來看APIView.dispatch()的源碼

    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 = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        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

其他的都和View.dispatch()差不多,下面看這兩行

request = self.initialize_request(request, *args, **kwargs)
self.request = request

 先看一下initialize_request()的源碼

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        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封裝到成了新的request對象。並重新賦給self.request,傳到執行的視圖函數中。

所以在視圖函數中從request中取參數的方式就變了

request.data 是個方法,包裝成了屬性,前臺傳過來body體中數據的數據,放在裏面
request.query_params 這個是原來GET中的數據
request._request 是原來的request對象

也可以用request.POST、request.GET取數據

總結起來就是APIView類重寫了View類的.as_view()和.dispatch()方法,並重新定義了request對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章