1.3 django restframework(上)

django restframework(簡(jiǎn)稱(chēng)drf)本質(zhì)上其實(shí)就是一個(gè)別人編寫(xiě)好的app,里面集成了很多編寫(xiě)restful API的功能功能,接下里咱們就來(lái)學(xué)習(xí)drf并用他來(lái)開(kāi)發(fā)restful API。

drf內(nèi)置了很多便捷的功能,在接下來(lái)的課程中會(huì)給大家依次講解下面的內(nèi)容:

  • 快速上手

  • 請(qǐng)求的封裝

  • 版本管理

  • 認(rèn)證

  • 權(quán)限

  • 限流

  • 序列化

  • 視圖

  • 條件搜索

  • 分頁(yè)

  • 路由

  • 解析器

 

1. 快速上手

  • 安裝

    pip install djangorestframework==3.12.4
    版本要求:djangorestframework==3.12.4
    Python (3.5, 3.6, 3.7, 3.8, 3.9)
    Django (2.2, 3.0, 3.1)
       
    版本要求:djangorestframework==3.11.2
    Python (3.5, 3.6, 3.7, 3.8)
    Django (1.11, 2.0, 2.1, 2.2, 3.0)
  • 配置,在settings.py中添加配置

    INSTALLED_APPS = [
      ...
       # 注冊(cè)rest_framework(drf)
       'rest_framework',
    ]
    ?
    # drf相關(guān)配置以后編寫(xiě)在這里
    REST_FRAMEWORK = {
     
    }
  • URL和視圖

    # urls.py
    ?
    from django.urls import path
    from app01 import views
    ?
    urlpatterns = [
       path('users/', views.UserView.as_view()),
    ]
    # views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    ?
    ?
    class UserView(APIView):
       def get(self, request, *args, **kwargs):
           return Response({"code": 1000, "data": "xxx"})
    ?
       def post(self, request, *args, **kwargs):
           return Response({"code": 1000, "data": "xxx"})

 

其實(shí)drf框架是在django基礎(chǔ)進(jìn)行的擴(kuò)展,所以上述執(zhí)行過(guò)得底層實(shí)現(xiàn)流程(同django的CBV):

 

 

drf中重寫(xiě)了 as_viewdispatch方法,其實(shí)就是在原來(lái)django的功能基礎(chǔ)上添加了一些功能,例如:

  • as_view,免除了csrf 驗(yàn)證,一般前后端分離不會(huì)使用csrf token認(rèn)證(后期會(huì)使用jwt認(rèn)證)。

  • dispatch,內(nèi)部添加了 版本處理、認(rèn)證、權(quán)限、訪問(wèn)頻率限制等諸多功能(后期逐一講解)。

 

2. 請(qǐng)求數(shù)據(jù)的封裝

以前我們通過(guò)django開(kāi)發(fā)項(xiàng)目時(shí),視圖中的request是 django.core.handlers.wsgi.WSGIRequest 類(lèi)的對(duì)象,其中包含了請(qǐng)求相關(guān)的所有數(shù)據(jù)。

# Django FBV
def index(request):
request.method
request.POST
request.GET
request.body
?
# Django CBV
from django.views import View
class UserView(View):
def get(self,request):
       request.method
       request.POST
       request.GET
       request.body

 

而在使用drf框架時(shí),視圖中的request是rest_framework.request.Request類(lèi)的對(duì)象,其是又對(duì)django的request進(jìn)行了一次封裝,包含了除django原request對(duì)象以外,還包含其他后期會(huì)使用的其他對(duì)象。

from rest_framework.views import APIView
from rest_framework.response import Response
?
?
class UserView(APIView):
   def get(self, request, *args, **kwargs):
       # request,不再是django中的request,而是又被封裝了一層,內(nèi)部包含:django的request、認(rèn)證、解析器等。
       return Response({"code": 1000, "data": "xxx"})
?
   def post(self, request, *args, **kwargs):
       return Response({"code": 1000, "data": "xxx"})
?
對(duì)象 = (request, 其他數(shù)據(jù))
# rest_framework.request.Request 類(lèi)

class Request:
    """
    Wrapper allowing to enhance a standard `HttpRequest` instance.
    Kwargs:
        - request(HttpRequest). The original request instance. (django中的request)
        - parsers(list/tuple). The parsers to use for parsing the
          request content.
        - authenticators(list/tuple). The authenticators used to try
          authenticating the request's user.
    """

    def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
    	self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        ...
	
    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data
    
	def __getattr__(self, attr):
        try:
            return getattr(self._request, attr) # self._request.method
        except AttributeError:
            return self.__getattribute__(attr)

所以,在使用drf框架開(kāi)發(fā)時(shí),視圖中的request對(duì)象與原來(lái)的有些不同,例如:

from rest_framework.views import APIView
from rest_framework.response import Response
from django.views import View
from rest_framework.request import Request


class UserView(APIView):
    def get(self, request, *args, **kwargs):
        
        # 通過(guò)對(duì)象的嵌套直接找到原request,讀取相關(guān)值
        request._request.method
        request._request.GET
        request._request.POST
        request._request.body
        
        # 舉例:
        	content-type: url-form-encoded
        	v1=123&v2=456&v3=999
            django一旦讀取到這個(gè)請(qǐng)求頭之后,就會(huì)按照 {"v1":123,"v2":456,"v3":999}
            
            content-type: application/json
            {"v1":123,"v2":456}
            request._request.POST
            request._request.body
        
        # 直接讀取新request對(duì)象中的值,一般此處會(huì)對(duì)原始的數(shù)據(jù)進(jìn)行一些處理,方便開(kāi)發(fā)者在視圖中使用。
        request.query_params  # 內(nèi)部本質(zhì)上就是 request._request.GET
        request.data # 內(nèi)部讀取請(qǐng)求體中的數(shù)據(jù),并進(jìn)行處理,例如:請(qǐng)求者發(fā)來(lái)JSON格式,他的內(nèi)部會(huì)對(duì)json字符串進(jìn)行反序列化。
        
        # 通過(guò) __getattr__ 去訪問(wèn) request._request 中的值
        request.method
        
        

 

底層源碼實(shí)現(xiàn):

 

 

3. 版本管理

在restful規(guī)范中要去,后端的API中需要體現(xiàn)版本。

drf框架中支持5種版本的設(shè)置。

3.1 URL的GET參數(shù)傳遞(*)

 

# settings.py

REST_FRAMEWORK = {
    "VERSION_PARAM": "v",
    "DEFAULT_VERSION": "v1",
    "ALLOWED_VERSIONS": ["v1", "v2", "v3"],
    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning"
}

源碼執(zhí)行流程:

 

 

3.2 URL路徑傳遞(*)

 

 

 

 

 

 

 

 

 

 

3.3 請(qǐng)求頭傳遞

 

 

 

 

 

3.4 二級(jí)域名傳遞

 

在使用二級(jí)域名這種模式時(shí)需要先做兩個(gè)配置:

  • 域名需解析至IP,本地可以在hosts文件中添加

    127.0.0.1       v1.wupeiqi.com
    127.0.0.1       v2.wupeiqi.com
  • 在django的settings.py配置文件中添加允許域名訪問(wèn)

    ALLOWED_HOSTS = ["*"]

 

3.5 路由的namespace傳遞

 

 

以上就是drf中支持的5種版本管理的類(lèi)的使用和配置。

 

 

全局配置

上述示例中,如果想要應(yīng)用某種 版本 的形式,需要在每個(gè)視圖類(lèi)中定義類(lèi)變量:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning


class UserView(APIView):
    versioning_class = QueryParameterVersioning
    ...

如果你項(xiàng)目比較大,需要些很多的視圖類(lèi),在每一個(gè)類(lèi)中都寫(xiě)一遍會(huì)比較麻煩,所有drf中也支持了全局配置。

# settings.py

REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",  # 處理版本的類(lèi)的路徑
    "VERSION_PARAM": "version",  # URL參數(shù)傳參時(shí)的key,例如:xxxx?version=v1
    "ALLOWED_VERSIONS": ["v1", "v2", "v3"],  # 限制支持的版本,None表示無(wú)限制
    "DEFAULT_VERSION": "v1",  # 默認(rèn)版本
}

 

訪問(wèn)URL:

http://127.0.0.1:8000/api/users/?version=v1
http://127.0.0.1:8000/api/users/?version=v2
http://127.0.0.1:8000/api/users/?version=v3

http://127.0.0.1:8000/api/admin/?version=v1
http://127.0.0.1:8000/api/admin/?version=v2
http://127.0.0.1:8000/api/admin/?version=v3

http://127.0.0.1:8000/api/v1/order/
http://127.0.0.1:8000/api/v2/order/
http://127.0.0.1:8000/api/v3/order/

 

底層源碼實(shí)現(xiàn)

 

 

反向生成URL

在每個(gè)版本處理的類(lèi)中還定義了reverse方法,他是用來(lái)反向生成URL并攜帶相關(guān)的的版本信息用的,例如:

 

 

 

小結(jié)

以后使用drf開(kāi)發(fā)后端API接口時(shí):

  1. 創(chuàng)建django程序

  2. 安裝drf框架

  3. 創(chuàng)建一個(gè)app專(zhuān)門(mén)來(lái)處理用戶的請(qǐng)求

  4. 注冊(cè)APP

  5. 設(shè)置版本

  6. 編寫(xiě)視圖類(lèi)

 

4. 認(rèn)證

 

在開(kāi)發(fā)后端的API時(shí),不同的功能會(huì)有不同的限制,例如:

  • 無(wú)需認(rèn)證,就可以訪問(wèn)并獲取數(shù)據(jù)。

  • 需認(rèn)證,用戶需先登錄,后續(xù)發(fā)送請(qǐng)求需攜帶登錄時(shí)發(fā)放的憑證(后期會(huì)講jwt)

 

在drf中也給我們提供了 認(rèn)證組件 ,幫助我們快速實(shí)現(xiàn)認(rèn)證相關(guān)的功能,例如:

# models.py

from django.db import models

class UserInfo(models.Model):
    username = models.CharField(verbose_name="用戶名", max_length=32)
    password = models.CharField(verbose_name="密碼", max_length=64)
    token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)

 

 

 

 

在視圖類(lèi)中設(shè)置類(lèi)變量 authentication_classes的值為 認(rèn)證類(lèi) MyAuthentication,表示此視圖在執(zhí)行內(nèi)部功能之前需要先經(jīng)過(guò) 認(rèn)證。

認(rèn)證類(lèi)的內(nèi)部就是去執(zhí)行:authenticate方法,根據(jù)返回值來(lái)表示認(rèn)證結(jié)果。

  • 拋出異常AuthenticationFailed,表示認(rèn)證失敗。內(nèi)部還會(huì)執(zhí)行 authenticate_header將返回值設(shè)置給響應(yīng)頭 WWW-Authenticate

  • 返回含有兩個(gè)元素的元組,表示認(rèn)證成功,并且會(huì)將元素的第1個(gè)元素賦值給 request.user、第2個(gè)值賦值給request.auth 。

    第1個(gè)值,一般是用戶對(duì)象。
    第2個(gè)值,一般是token
  • 返回None,表示繼續(xù)調(diào)用 后續(xù)的認(rèn)證類(lèi) 進(jìn)行認(rèn)證(上述案例未涉及)

 

關(guān)于 ”返回None“

接下來(lái)說(shuō)說(shuō) “返回None” 是咋回事。

在視圖類(lèi)的 authentication_classes 中定義認(rèn)證類(lèi)時(shí),傳入的是一個(gè)列表,支持定義多個(gè)認(rèn)證類(lèi)。

當(dāng)出現(xiàn)多個(gè)認(rèn)證類(lèi)時(shí),drf內(nèi)部會(huì)按照列表的順序,逐一執(zhí)行認(rèn)證類(lèi)的 authenticate 方法,如果 返回元組 或 拋出異常 則會(huì)終止后續(xù)認(rèn)證類(lèi)的執(zhí)行;如果返回None,則意味著繼續(xù)執(zhí)行后續(xù)的認(rèn)證類(lèi)。

如果所有的認(rèn)證類(lèi)authenticate都返回了None,則默認(rèn) request.user="AnonymousUser" 和 request.auth=None,也可以通過(guò)修改配置文件來(lái)修改默認(rèn)值。

REST_FRAMEWORK = {
 "UNAUTHENTICATED_USER": lambda: None,
 "UNAUTHENTICATED_TOKEN": lambda: None,
}

 

”返回None“的應(yīng)用場(chǎng)景:

當(dāng)某個(gè)API,已認(rèn)證 和 未認(rèn)證 的用戶都可以方法時(shí),比如:

  • 已認(rèn)證用戶,訪問(wèn)API返回該用戶的視頻播放記錄列表。

  • 未認(rèn)證用戶,訪問(wèn)API返回最新的的視頻列表。

注意:不同于之前的案例,之前案例是:必須認(rèn)證成功后才能訪問(wèn),而此案例則是已認(rèn)證和未認(rèn)證均可訪問(wèn)。

 

 

 

 

關(guān)于多個(gè)認(rèn)證類(lèi)

一般情況下,編寫(xiě)一個(gè)認(rèn)證類(lèi)足矣。

當(dāng)項(xiàng)目中可能存在多種認(rèn)證方式時(shí),就可以寫(xiě)多個(gè)認(rèn)證類(lèi)。例如,項(xiàng)目認(rèn)證支持:

  • 在請(qǐng)求中傳遞token進(jìn)行驗(yàn)證。

  • 請(qǐng)求攜帶cookie進(jìn)行驗(yàn)證。

  • 請(qǐng)求攜帶jwt進(jìn)行驗(yàn)證(后期講)。

  • 請(qǐng)求攜帶的加密的數(shù)據(jù),需用特定算法解密(一般為app開(kāi)發(fā)的接口都是有加密算法)

  • ...

此時(shí),就可以編寫(xiě)多個(gè)認(rèn)證類(lèi),并按照需要應(yīng)用在相應(yīng)的視圖中,例如:

 

 

注意:此示例后續(xù)在視圖中讀取的 request.user 的值為None時(shí),表示未認(rèn)證成功;不為None時(shí),則表示認(rèn)證成功。

 

全局配置

在每個(gè)視圖類(lèi)的類(lèi)變量 authentication_classes 中可以定義,其實(shí)在配置文件中也可以進(jìn)行全局配置,例如:

REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": lambda: None,
    "UNAUTHENTICATED_TOKEN": lambda: None,
    "DEFAULT_AUTHENTICATION_CLASSES":["xxxx.xxxx.xx.類(lèi)名","xxxx.xxxx.xx.類(lèi)名",]
}

 

底層源碼實(shí)現(xiàn):

 

 

5. 權(quán)限

認(rèn)證,根據(jù)用戶攜帶的 token/其他 獲取當(dāng)前用戶信息。

權(quán)限,讀取認(rèn)證中獲取的用戶信息,判斷當(dāng)前用戶是否有權(quán)限訪問(wèn),例如:普通用戶、管理員、超級(jí)用戶,不同用戶具有不同的權(quán)限。

class UserInfo(models.Model):
    
    role_choices = ((1, "普通用戶"), (2, "管理員"), (3, "超級(jí)管理員"),)
    role = models.IntegerField(verbose_name="角色", choices=role_choices, default=1)
    
    username = models.CharField(verbose_name="用戶名", max_length=32)
    password = models.CharField(verbose_name="密碼", max_length=64)
    token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)

 

import uuid
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import AuthenticationFailed

from app01 import models


class AuthView(APIView):
    """ 用戶登錄認(rèn)證 """
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        print(request.data)  # {"username": "wupeiqi", "password": "123"}
        username = request.data.get('username')
        password = request.data.get('password')

        user_object = models.UserInfo.objects.filter(username=username, password=password).first()
        if not user_object:
            return Response({"code": 1000, "data": "用戶名或密碼錯(cuò)誤"})

        token = str(uuid.uuid4())

        user_object.token = token
        user_object.save()

        return Response({"code": 0, "data": {"token": token, "name": username}})


class TokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get("token")
        if not token:
            raise AuthenticationFailed({"code": 1002, "data": "認(rèn)證失敗"})
        user_object = models.UserInfo.objects.filter(token=token).first()
        if not user_object:
            raise AuthenticationFailed({"code": 1002, "data": "認(rèn)證失敗"})
        return user_object, token

    def authenticate_header(self, request):
        return 'Bearer realm="API"'


class PermissionA(BasePermission):
    message = {"code": 1003, 'data': "無(wú)權(quán)訪問(wèn)"}

    def has_permission(self, request, view):
        if request.user.role == 2:
            return True
        return False
	
    # 暫時(shí)先這么寫(xiě)
    def has_object_permission(self, request, view, obj):
        return True


class OrderView(APIView):
    authentication_classes = [TokenAuthentication, ]

    permission_classes = [PermissionA,]

    def get(self, request, *args, **kwargs):
        print(request.user)
        return Response({"code": 0, "data": {"user": None, 'list': [1, 2, 3]}})


class PayView(APIView):
    authentication_classes = [TokenAuthentication, ]
    permission_classes = [PermissionA, ]

    def get(self, request, *args, **kwargs):
        print(request.user)
        return Response({"code": 0, "data": "數(shù)據(jù)..."})

 

 

 

關(guān)于多個(gè)權(quán)限類(lèi)

當(dāng)開(kāi)發(fā)過(guò)程中需要用戶同時(shí)具備多個(gè)權(quán)限(缺一不可)時(shí),可以用多個(gè)權(quán)限類(lèi)來(lái)實(shí)現(xiàn)。

權(quán)限組件內(nèi)部處理機(jī)制:按照列表的順序逐一執(zhí)行 has_permission 方法,如果返回True,則繼續(xù)執(zhí)行后續(xù)的權(quán)限類(lèi);如果返回None或False,則拋出權(quán)限異常并停止后續(xù)權(quán)限類(lèi)的執(zhí)行。

# models.py

from django.db import models


class Role(models.Model):
    """ 角色表 """
    title = models.CharField(verbose_name="名稱(chēng)", max_length=32)


class UserInfo(models.Model):
    """ 用戶表 """
    username = models.CharField(verbose_name="用戶名", max_length=32)
    password = models.CharField(verbose_name="密碼", max_length=64)
    token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)

    roles = models.ManyToManyField(verbose_name="角色", to="Role")

 

# urls.py

from django.urls import path, re_path, include
from app01 import views

urlpatterns = [
    path('api/auth/', views.AuthView.as_view()),
    path('api/order/', views.OrderView.as_view()),
]

 

# views.py

import uuid
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import AuthenticationFailed

from app01 import models


class AuthView(APIView):
    """ 用戶登錄認(rèn)證 """

    def post(self, request, *args, **kwargs):
        print(request.data)  # {"username": "wupeiqi", "password": "123"}
        username = request.data.get('username')
        password = request.data.get('password')

        user_object = models.UserInfo.objects.filter(username=username, password=password).first()
        if not user_object:
            return Response({"code": 1000, "data": "用戶名或密碼錯(cuò)誤"})

        token = str(uuid.uuid4())

        user_object.token = token
        user_object.save()

        return Response({"code": 0, "data": {"token": token, "name": username}})


class TokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get("token")
        if not token:
            raise AuthenticationFailed({"code": 1002, "data": "認(rèn)證失敗"})
        user_object = models.UserInfo.objects.filter(token=token).first()
        if not user_object:
            raise AuthenticationFailed({"code": 1002, "data": "認(rèn)證失敗"})
        return user_object, token

    def authenticate_header(self, request):
        return 'Bearer realm="API"'


class PermissionA(BasePermission):
    message = {"code": 1003, 'data': "無(wú)權(quán)訪問(wèn)"}

    def has_permission(self, request, view):
        exists = request.user.roles.filter(title="員工").exists()
        if exists:
            return True
        return False

    def has_object_permission(self, request, view, obj):
        return True


class PermissionB(BasePermission):
    message = {"code": 1003, 'data': "無(wú)權(quán)訪問(wèn)"}

    def has_permission(self, request, view):
        exists = request.user.roles.filter(title="主管").exists()
        if exists:
            return True
        return False

    def has_object_permission(self, request, view, obj):
        return True

class OrderView(APIView):
    authentication_classes = [TokenAuthentication, ]
    permission_classes = [PermissionA, PermissionA]

    def get(self, request, *args, **kwargs):
        return Response({"code": 0, "data": {"user": None, 'list': [1, 2, 3]}})


class PayView(APIView):
    authentication_classes = [TokenAuthentication, ]
    permission_classes = [PermissionA, ]

    def get(self, request, *args, **kwargs):
        return Response({"code": 0, "data": "數(shù)據(jù)..."})

 

 

關(guān)于 has_object_permission【欠】

當(dāng)我們使用drf來(lái)編寫(xiě) 視圖類(lèi)時(shí),如果是繼承 APIView,則 has_object_permission不會(huì)被執(zhí)行(沒(méi)用),例如:

from rest_framework.views import APIView

class PayView(APIView):
    authentication_classes = [TokenAuthentication, ]
    permission_classes = [PermissionA, ]

    def get(self, request, *args, **kwargs):
        return Response({"code": 0, "data": "數(shù)據(jù)..."})

 

但是,當(dāng)我們后期學(xué)習(xí)了 視圖類(lèi)的各種騷操作之后,發(fā)現(xiàn)視圖也可以繼承 GenericAPIView,此時(shí) 有可能 會(huì)執(zhí)行 has_object_permission 用于判斷是否有權(quán)限訪問(wèn)某個(gè)特定ID的對(duì)象(學(xué)完視圖后,再細(xì)講)。

 

 

調(diào)用 self.get_object 方法時(shí),會(huì)按照 permission_classes中權(quán)限組件的順序,依次執(zhí)行他們的 has_object_permission 方法。

self.get_object其實(shí)就根據(jù)用戶傳入的 pk,搜索并獲取某個(gè)對(duì)象的過(guò)程。

 

全局配置

REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES":["xxxx.xxxx.xx.類(lèi)名","xxxx.xxxx.xx.類(lèi)名",]
}

 

 

底層源碼實(shí)現(xiàn)

 

 

小結(jié)

  • 請(qǐng)求的封裝

  • 版本的處理

    • 過(guò)程:選擇版本處理類(lèi),獲取用戶傳入的版本信息

    • 結(jié)果:在 request.version = 版本、request.versioning_scheme=版本處理類(lèi)的對(duì)象

  • 認(rèn)證組件,在視圖執(zhí)行之前判斷用戶是否認(rèn)證成功。

    • 過(guò)程:執(zhí)行所有的認(rèn)證類(lèi)中的 authenticate 方法

      • 返回None,繼續(xù)執(zhí)行后續(xù)的認(rèn)證類(lèi)(都未認(rèn)證成功,request.user 和 auth有默認(rèn)值,也可以全局配置)

      • 返回2個(gè)元素的元組,中斷

      • 拋出 AuthenticationFailed,中斷

    • 結(jié)果:在 request.userrequest.auth 賦值(后續(xù)代碼可以使用)

  • 權(quán)限

    • 過(guò)程:執(zhí)行所有的權(quán)限類(lèi)的has_permission方法,只有所有都返回True時(shí),才表示具有權(quán)限

    • 結(jié)果:有權(quán)限則可以執(zhí)行后續(xù)的視圖,無(wú)權(quán)限則直接返回 自定義的錯(cuò)誤信息

    •