问题1:RESTful框架到底解决了什么问题?(URL具有自描述性、资源表述与视图的解耦合、互操作性利用构建微服务以及集成第三方系统、无状态性太高水平扩展能力)
问题2:项目在使用RESTful架构时有没有遇到一些问题或隐患?(对资源访问的限制、资源从属关系检查、避免泄露业务信息防范可能的攻击)
补充:下面的几个和安全性相关的响应头在前面讲中间件的时候提到过的。
- X-Frame-Options:DENY
- X-Content-Type-Options:nosniff
- X-XSS-Protection:1;mode=block;
- Strict-Transport-Security:max-age=3153600;
问题3:如何保护API中的敏感信息以及防范重放攻击?(摘要和令牌)
推荐阅读:《如何有效防止API的重放攻击》。
使用djangorestframework
安装djangorestframework(为了方便描述,以下同意简称drf)。
pip install djangorestframework
配置drf
INSTALLED_APPS = [
'rest_framework',
]
REST_FRAMEWORK = {
# 配置默认页面大小
'PAGE_SIZE': 10,
# 配置默认的分页类
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 配置异常处理器
# 'EXCEPTION_HANDLER': 'api.exceptions.exception_handler',
# 配置默认解析器
# 'DEFAULT_PARSER_CLASSES': (
# 'rest_framework.parsers.JSONParser',
# 'rest_framework.parsers.FormParser',
# 'rest_framework.parsers.MultiPartParser',
# ),
# 配置默认限流类
# 'DEFAULT_THROTTLE_CLASSES': (),
# 配置默认授权类
# 'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.IsAuthenticated',
# ),
# 配置默认认证类
# 'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# ),
}
编写序列化器
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')
]
说明:上面使用了Django自带的视图装饰器(@cache_page)来实现API接口返回数据的缓存。
方法2:使用APIview及其子类
更好的复用代码,不要重复发明“轮子”。
class HouseTypeApiView(CacheResponseMixin, ListAPIView):
queryset = HouseType.objects.all()
serializer_class = HouseTypeSerializer
urlpattern = [
path('housetypes/', views.HouseTypeApiView.as_view(), name='housetypes'),
]
说明:上面使用了drf_extensions提供的CacheResponseMixin混入类实现了对接口数据的缓存。如果重写了获取数据的方法,可以使用drf_extensions提供的@Cache_response来实现对接口数据的缓存,也可以用自定义的函数来生成缓存中的key。当然还有一个选择就是通过Django提供的@method_decorator装饰器,将@cache_page装饰器处理为装饰方法装饰器,这样也能提供使用缓存服务。
drf-extensions
配置如下所示。
# 配置DRF扩展来支持缓存API接口调用结果
REST_FRAMEWORK = {
'DEFAULT_CACHE_RESPONSE_TIMEOUT' : 300,
'DEFAULT_USER_CACHE' : 'default',
# 配置默认缓存单个对象的key函数
'DEFAULT_OBJECT_CACHE_KEY_FUNC':'rest_framework_extensions.utils.default_object_cache_key_func',
# 配置默认缓存对象列表的key函数
'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定制的页面来显示接口返回的JSON数据,当然也可以使用POSTMAN这样的工具来对API接口进行测试
补充说明
在这里顺便提一下跟前端相关的几个问题
问题一:如何让浏览器能够发起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>
问题2:如何解决多个JavaScript库之间某个定义(如$函数)冲突的 问题?
<script src="js/jquery.min.js"></script>
<script src="js/abc.min.js"></script>
<script>
// $已经被后加载的JavaScript库占用了
// 但是可以直接用绑定在window对象上的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库使用
jQuery.noConflict();
jQuery(function() {
jQuery('#okBtn').on('click', function() {});
});
</script>
网友评论