1.restful api的规范
- API与用户的通信协议,总是使用HTTPs协议。
- 域名
- https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
- https://example.org/api/ API很简单
- 版本
- URL,如:https://api.example.com/v1/
- 请求头 跨域时,引发发送多次请求
- 路径,视网络上任何东西都是资源,均使用名词表示(可复数)
- https://api.example.com/v1/zoos
- https://api.example.com/v1/animals
- https://api.example.com/v1/employees
- method
- GET :从服务器取出资源(一项或多项)
- POST :在服务器新建一个资源
- PUT :在服务器更新资源(客户端提供改变后的完整资源)
- PATCH :在服务器更新资源(客户端提供改变的属性)
- DELETE :从服务器删除资源
- 过滤,通过在url上传参的形式传递搜索条件
- https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
- https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
- https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
- https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
在进行API的设计中要执行restful的规范,比如api尽量使用名词可以是复数如user/users,不要使用形容词如show。 api中可以添加版本号如:https://api.example.com/v1/ API的域名中可以添加“api”字符作为标识如: https://api.example.com 尽量将API部署在专用域名(会存在跨域问题) https://example.org/api/ API很简单; 过滤可以以参数进行传递: https://api.example.com/v1/zoos?limit=10:指定返回记录的数量 https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置 https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数 https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序 https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件 提交方式可以进行获取,添加,删除,修改等操作 GET :从服务器取出资源(一项或多项) POST :在服务器新建一个资源 PUT :在服务器更新资源(客户端提供改变后的完整资源) PATCH :在服务器更新资源(客户端提供改变的属性) DELETE :从服务器删除资源
2.用户认证:
#view.py class MyAuthentication(BaseAuthentication): def authenticate(self, request): # return None ,我不管 token = request.query_params.get('token') obj = models.UserInfo.objects.filter(token=token).first() if obj: return (obj.username,obj) raise APIException('用户认证失败') class AuthView(APIView): authentication_classes=[MyAuthentication,] class HostView(APIView): def get(self,request,*args,**kwargs): # 原来request对象,django.core.handlers.wsgi.WSGIRequest # 现在的request对象,rest_framework.request.Request print(request.user) print(request.auth) return HttpResponse('主机列表') #settings.py REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, "DEFAULT_AUTHENTICATION_CLASSES": [ "app02.utils.MyAuthentication", ], }
3.授权(+认证)
class MyAuthentication(BaseAuthentication): def authenticate(self, request): token = request.query_params.get('token') obj = models.UserInfo.objects.filter(token=token).first() if obj: return (obj.username,obj) return None def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ # return 'Basic realm="api"' pass class MyPermission(object): message = "无权访问" def has_permission(self,request,view): if request.user: return True return False class AdminPermission(object): message = "无权访问" def has_permission(self,request,view): if request.user == 'alex': return True return False class HostView(APIView): """ 匿名用户和用户都能访问 """ authentication_classes = [MyAuthentication,] permission_classes = [] def get(self,request,*args,**kwargs): # 原来request对象,django.core.handlers.wsgi.WSGIRequest # 现在的request对象,rest_framework.request.Request self.dispatch print(request.user) # print(request.user) # print(request.auth) return Response('主机列表')
4.版本
urlpatterns = [ #url(r'^admin/', admin.site.urls), url(r'^api/(?P<version>[v1|v2]+)/', include('api.urls')), ] urlpatterns = [ url(r'^users/', views.UsersView.as_view(),name='u'), ] class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch print(request.version) # QueryParameterVersioning().detemiin_version() print(request.versioning_scheme) # QueryParameterVersioning() REST_FRAMEWORK = { 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], # 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning" 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning" } HostName urlpatterns = [ #url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), ] urlpatterns = [ url(r'^users/', views.UsersView.as_view(),name='u'), ] class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch print(request.version) # QueryParameterVersioning().detemiin_version() print(request.versioning_scheme) # QueryParameterVersioning() REST_FRAMEWORK = { 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning" } # C:WindowsSystem32driversetc # vim /etc/hosts 127.0.0.1 v1.luffy.com 127.0.0.1 v2.luffy.com
5.解析器
请求的数据进行解析:请求体进行解析。表示服务端可以解析的数据格式的种类。 Content-Type: application/url-encoding..... request.body request.POST Content-Type: application/json..... request.body request.POST 客户端: Content-Type: application/json '{"name":"alex","age":123}' 服务端接收: 读取客户端发送的Content-Type的值 application/json parser_classes = [JSONParser,] media_type_list = ['application/json',] 如果客户端的Content-Type的值和 application/json 匹配:JSONParser处理数据 如果客户端的Content-Type的值和 application/x-www-form-urlencoded 匹配:FormParser处理数据 配置: 单视图: class UsersView(APIView): parser_classes = [JSONParser,] 全局配置: REST_FRAMEWORK = { 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], # 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning" 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', ] }
6.序列化
a. 基本操作: class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 # user_list = models.UserInfo.objects.all() # ser = UsersSerializer(instance=user_list,many=True) # return Response(ser.data) # 方式二之单对象 user = models.UserInfo.objects.all().first() ser = UsersSerializer(instance=user, many=False) return Response(ser.data) b. 跨表 class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() group_id = serializers.CharField() xxxx = serializers.CharField(source="group.title") x1 = serializers.CharField(source="group.mu.name") class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data) c. 复杂序列化 解决方案一: class MyCharField(serializers.CharField): def to_representation(self, value): data_list = [] for row in value: data_list.append(row.name) return data_list class UsersSerializer(serializers.Serializer): name = serializers.CharField() # obj.name pwd = serializers.CharField() # obj.pwd group_id = serializers.CharField() # obj.group_id xxxx = serializers.CharField(source="group.title") # obj.group.title x1 = serializers.CharField(source="group.mu.name") # obj.mu.name # x2 = serializers.CharField(source="roles.all") # obj.mu.name x2 = MyCharField(source="roles.all") # obj.mu.name 解决方案二: class MyCharField(serializers.CharField): def to_representation(self, value): return {'id':value.pk, 'name':value.name} class UsersSerializer(serializers.Serializer): name = serializers.CharField() # obj.name pwd = serializers.CharField() # obj.pwd group_id = serializers.CharField() # obj.group_id xxxx = serializers.CharField(source="group.title") # obj.group.title x1 = serializers.CharField(source="group.mu.name") # obj.mu.name # x2 = serializers.CharField(source="roles.all") # obj.mu.name x2 = serializers.ListField(child=MyCharField(),source="roles.all") # obj.mu.name 解决方案三(*): class UsersSerializer(serializers.Serializer): name = serializers.CharField() # obj.name pwd = serializers.CharField() # obj.pwd group_id = serializers.CharField() # obj.group_id xxxx = serializers.CharField(source="group.title") # obj.group.title x1 = serializers.CharField(source="group.mu.name") # obj.mu.name # x2 = serializers.CharField(source="roles.all") # obj.mu.name # x2 = serializers.ListField(child=MyCharField(),source="roles.all") # obj.mu.name x2 = serializers.SerializerMethodField() def get_x2(self,obj): obj.roles.all() role_list = obj.roles.filter(id__gt=1) data_list = [] for row in role_list: data_list.append({'pk':row.pk,'name':row.name}) return data_list 以上三种都是使用相同的视图: class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data) d. 基于Model class UsersSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" # fields = ['name', 'pwd','group'] depth = 1 class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data) e. 生成URL class UsersSerializer(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name='detail') class Meta: model = models.UserInfo fields = "__all__" fields = ['name', 'pwd','group'] depth = 1 class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True,context={'request':request}) return Response(ser.data) f. 全局生成URL class UsersSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = models.UserInfo fields = "__all__" # fields = ['id','name','pwd'] class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True,context={'request':request}) return Response(ser.data) 请求数据验证: a. class PasswordValidator(object): def __init__(self, base): self.base = base def __call__(self, value): if value != self.base: message = '用户输入的值必须是 %s.' % self.base raise serializers.ValidationError(message) def set_context(self, serializer_field): """ This hook is called by the serializer instance, prior to the validation call being made. """ # 执行验证之前调用,serializer_fields是当前字段对象 pass class UsersSerializer(serializers.Serializer): name = serializers.CharField(min_length=6) pwd = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('666')]) b. class PasswordValidator(object): def __init__(self, base): self.base = base def __call__(self, value): if value != self.base: message = '用户输入的值必须是 %s.' % self.base raise serializers.ValidationError(message) def set_context(self, serializer_field): """ This hook is called by the serializer instance, prior to the validation call being made. """ # 执行验证之前调用,serializer_fields是当前字段对象 pass class UsersSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" extra_kwargs = { 'name': {'min_length': 6}, 'pwd': {'validators': [PasswordValidator(666), ]} } 使用: class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True,context={'request':request}) return Response(ser.data) def post(self,request,*args,**kwargs): ser = UsersSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return Response('...')
7.分页
a. 基于limit offset做分页 class P1(LimitOffsetPagination): max_limit = 3 default_limit = 2 limit_query_param = 'limit' offset_query_param = 'offset' class IndexView(views.APIView): def get(self,request,*args,**kwargs): user_list = models.UserInfo.objects.all() p1 = P1() page_user_list = p1.paginate_queryset(queryset=user_list, request=request, view=self) ser = IndexSerializer(instance=page_user_list, many=True) return Response(ser.data) # 不含上一页和下一页 # return p1.get_paginated_response(ser.data) # 含上一页和下一页 class IndexView(views.APIView): def get(self,request,*args,**kwargs): ret = BaseResponse() try: user_list = models.UserInfo.objects.all() p1 = P1() page_user_list = p1.paginate_queryset(queryset=user_list,request=request,view=self) ser = IndexSerializer(instance=page_user_list,many=True) ret.data = ser.data ret.next = p1.get_next_link() except Exception as e: ret.code= 1001 ret.error = 'xxxx错误' return Response(ret.__dict__) b. 基于页码的分页 class P2(PageNumberPagination): # 每页显示的数据条数 max_page_size = 5 page_size = 2 page_size_query_param = 'size' # 页码 page_query_param = 'page' c. 基于Cursor的分页 class P3(CursorPagination): cursor_query_param = 'cursor' page_size = 2 ordering = 'id'
8.视图
1. APIView class IndexView(views.APIView): def get(self, request, *args, **kwargs): user_list = models.UserInfo.objects.all() ser = IndexSerializer(instance=user_list,many=True) return Response(ser.data) 2. GenericAPIView(APIView) 3. GenericViewSet(ViewSetMixin, generics.GenericAPIView) 路由修改: urlpatterns = [ url(r'^index/$', views.IndexView.as_view({'get':'list','post':'create'})), url(r'^index/(?P<pk>d+)$', views.IndexView.as_view({'get':'retrieve'})), ] 视图修改: class IndexView(viewsets.GenericViewSet): def list(self,request,*args,**kwargs): pass # 获取列表信息 def retrieve(self, request, *args, **kwargs): pass # 获取单条数据 def create(self,request, *args, **kwargs): pass 自定义: 增 POST /users/ 删 DELETE /users/1/ 改 PUT /users/1/ patch /users/1/ 查 GET /users/ GET /users/1/ urlpatterns = [ url(r'^index/$', views.IndexView.as_view()), url(r'^index/(?P<pk>d+)$', views.IndexView.as_view()), ] class IndexView(views.APIView): def get(self,request,*args,**kwargs): pk = kwargs.get('pk') if pk: pass # 获取单条信息 else: pass # 获取列表信息 def post(self,request,*args,**kwargs): pass def put(self,request,*args,**kwargs): pass def patch(self,request,*args,**kwargs): pass def delete(self,request,*args,**kwargs): pass 4. ModelViewSet(mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,GenericViewSet) class IndexView(ModelViewSet):
9.路由
第一类: # http://127.0.0.1:8000/api/v1/auth/ url(r'^auth/$', views.AuthView.as_view()), # http://127.0.0.1:8000/api/v1/auth.json # 想要让页面显示json格式 url(r'^auth.(?P<format>[a-z0-9]+)$', views.AuthView.as_view()), # http://127.0.0.1:8000/api/v1/auth/1/ url(r'^auth/(?P<pk>d+)/$', views.AuthView.as_view()), # http://127.0.0.1:8000/api/v1/auth/1.json url(r'^auth/(?P<pk>d+).(?P<format>[a-z0-9]+)$', views.AuthView.as_view()), class AuthView(views.APIView): def get(self,request,*args,**kwargs): return Response('...') 第二类: url(r'^index/$', views.IndexView.as_view({'get':'list','post':'create'})), url(r'^index/.(?P<format>[a-z0-9]+)$', views.IndexView.as_view({'get':'list','post':'create'})), url(r'^index/(?P<pk>d+)/$', views.IndexView.as_view({'get':'retrieve','delete':'destroy','put':'update','patch':'partial_update'})), url(r'^index/(?P<pk>d+).(?P<format>[a-z0-9]+)$', views.IndexView.as_view({'get':'retrieve','delete':'destroy','put':'update','patch':'partial_update'})), class IndexView(viewsets.ModelViewSet): queryset = models.UserInfo.objects.all() serializer_class = IndexSerializer pagination_class = P2 第三类: router = DefaultRouter() router.register('index',views.IndexViewSet) urlpatterns = [ url(r'^', include(router.urls)), ] class IndexViewSet(viewsets.ModelViewSet): queryset = models.UserInfo.objects.all() serializer_class = IndexSerializer pagination_class = P2 class IndexSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__"
10.渲染器
class IndexView(views.APIView): # renderer_classes = [JSONRenderer,BrowsableAPIRenderer] def get(self,request,*args,**kwargs): self.dispatch user_list = models.UserInfo.objects.all() ser = IndexSerializer(instance=user_list,many=True) return Response(ser.data) urlpatterns = [ url(r'^index/$', views.IndexView.as_view()), url(r'^index.(?P<format>[a-z0-9]+)$', views.IndexView.as_view()), ]