目錄

  • 認證
  • 登錄接口
  • 路由
  • 視圖類
  • 認證類
  • 權限
  • 權限類
  • 頻率
  • 頻率類
  • 視圖類
  • 配置文件
  • 自定義頻率類
  • drf內置認證類、權限類、頻率類
  • 認證類、權限類、頻率類源碼分析
  • 認證類
  • 權限類
  • 頻率類
  • 排序
  • 過濾
  • 全局異常處理
  • 使用步驟
  • 自動生成接口文檔
  • RBAC介紹
  • 補充1
  • 回顧
  • 補充2
  • 補充3

認證

登錄認證《————某些接口必須要登錄以後才能訪問
登錄接口————》登錄成功返回隨機字符串————》攜帶隨機字符串【認證】通過再繼續訪問接口
APIView源碼————》三大認證實在視圖類的方法之前執行的
# 寫一個登錄接口
    用户表	用户token表
    前端傳入用户名密碼--視圖類--登錄方法--
     --校驗用户名密碼--正確生成隨機字符串存入數據庫--把隨機字符串返回給前端
# 再隨便寫一個接口 --登錄後才能訪問
# 寫認證類
    1.寫一個類 繼承BaseAuthentication
    2.再類中重寫authenticate方法
    3.在方法中做驗證 如果通過--返回兩個值  
    	            不通過--拋AuthenticationFailed異常
# 使用認證類
	局部使用-->視圖類中
    	class BookView(APIView):
            authentication_classes = [LoginAuth, ]

	全局使用-->配置文件中
    	REST_FRAMEWORK = {
            # 全局使用認證類
            'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
        }
        
        局部禁用
    	class BookView(APIView):
            authentication_classes = []

登錄接口

# 模型類
class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, '管理員'), (2, '普通用户'), (3, '遊客')), default=3)

class UserToken(models.Model):
    token_code = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE)

路由

from .views import UserView, BookView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('user', UserView, 'user')
router.register('book', BookView, 'book')
urlpatterns = [
]
urlpatterns+=router.urls

視圖類

class UserView(ViewSet):
    authentication_classes = []	 # 局部禁用 總不能登錄也要先登錄才能登錄吧
	# 127.0.0.1:8080/user/user/login--->post
    @action(methods=['post',], detail=False)
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 登錄成功-->生成隨機字符串-->uuid能夠生成一個隨機不重複字符串           
            token = str(uuid.uuid4())
            # 存到UserToken表內	有就更新 沒有新建
            # defaults=None,**kwargs 根據傳入的關鍵字參數去查 查到就用defaults給的更新	此處為user
            UserToken.objects.update_or_create(user=user, defaults={'token_code': token})
            return Response({'code': 100, 'msg': '登錄成功', 'token_code': token})
        else:
            return Response({'code': 101, 'msg': '用户名或密碼錯誤'})

認證類

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken

class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 做驗證 驗證用户是否登錄	把生成的token放到請求頭裏
        if request.method in ['POST', 'PUT', 'DELETE']:
            # 增刪改需要登錄才能操作
            token = request.query_params.get('token')
            # 拿到請求頭裏的token 去數據庫查
            user_token_obj = UserToken.objects.filter(token_code=token).first()
            if user_token_obj:
		# 驗證通過返回兩個值————當前登錄用户,token
                return user_token_obj.user, token
            else:
                raise AuthenticationFailed('登錄才可以操作哦')

權限

登錄成功後 但有的接口區分權限 有權限的人能才能操作
User表的user_type字段來區分權限
# 權限類的寫法
    1.寫一個類 繼承BasePermission
    2.重寫has_permission方法
    3.在has_permission中進行權限的判斷
    	有權限返回True,沒權限返回False
        返回的中文提示信息使用message字段標識

# 權限類的使用	
    1.局部使用--->視圖類中
      class BookView(APIView):
            permission_classes = [PermissionAuth, ]

    2.全局使用--->配置文件
      REST_FRAMEWORK = {
            # 全局使用認證類
            'DEFAULT_PERMISSION_CLASSES':['app01.auth.PermissionAuth',]
        }

    3.局部禁用
      class BookView(APIView):
            permission_classes = []

權限類

from rest_framework.permissions import BasePermission
class PermissionAuth(BasePermission):
    message = '你沒有權限'
    
    def has_permission(self, request, view):
        if request.method == 'DELETE':
            if request.user.user_type == 3:
                print('有權限 刪除成功')
                return True
            else:
                print('沒權限 刪除失敗')
                self.message = '你是 %s 用户, 不能操作'%request.user.get_user_type_display()
                return False
        else:
            print('沒進行刪除操作')
            return True

頻率

# 某個接口,限制訪問頻率---->可以根據IP,用户id
# 頻率類的編寫
    1.寫一個類 繼承SimpleRateThrottle
    2.重寫get_cache_key方法
    3.返回什麼 就以什麼做限制
    4.寫一個類屬性
    	scope = 'xxx'	# 這個屬性值用於配置
    5.配置文件中配置:
        REST_FRAMEWORK = {
        # 頻率類中scope對應的值
        'xxx':'3/m'   # 數字/s m h  d
        }
    6.局部使用和全局使用
    	-局部:視圖類中
        	class BookView(APIView):
    			throttle_classes = [IPThrottle, ]
        -全局:配置文件
            REST_FRAMEWORK = {
            	"DEFAULT_THROTTLE_RATES": {
                	# 頻率類中scope對應的值
                	'xxx': '3/m'  # 數字/s m h  d
            	},
            	'DEFAULT_THROTTLE_CLASSES':['app01.throttling.IPThrottle', ]
        	}

頻率類

class IPThrottle(SimpleRateThrottle):
    # 寫一個類屬性,字符串
    scope = 'xxx'
    def get_cache_key(self, request, view):
        # return 什麼就以什麼做限制  返回ip/返回用户id
        return request.META.get('REMOTE_ADDR')
        # return request.user.id  # 返回用户id

視圖類

class BookView(APIView):
    throttle_classes = [IPThrottle, ]

    def get(self, request):
        return Response('ok')

    def throttled(self, request, wait):
        from rest_framework.exceptions import Throttled
        class MyThrottled(Throttled):
            default_detail = '超限制了'
            extra_detail_singular = '還有 {wait} 秒.'
            extra_detail_plural = '超出了 {wait} 秒.'

        raise MyThrottled(wait)

配置文件

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_RATES": {
        # 頻率類中scope對應的值
        '3_min': '3/m',  # 數字/s m h  d
        'anon':'4/h',
        'user':'5/m'
    },
    'DEFAULT_THROTTLE_CLASSES':['app01.throttling.IPThrottle',]
}

自定義頻率類

# 邏輯: 
    1. 取出訪問者ip	 {192.168.1.12:[訪問時間3,訪問時間2,訪問時間1],192.168.1.12:[],192.168.1.14:[]}
    2. 判斷ip在不在訪問字典內,不在則添加進去 並加入第一次訪問時間
    3. 不是第一次 則根據ip取出訪問時間的列表	循環判斷列表不為空,並且當前時間減去列表的最後一個時間大於60s,把這種數據pop掉,這樣列表中只有60s以內的訪問時間,
    4. 判斷列表<3 説明訪問次數不足三次 將當前時間插到列表第一個位置 返回True 表示通過
    5. >=3 説明訪問超過三次 返回False驗證失敗
class MyThrottling():
    visit_record = {}

    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        ip = request.META.get('REMOTE_ADDR')        # 1.拿到ip
        import time
        ctime = time.time()

        # 2.如果ip不在訪問字典裏 表示第一次訪問 將ip,和第一次訪問時間添加進去
        if ip not in self.visit_record:
            self.visit_record[ip] = [ctime, ]
            #  {192.168.1.12:[訪問時間3,訪問時間2,訪問時間1],192.168.1.12:[],192.168.1.14:[]}
            return True
        # 3.不是第一次 則 取出ip對應的時間列表
        self.history = self.visit_record.get(ip)        # 時間列表[時間3,時間2,時間1]
        while self.history and ctime - self.history[-1] > 60:  # 將現在時間和列表從後往前比 超過60s的去掉
            self.history.pop()

        # 4.列表<3  説明訪問不足三次  便把當前時間插到列表第一個 返回True
        if len(self.history) < 3:
            self.history.insert(0, ctime)
            return True
        else:
            return False

    def wait(self):
        import time
        ctime = time.time()
        return 60 - (ctime - self.history[-1])

drf內置認證類、權限類、頻率類

# 內置的認證---》跟咱們項目都不貼和,咱們不用,咱們自己根據自己的規則寫
    -SessionAuthentication:之前老的session認證登錄方式用,後期都不用了
    -BasicAuthentication :基本認證方式,咱們不用
    -TokenAuthentication :使用token認證方式,有用,但是咱們也是自己寫的    
    
# 內置的權限類
    -IsAdminUser :校驗是不是auth的超級管理員權限
    -IsAuthenticated:後面會用到,驗證用户是否登錄了,登錄了才有權限,沒登錄就沒有權限
    -IsAuthenticatedOrReadOnly:知道有這個東西即可
    
# 內置的頻率類
    -UserRateThrottle :限制登錄用户的頻率,需要配置配置文件
    -AnonRateThrottle:登錄用户不限制,未登錄用户限制,需要配置配置文件

認證類、權限類、頻率類源碼分析

# 研究的第一個點:三大認證的執行順序
    1.APIView--->dispatch--->三大認證
  	self.initial(request, *args, **kwargs)	# 執行三大認證
     	'self'為視圖類的對象----如BookView的對象

    2.研究initial方法:執行了三個方法
    	self.perform_authentication(request)	# 認證
        self.check_permissions(request)		# 權限
        self.check_throttles(request)	# 頻率
# 相關源碼
class APIView(View):
    def dispatch(self, request, *args, **kwargs):
    	......
        try:
            self.initial(request, *args, **kwargs)	# 先認證再執行請求
            if request.method.lower() in self.http_method_names:
                ...
    
    def initial(self, request, *args, **kwargs):
        ......
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

認證類

# 為什麼寫了認證類,配置在視圖類上,就會走認證?
# 入口:認證類怎麼執行的---->self.perform_authentication(request)
    1.self.perform_authentication源碼
    	def perform_authentication(self, request):
		    request.user
            
    2.user是新的request類中的 user(屬性?方法?)
    	---->'Request'類內找到user 是一個方法 但加了@property
        @property
        def user(self):
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    self._authenticate()	# 一開始沒有_user 於是執行這句
            return self._user        
        
    3.self._authenticate()  ---->self為Request的對象---->執行的是Request的_authenticate方法
    	def _authenticate(self):
            # authenticator配置的認證類的對象
            for authenticator in self.authenticators:	# 見第四點
                # 你配置在視圖類上authentication_classes = [你寫的認證類,]---->[你寫的認證類1(),你寫的認證類2()]	
                try:
                    # 調用認證類對象的authenticate方法 傳了兩個參數!一個認證類自己 一個self(Request類的對象)
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:	 # 拋的是AuthenticationFailed,捕獲的是APIException	
                    self._not_authenticated()
                    raise

                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    # 如果返回了兩個值,第一個值給了request.user,第二個值給了request.auth
                    self.user, self.auth = user_auth_tuple
                    return
            self._not_authenticated()
        
    4.self.authenticators到底是啥?
    	# 你配置在視圖類上authentication_classes = [你寫的認證類,]---->[你寫的認證類1(),你寫的認證類2()]		
        Reqeust這個類實例化的時候,傳入的,如果不傳就是空元組
        找Request的實例化----> dispatch中包裝了新的Request  
        request = self.initialize_request(request, *args, **kwargs)
        authenticators=self.get_authenticators()
        
        # APIView中的get_authenticators
        return [auth() for auth in self.authentication_classes]
    	self.authentication_classes為配在視圖類上的認證類列表

點擊查看相關源碼

# 相關源碼
class APIView(View):
    def perform_authentication(self, request):
	request.user

    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]

    def initialize_request(self, request, *args, **kwargs):
        ...
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),	這裏 !!!!!!!!!!	
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    def dispatch(self, request, *args, **kwargs):
        request = self.initialize_request(request, *args, **kwargs)
        
    
    
class Request:
    def __init__(self,request...authenticators=None...):
        self._request = request
    	self.authenticators = authenticators or ()
        
    @property
    def user(self):
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()	# 一開始沒有_user 於是執行這句
                return self._user  
            
    def _authenticate(self):
        for authenticator in self.authenticators:
            try:
                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()

權限類

1. 執行權限類 self.check_permissions(request)

    def check_permissions(self, request):
        for permission in self.get_permissions(): # 配在視圖類上的權限類列表對象
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )

  2. 研究self.get_permissions()
    def get_permissions(self):
        return [permission() for permission in self.permission_classes]

頻率類

1. self.check_throttles(request)
    def check_throttles(self, request):
        throttle_durations = []
        for throttle in self.get_throttles(): # 配在視圖類上頻率類列表 頻率類的對象
            if not throttle.allow_request(request, self):
                throttle_durations.append(throttle.wait())
        if throttle_durations:
            durations = [
                duration for duration in throttle_durations
                if duration is not None
            ]

            duration = max(durations, default=None)
            self.throttled(request, duration)
            
            
# 自定義頻率類一定要重寫allow_request, 返回True就是沒有頻率顯示,返回False就是被頻率限制了

# 聯想我們寫過的繼承SimpleRateThrottle的類
	SimpleRateThrottle重寫了allow_request方法
    
class SimpleRateThrottle(BaseThrottle):
  	
    def __init__(self):
        if not getattr(self, 'rate', None):
            self.rate = self.get_rate()	# self.rate現在是 '3/m'
             # 3		 60
        self.num_requests, self.duration = self.parse_rate(self.rate)  # 從parse_rate方法獲取
  
    def get_rate(self):    # 
        if not getattr(self, 'scope', None):
            msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                   self.__class__.__name__)
            raise ImproperlyConfigured(msg)

        try:
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)

    def parse_rate(self, rate):
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        return (num_requests, duration)
        
    def allow_request(self, request, view):        
        # 只要配置文件配了 就有值 在init中
        if self.rate is None:	
            return True
	# 現在的唯一字符串	ip地址
        self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True
	# [時間2, 時間1]
        self.history = self.cache.get(self.key, [])
        self.now = self.timer()

        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()	# 把和現在差60s的數據都剔除 self.history只剩60s內的訪問時間
        if len(self.history) >= self.num_requests:	# >=配置的數字 3
            return self.throttle_failure()	# return False
        return self.throttle_success()	# 把當前的時間插入 return True

排序

# 排序功能接口只針對於:獲取所有接口

# 繼承了GenericAPIView的視圖類,只要加入,兩個類屬性
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    filter_backends = [OrderingFilter, ]
    ordering_fields = ['price','id']

# 訪問的時候
http://127.0.0.1:8000/api/v1/books?ordering=price  	# 按price升序
http://127.0.0.1:8000/api/v1/books?ordering=-price 	# 按price降序
http://127.0.0.1:8000/api/v1/books?ordering=price,id    # 先按價格升序排,價格一樣,再按id升序排

過濾

1. 內置的過濾使用:不能指定查詢哪個字段 '模糊查詢'
    繼承了GenericAPIView的視圖類,只要加入,兩個類屬性
    
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    filter_backends = [SearchFilter,]
    search_fields=['name','price']  # 按name或price過濾    

# 使用
	http://127.0.0.1:8000/api/v1/books?search=關鍵字	# name like xx or price like xx
2. 第三方django-filter: 指定字段精確查詢
    -安裝:pip3 install django-filter
    繼承了GenericAPIView的視圖類,只要加入,兩個類屬性

from django_filters.rest_framework import DjangoFilterBackend
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    filter_backends = [DjangoFilterBackend, ]
    filter_fields = ['name', 'price']    
    
# 使用    
        http://127.0.0.1:8000/api/v1/books?name=三國&price=11
3.自定義過濾器---->完成更多查詢操作
    -寫一個類,繼承BaseFilterBackend
    -重寫filter_queryset方法
    -配置在視圖類中

from .throttling import FilterName
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    filter_backends = [FilterName, ]

# 既有過濾又有排序
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()
    filter_backends = [FilterName, OrderingFilter]
    ordering_fields = ['price', ]
    
# 源碼分析,為什麼這麼配置就生效
    -GenericAPIView的方法

def filter_queryset(self, queryset):
    for backend in list(self.filter_backends):
        queryset = backend().filter_queryset(self.request, queryset, self)
    return queryset

全局異常處理

drf配置文件中 已經配置了 但不符合我們想要的要求
# drf的配置文件:
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
     如果拋了異常 就會執行exception_handler函數
    
現在我們重寫一個函數 拋異常時 執行我們寫的函數

# drf默認的異常處理 只處理了drf自己的異常:
    所有drf中拋的異常	都有detail,django的異常	拋出很長的xml數據
    
{
    "detail": "it's wrong"
}    

# 我們想要的
{
    "code": 999,
    "msg": "it's wrong"
}

使用步驟

1.寫一個函數
from rest_framework.views import exception_handler
from rest_framework.response import Response
def common_exception_handler(exc, context):
    # 正常來講,在這裏需要記錄日誌---》如何在django中記錄日誌後面講
    # 日誌記錄,越詳細越好:哪個用户(id,ip),在什麼時間,執行哪個視圖函數時報了錯,請求地址是什麼
    print(context['view'])  # 視圖類對象
    print(context['request'])   # 當前請求對象----->可以取出ip,用户id,當前時間,請求地址來
    view = context['view']
    request = context['request']
    print('ip地址為: %s 的用户 訪問了:%s 視圖類, 報錯 請求地址為:%s' % (request.META.get('REMOTE_ADDR'), str(view), request.path))

    response = exception_handler(exc, context)
    if response:    # 這是drf的異常 drf已處理 但不是我們想要的格式{'code': 100, 'msg': ''}
        res = Response({'code': 999, 'msg': request.data.get('detail')})
    else:
        # res=Response({'code':998,'msg':'服務器錯誤,請聯繫系統管理員'})
        res = Response({'code': 998, 'msg': str(exc)})
    return res

2.把函數配置在配置文件中
REST_FRAMEWORK = {
    # 自己寫的全局異常捕獲
        'EXCEPTION_HANDLER': 'app01.exception.common_exception_handler',
    }

自動生成接口文檔

# 後端人員,寫好接口,提供接口文檔給前端用

# 如何編寫接口文檔
    1. 使用word寫,md寫---->提交到git上
    2.公司有接口文檔平台---->後端人員在文檔平台錄入數據---->公司自己開發,yapi(百度開源),第三方
    3.自動生成接口文檔---->項目寫好了,一鍵生成接口文檔---->一鍵生成---->導出---->導入到yapi
    
# drf中自動生成接口文檔
    -coreapi,swagger(更通用一些,go,java)
    -安裝 pip3 install coreapi
    -在項目中配置	
    	-加入路由:
        from rest_framework.documentation import include_docs_urls
        urlpatterns = [
            path('docs/', include_docs_urls(title='站點頁面標題'))
        ]
        -配置文件中配置
        REST_FRAMEWORK = {
         'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',

        }
        
        -儘管寫接口,寫註釋,會自動生成

RBAC介紹

# RBAC
	基於角色的訪問控制(Role-Based Access Control)
    在RBAC中:
    	權限與角色相關聯 用户通過成為適當角色的成員而獲得角色的權限
        這就極大地簡化了權限的管理。
        這樣管理都是層級相互依賴的,權限賦予給角色,而把角色又賦予用户,這樣的權限設計很清楚,管理起來很方便
        
# 這種設計,在公司內部系統用的多,對外的系統基本不用

# 權限:真正的權限,比如發工資,招人,申請測試機
# 角色:(組,部門) 角色下有很多員工
# 用户:一個個的人

# 用户和角色關係是多對多,中間表
# 權限和角色關係是多對多,中間表
# 權限和用户的中間表

#前後端分離項目控制權限---》權限類中
#前後端混合
    -django框架在公司裏用的很多,寫內部項目,肯定要用權限控制,用的就是rbac,自己實現一套,django內置了後台管理,自帶rbac(admin+auth)
    -基於django的admin二次開發
    	-美化:xadmin(基本不用了,2.x以後django不兼容多,作者棄坑了)
        -simpleui:
    -django+drf+vue=djagnovueadmin  一款前後端分離的自帶rbac的後台管理框架
    
# django的admin基於rbac
    -auth_user  	 # 用户表
    -auth_permission  # 權限表
    -auth_group  	 # 組,角色表
    
    -auth_user_groups   # 用户和組中間表
    -auth_group_permissions  # 組和權限中間表
    -auth_user_user_permissions  # 用户和權限中間表

補充1

# external libraries
    -Python 解釋器--內置模塊和包:os,sys,json 。。
    -import os
    -裝的第三模塊:site-packages中
    	-真正的位置:python解釋器安裝路徑下的site-package中
        -有些第三方模塊一安裝---》釋放可執行文件---》一定要注意,釋放的位置釋放在環境變量裏
        
# 安裝第三方模塊
    -pychram中圖形化界面裝-----》必須有pycharm---》換源
    -pip install django==2.2.2 -i 國內源
     -敲的pip 是哪個解釋器的pip 確認好
        
# 換源
    -裝第三方模塊---》本質就是從遠端某個位置拉了一個包 xx.whl--->釋放到了python解釋器的site-package文件夾下,把可執行文件釋放到scripts文件夾下
    -用官方源:在國外---》下載慢
    -使用國內鏡像:豆瓣,阿里,清華

回顧

# http回顧
    -http 請求:特點
    -1 http基於tcp協議之上的協議---->tcp處於osi七層的傳輸層  http處於應用層
    -2 基於請求響應----必須是客户端發起請求,服務端響應	不能服務端主動推送信息
    -3 無狀態,無連接	cookie和session
    -http協議有版本
    	-0.9  1.1  2.x:多路複用   3.x 
            
	-http分請求協議和響應協議
    	-請求協議
            -請求首行:  請求方式,請求地址(get地址中數據),協議版本
            -請求頭:	key:value	客户端類型,客户端ip地址,請求編碼,cookie...
	    -請求體: 所謂的body體,posy,put請求真正攜帶的數據
                    -urlencoded:  name=lqz&age=19&gender=male
                    -json :   {"name":"lqz","age":19,"gender":"male"}
                    -form-data: 兩部分:數據部分,文件部分中間用  -----------分割

	-響應協議:
            -響應首行:   協議版本,響應狀態碼,響應狀態短語
            -響應頭:     響應編碼格式,cookie...
            -響應體:     html,json,xml.....

# websocket協議	應用層協議
	-服務端主動向客户端推送的情況,使用websocket

# http請求 輪詢
# http請求 長輪詢:發過去,等一會,再回來
# websocket 協議主動推送

補充2

python	動態強類型語言
go	靜態強類型語言
java	靜態強類型語言	---隱式類型轉換
js	動態弱類型語言

# 動態:解釋性語言
# 強弱:
     數據類型強:不同類型之間不允許直接運算
     數據類型弱:不同類型之間不需要轉換可以直接運算

補充3

# 鴨子類型(面試重點)
    -接口概念:規範子類的行為   你當成:父類,有一些方法  ---》Duck類,speak,run方法
    -只要子類,繼承了Duck,我們就認為,這些子類,他們是同一類,都是鴨子Duck這個類
    -python不推崇這個,推崇鴨子類型
    -現在只要有一個類,中有speak和run 方法 ,這個類就是鴨子類
    
    -abc裝飾器,裝飾父類中的方法,子類必須實現該方法,如果不實現,就拋異常---》正好違背了鴨子類型
    -djagno中常用的方式,父類拋異常的方式,子類必須重寫該方法:
       def authenticate(self, request):
          raise NotImplementedError(".authenticate() must be overridden.")
# 函數
	def add(a:int,b:int):->int
        return a+b