RESTful

問(wèn)題1:RESTful框架到底解決了什么問(wèn)題?(URL具有自描述性、資源表述與視圖的解耦合、互操作性利用構(gòu)建微服務(wù)以及集成第三方系統(tǒng)、無(wú)狀態(tài)性太高水平擴(kuò)展能力)
問(wèn)題2:項(xiàng)目在使用RESTful架構(gòu)時(shí)有沒(méi)有遇到一些問(wèn)題或隱患?(對(duì)資源訪問(wèn)的限制、資源從屬關(guān)系檢查、避免泄露業(yè)務(wù)信息防范可能的攻擊)
補(bǔ)充:下面的幾個(gè)和安全性相關(guān)的響應(yīng)頭在前面講中間件的時(shí)候提到過(guò)的。

  • X-Frame-Options:DENY
  • X-Content-Type-Options:nosniff
  • X-XSS-Protection:1;mode=block;
  • Strict-Transport-Security:max-age=3153600;
    問(wèn)題3:如何保護(hù)API中的敏感信息以及防范重放攻擊?(摘要和令牌)
    推薦閱讀:《如何有效防止API的重放攻擊》
使用djangorestframework

安裝djangorestframework(為了方便描述,以下同意簡(jiǎn)稱drf)。
pip install djangorestframework
配置drf

INSTALLED_APPS = [
    
    'rest_framework',
    
]

REST_FRAMEWORK = {
    # 配置默認(rèn)頁(yè)面大小
    'PAGE_SIZE': 10,
    # 配置默認(rèn)的分頁(yè)類
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # 配置異常處理器
    # 'EXCEPTION_HANDLER': 'api.exceptions.exception_handler',
    # 配置默認(rèn)解析器
    # 'DEFAULT_PARSER_CLASSES': (
    #     'rest_framework.parsers.JSONParser',
    #     'rest_framework.parsers.FormParser',
    #     'rest_framework.parsers.MultiPartParser',
    # ),
    # 配置默認(rèn)限流類
    # 'DEFAULT_THROTTLE_CLASSES': (),
    # 配置默認(rèn)授權(quán)類
    # 'DEFAULT_PERMISSION_CLASSES': (
    #     'rest_framework.permissions.IsAuthenticated',
    # ),
    # 配置默認(rèn)認(rèn)證類
    # 'DEFAULT_AUTHENTICATION_CLASSES': (
    #     'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    # ),
}
編寫(xiě)序列化器
from rest_framework import serializers
from rest_framewor.serializers import MpdelSerializer
from common.models import District, HouseType, Estate, Agent

class DistrictSerializer(ModelSerializer):
  class Meta:
    model = District
    fields = ('distid', 'name')

class HouseTypeSerializer(ModelSerializer):
  class Meta:
     model = HouseType
     fields = '__all__'

class AgentSerializer(ModelSerializer):
  class Meta:
    model = Agent
    fields = ('agentid', 'name', 'tel', 'servstar', 'certificated')

class EstateSerializer(ModelSerializer):
  district = serializers.SerializerMethodField()
  agents = serializers.SerializerMethodField()

  @staticmethod
  def get_agent(estate):
    return AgentSerializer(estate.agents, many = True).data

  @staticmethod
  def get_district(estate):
    return DistrictSerializer(estate.district).data
   
 class Meta;
    model = Estate
    fields = '__all__'
方法1:使用裝飾器
@api_view(['GET'])
@cache_page(timeout=None,cache='api')
def province(request):
  query = District.objects.filter(parent_isnull=True)
  serializer = DisterictSerializer(queryset, many=True)
  return Response(serializer.data)

@api_view(['GET'])
@cache_page(timeout=300, cache='api')
def cities(request, provid)
 queryset = District.objects.filter(parent__distid=provid)
  serializer = DistrictSerializer(queryset, many=True)
  return Response(serializer.data)
urlpattern = [
  path = ('districts/', views.provinces, name='districts'),
  path = ('districts/<int:provid>/', view.cities, name = 'cities')
]

說(shuō)明:上面使用了Django自帶的視圖裝飾器(@cache_page)來(lái)實(shí)現(xiàn)API接口返回?cái)?shù)據(jù)的緩存。

方法2:使用APIview及其子類

更好的復(fù)用代碼,不要重復(fù)發(fā)明“輪子”。

class HouseTypeApiView(CacheResponseMixin, ListAPIView):
  queryset = HouseType.objects.all()
  serializer_class = HouseTypeSerializer
urlpattern = [
  path('housetypes/', views.HouseTypeApiView.as_view(), name='housetypes'),
]

說(shuō)明:上面使用了drf_extensions提供的CacheResponseMixin混入類實(shí)現(xiàn)了對(duì)接口數(shù)據(jù)的緩存。如果重寫(xiě)了獲取數(shù)據(jù)的方法,可以使用drf_extensions提供的@Cache_response來(lái)實(shí)現(xiàn)對(duì)接口數(shù)據(jù)的緩存,也可以用自定義的函數(shù)來(lái)生成緩存中的key。當(dāng)然還有一個(gè)選擇就是通過(guò)Django提供的@method_decorator裝飾器,將@cache_page裝飾器處理為裝飾方法裝飾器,這樣也能提供使用緩存服務(wù)。

drf-extensions配置如下所示。

# 配置DRF擴(kuò)展來(lái)支持緩存API接口調(diào)用結(jié)果
REST_FRAMEWORK = {
  'DEFAULT_CACHE_RESPONSE_TIMEOUT' : 300,
  'DEFAULT_USER_CACHE' : 'default',
# 配置默認(rèn)緩存單個(gè)對(duì)象的key函數(shù)  
  'DEFAULT_OBJECT_CACHE_KEY_FUNC':'rest_framework_extensions.utils.default_object_cache_key_func',
  # 配置默認(rèn)緩存對(duì)象列表的key函數(shù)
  'DEFAULT_LIST_CACHE_KEY_FUNC' : 'rest_framework_extensions.utils.default_list_cache_key_func',
}
方法3:使用ViewSet及其子類
class HouseTypeView(CacheResponseMixin, viewsets.ModelViewSet):
  queryset = HouseType.objects.all()
  serializer_class = HouseTypeSerializer
  pagination_class = None
router = DefaultRoute()
router.register('housetypes',views.HouseTypeViewSet)

urlpattern += router.urls

djangorestframework提供了Bootstrap定制的頁(yè)面來(lái)顯示接口返回的JSON數(shù)據(jù),當(dāng)然也可以使用POSTMAN這樣的工具來(lái)對(duì)API接口進(jìn)行測(cè)試

補(bǔ)充說(shuō)明

在這里順便提一下跟前端相關(guān)的幾個(gè)問(wèn)題
問(wèn)題一:如何讓瀏覽器能夠發(fā)起DELETE/PUT/PATCH?

<form method='post'>
  <input type="hiden" name="_mehthod" value="delete">
</form>
if request.method == 'POST' and '_method' in request.POST:
  request.method = request.POST['_method'].upper()
<script>
  $.ajax({
    'url' : '/api/provinces',
    'type' : 'put',
    'data' : {},
    'dataType' : 'json',
    'success' : 'functon(json){}',
    'error' : function(){}
});
  $.getJSON('api/provinces',function(json){});
</scritpt>

問(wèn)題2:如何解決多個(gè)JavaScript庫(kù)之間某個(gè)定義(如$函數(shù))沖突的 問(wèn)題?

<script src="js/jquery.min.js"></script>
<script src="js/abc.min.js"></script>
<script>
    // $已經(jīng)被后加載的JavaScript庫(kù)占用了
    // 但是可以直接用綁定在window對(duì)象上的jQuery去代替$
    jQuery(function() {
        jQuery('#okBtn').on('click', function() {});
    });
</script>
<script src="js/abc.min.js"></script>
<script src="js/jquery.min.js"></script>
<script>
    // 將$讓出給其他的JavaScript庫(kù)使用
    jQuery.noConflict();
    jQuery(function() {
        jQuery('#okBtn').on('click', function() {});
    });
</script>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容