一. 什么是RESTful

  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
  • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

二. 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:指定筛选条件
  • 状态码
     
    200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    204 NO CONTENT - [DELETE]:用户删除数据成功。
    400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
     

    常用状态码列表

  • 错误处理,状态码是4xx时,应返回错误信息,error当做key。
    1
    2
    3
    {
        error: "Invalid API key"
    }
  • 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
    1
    2
    3
    4
    5
    6
    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档
  • Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
    1
    2
    3
    4
    5
    6
    {"link": {
      "rel":   "collection https://www.example.com/zoos",
      "href""https://api.example.com/zoos",
      "title": "List of zoos",
      "type""application/vnd.yourformat+json"
    }}

  摘自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

三. 基于Django实现

路由系统:

1
2
3
urlpatterns = [
    url(r'^users', Users.as_view()),
]

CBV视图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from django.views import View
from django.http import JsonResponse
 
class Users(View):
    def get(self, request, *args, **kwargs):
        result = {
            'status': True,
            'data': 'response data'
        }
        return JsonResponse(result, status=200)
 
    def post(self, request, *args, **kwargs):
        result = {
            'status': True,
            'data': 'response data'
        }
        return JsonResponse(result, status=200

四. 基于Django Rest Framework框架实现

1. 基本流程

url.py

1
2
3
4
5
6
from django.conf.urls import url, include
from web.views.s1_api import TestView
 
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from rest_framework.views import APIView
from rest_framework.response import Response
 
 
class TestView(APIView):
    def dispatch(self, request, *args, **kwargs):
        """
        请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法
         
        注意:APIView中的dispatch方法有好多好多的功能
        """
        return super().dispatch(request, *args, **kwargs)
 
    def get(self, request, *args, **kwargs):
        return Response('GET请求,响应内容')
 
    def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')
 
    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')

上述是rest framework框架基本流程,重要的功能是在APIView的dispatch中触发。

2.  认证和授权

a. 用户url传入的token认证

from django.conf.urls import url, include
from web.viewsimport TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions token_list = [
'sfsfss123kuf3j123',
'asijnfowerkkf9812',
] class TestAuthentication(BaseAuthentication):
def authenticate(self, request):
"""
用户认证,如果验证成功后返回元组: (用户,用户Token)
:param request:
:return:
None,表示跳过该验证;
如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None
(user,token)表示验证通过并设置用户名和Token;
AuthenticationFailed异常
"""
val = request.query_params.get('token')
if val not in token_list:
raise exceptions.AuthenticationFailed("用户认证失败") return ('登录用户', '用户token') 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.
"""
# 验证失败时,返回的响应头WWW-Authenticate对应的值
pass class TestView(APIView):
authentication_classes = [TestAuthentication, ]
permission_classes = [] def get(self, request, *args, **kwargs):
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

b. 请求头认证

from django.conf.urls import url, include
from web.viewsimport TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions token_list = [
'sfsfss123kuf3j123',
'asijnfowerkkf9812',
] class TestAuthentication(BaseAuthentication):
def authenticate(self, request):
"""
用户认证,如果验证成功后返回元组: (用户,用户Token)
:param request:
:return:
None,表示跳过该验证;
如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None
(user,token)表示验证通过并设置用户名和Token;
AuthenticationFailed异常
"""
import base64
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if auth:
auth = auth.encode('utf-8')
auth = auth.split()
if not auth or auth[0].lower() != b'basic':
raise exceptions.AuthenticationFailed('验证失败')
if len(auth) != 2:
raise exceptions.AuthenticationFailed('验证失败')
username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
if username == 'alex' and password == '':
return ('登录用户', '用户token')
else:
raise exceptions.AuthenticationFailed('用户名或密码错误') 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' class TestView(APIView):
authentication_classes = [TestAuthentication, ]
permission_classes = [] def get(self, request, *args, **kwargs):
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

c. 多个认证规则

from django.conf.urls import url, include
from web.views.s2_auth import TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions token_list = [
'sfsfss123kuf3j123',
'asijnfowerkkf9812',
] class Test1Authentication(BaseAuthentication):
def authenticate(self, request):
"""
用户认证,如果验证成功后返回元组: (用户,用户Token)
:param request:
:return:
None,表示跳过该验证;
如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
else:
self.auth = None
(user,token)表示验证通过并设置用户名和Token;
AuthenticationFailed异常
"""
import base64
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if auth:
auth = auth.encode('utf-8')
else:
return None
print(auth,'xxxx')
auth = auth.split()
if not auth or auth[0].lower() != b'basic':
raise exceptions.AuthenticationFailed('验证失败')
if len(auth) != 2:
raise exceptions.AuthenticationFailed('验证失败')
username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
if username == 'alex' and password == '':
return ('登录用户', '用户token')
else:
raise exceptions.AuthenticationFailed('用户名或密码错误') 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 Test2Authentication(BaseAuthentication):
def authenticate(self, request):
"""
用户认证,如果验证成功后返回元组: (用户,用户Token)
:param request:
:return:
None,表示跳过该验证;
如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
else:
self.auth = None
(user,token)表示验证通过并设置用户名和Token;
AuthenticationFailed异常
"""
val = request.query_params.get('token')
if val not in token_list:
raise exceptions.AuthenticationFailed("用户认证失败") return ('登录用户', '用户token') 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.
"""
pass class TestView(APIView):
authentication_classes = [Test1Authentication, Test2Authentication]
permission_classes = [] def get(self, request, *args, **kwargs):
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

d. 认证和权限

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission from rest_framework.request import Request
from rest_framework import exceptions token_list = [
'sfsfss123kuf3j123',
'asijnfowerkkf9812',
] class TestAuthentication(BaseAuthentication):
def authenticate(self, request):
"""
用户认证,如果验证成功后返回元组: (用户,用户Token)
:param request:
:return:
None,表示跳过该验证;
如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
else:
self.auth = None
(user,token)表示验证通过并设置用户名和Token;
AuthenticationFailed异常
"""
val = request.query_params.get('token')
if val not in token_list:
raise exceptions.AuthenticationFailed("用户认证失败") return ('登录用户', '用户token') 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.
"""
pass class TestPermission(BasePermission):
message = "权限验证失败" def has_permission(self, request, view):
"""
判断是否有权限访问当前请求
Return `True` if permission is granted, `False` otherwise.
:param request:
:param view:
:return: True有权限;False无权限
"""
if request.user == "管理员":
return True # GenericAPIView中get_object时调用
def has_object_permission(self, request, view, obj):
"""
视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证
Return `True` if permission is granted, `False` otherwise.
:param request:
:param view:
:param obj:
:return: True有权限;False无权限
"""
if request.user == "管理员":
return True class TestView(APIView):
# 认证的动作是由request.user触发
authentication_classes = [TestAuthentication, ] # 权限
# 循环执行所有的权限
permission_classes = [TestPermission, ] def get(self, request, *args, **kwargs):
# self.dispatch
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

e. 全局使用

上述操作中均是对单独视图进行特殊配置,如果想要对全局进行配置,则需要再配置文件中写入即可。

REST_FRAMEWORK = {
'UNAUTHENTICATED_USER': None,
'UNAUTHENTICATED_TOKEN': None,
"DEFAULT_AUTHENTICATION_CLASSES": [
"web.utils.TestAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"web.utils.TestPermission",
],
}

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response class TestView(APIView): def get(self, request, *args, **kwargs):
# self.dispatch
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

3. 用户访问次数/频率限制

a. 基于用户IP限制访问频率

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework import exceptions
from rest_framework.throttling import BaseThrottle
from rest_framework.settings import api_settings # 保存访问记录
RECORD = {
'用户IP': [12312139, 12312135, 12312133, ]
} class TestThrottle(BaseThrottle):
ctime = time.time def get_ident(self, request):
"""
根据用户IP和代理IP,当做请求者的唯一IP
Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
if present and number of proxies is > 0. If not use all of
HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
"""
xff = request.META.get('HTTP_X_FORWARDED_FOR')
remote_addr = request.META.get('REMOTE_ADDR')
num_proxies = api_settings.NUM_PROXIES if num_proxies is not None:
if num_proxies == 0 or xff is None:
return remote_addr
addrs = xff.split(',')
client_addr = addrs[-min(num_proxies, len(addrs))]
return client_addr.strip() return ''.join(xff.split()) if xff else remote_addr def allow_request(self, request, view):
"""
是否仍然在允许范围内
Return `True` if the request should be allowed, `False` otherwise.
:param request:
:param view:
:return: True,表示可以通过;False表示已超过限制,不允许访问
"""
# 获取用户唯一标识(如:IP) # 允许一分钟访问10次
num_request = 10
time_request = 60 now = self.ctime()
ident = self.get_ident(request)
self.ident = ident
if ident not in RECORD:
RECORD[ident] = [now, ]
return True
history = RECORD[ident]
while history and history[-1] <= now - time_request:
history.pop()
if len(history) < num_request:
history.insert(0, now)
return True def wait(self):
"""
多少秒后可以允许继续访问
Optionally, return a recommended number of seconds to wait before
the next request.
"""
last_time = RECORD[self.ident][0]
now = self.ctime()
return int(60 + last_time - now) class TestView(APIView):
throttle_classes = [TestThrottle, ] def get(self, request, *args, **kwargs):
# self.dispatch
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容') def throttled(self, request, wait):
"""
访问次数被限制时,定制错误信息
""" class Throttled(exceptions.Throttled):
default_detail = '请求被限制.'
extra_detail_singular = '请 {wait} 秒之后再重试.'
extra_detail_plural = '请 {wait} 秒之后再重试.' raise Throttled(wait)

views.py

b. 基于用户IP显示访问频率(利于Django缓存)

REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'test_scope': '10/m',
},
}

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework import exceptions
from rest_framework.throttling import SimpleRateThrottle class TestThrottle(SimpleRateThrottle): # 配置文件定义的显示频率的Key
scope = "test_scope" def get_cache_key(self, request, view):
"""
Should return a unique cache-key which can be used for throttling.
Must be overridden. May return `None` if the request should not be throttled.
"""
if not request.user:
ident = self.get_ident(request)
else:
ident = request.user return self.cache_format % {
'scope': self.scope,
'ident': ident
} class TestView(APIView):
throttle_classes = [TestThrottle, ] def get(self, request, *args, **kwargs):
# self.dispatch
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容') def throttled(self, request, wait):
"""
访问次数被限制时,定制错误信息
""" class Throttled(exceptions.Throttled):
default_detail = '请求被限制.'
extra_detail_singular = '请 {wait} 秒之后再重试.'
extra_detail_plural = '请 {wait} 秒之后再重试.' raise Throttled(wait)

views.py

c. view中限制请求频率

REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'xxxxxx': '10/m',
},
}

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework import exceptions
from rest_framework.throttling import ScopedRateThrottle # 继承 ScopedRateThrottle
class TestThrottle(ScopedRateThrottle): def get_cache_key(self, request, view):
"""
Should return a unique cache-key which can be used for throttling.
Must be overridden. May return `None` if the request should not be throttled.
"""
if not request.user:
ident = self.get_ident(request)
else:
ident = request.user return self.cache_format % {
'scope': self.scope,
'ident': ident
} class TestView(APIView):
throttle_classes = [TestThrottle, ] # 在settings中获取 xxxxxx 对应的频率限制值
throttle_scope = "xxxxxx" def get(self, request, *args, **kwargs):
# self.dispatch
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容') def throttled(self, request, wait):
"""
访问次数被限制时,定制错误信息
""" class Throttled(exceptions.Throttled):
default_detail = '请求被限制.'
extra_detail_singular = '请 {wait} 秒之后再重试.'
extra_detail_plural = '请 {wait} 秒之后再重试.' raise Throttled(wait)

views.py

d. 匿名时用IP限制+登录时用Token限制

REST_FRAMEWORK = {
'UNAUTHENTICATED_USER': None,
'UNAUTHENTICATED_TOKEN': None,
'DEFAULT_THROTTLE_RATES': {
'luffy_anon': '10/m',
'luffy_user': '20/m',
},
}

settings.py

from django.conf.urls import url, include
from web.views.s3_throttling import TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.throttling import SimpleRateThrottle class LuffyAnonRateThrottle(SimpleRateThrottle):
"""
匿名用户,根据IP进行限制
"""
scope = "luffy_anon" def get_cache_key(self, request, view):
# 用户已登录,则跳过 匿名频率限制
if request.user:
return None return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request)
} class LuffyUserRateThrottle(SimpleRateThrottle):
"""
登录用户,根据用户token限制
"""
scope = "luffy_user" def get_ident(self, request):
"""
认证成功时:request.user是用户对象;request.auth是token对象
:param request:
:return:
"""
# return request.auth.token
return "user_token" def get_cache_key(self, request, view):
"""
获取缓存key
:param request:
:param view:
:return:
"""
# 未登录用户,则跳过 Token限制
if not request.user:
return None return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request)
} class TestView(APIView):
throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ] def get(self, request, *args, **kwargs):
# self.dispatch
print(request.user)
print(request.auth)
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

e. 全局使用

REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'api.utils.throttles.throttles.LuffyAnonRateThrottle',
'api.utils.throttles.throttles.LuffyUserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'anon': '10/day',
'user': '10/day',
'luffy_anon': '10/m',
'luffy_user': '20/m',
},
}

settings

4. 版本

a. 基于url的get传参方式

如:/users?version=v1

REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(),name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning class TestView(APIView):
versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): # 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme) # 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

b. 基于url的正则方式

如:/v1/users/

REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning class TestView(APIView):
versioning_class = URLPathVersioning def get(self, request, *args, **kwargs):
# 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme) # 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

c. 基于 accept 请求头方式

如:Accept: application/json; version=1.0

REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import AcceptHeaderVersioning class TestView(APIView):
versioning_class = AcceptHeaderVersioning def get(self, request, *args, **kwargs):
# 获取版本 HTTP_ACCEPT头
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme)
# 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

d. 基于主机名方法

如:v1.example.com

ALLOWED_HOSTS = ['*']
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import HostNameVersioning class TestView(APIView):
versioning_class = HostNameVersioning def get(self, request, *args, **kwargs):
# 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme)
# 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

e. 基于django路由系统的namespace

如:example.com/v1/users/

REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^v1/', ([
url(r'test/', TestView.as_view(), name='test'),
], None, 'v1')),
url(r'^v2/', ([
url(r'test/', TestView.as_view(), name='test'),
], None, 'v2')), ]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import NamespaceVersioning class TestView(APIView):
versioning_class = NamespaceVersioning def get(self, request, *args, **kwargs):
# 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme)
# 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

f. 全局使用

REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1', 'v2'],
'VERSION_PARAM': 'version'
}

settings.py

5. 解析器(parser)

根据请求头 content-type 选择对应的解析器就请求体内容进行处理。

a. 仅处理请求头content-type为application/json的请求体

from django.conf.urls import url, include
from web.views.s5_parser import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser class TestView(APIView):
parser_classes = [JSONParser, ] def post(self, request, *args, **kwargs):
print(request.content_type) # 获取请求的值,并使用对应的JSONParser进行处理
print(request.data) # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES) return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

b. 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FormParser class TestView(APIView):
parser_classes = [FormParser, ] def post(self, request, *args, **kwargs):
print(request.content_type) # 获取请求的值,并使用对应的JSONParser进行处理
print(request.data) # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES) return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

c. 仅处理请求头content-type为multipart/form-data的请求体

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import MultiPartParser class TestView(APIView):
parser_classes = [MultiPartParser, ] def post(self, request, *args, **kwargs):
print(request.content_type) # 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data">
<input type="text" name="user" />
<input type="file" name="img"> <input type="submit" value="提交"> </form>
</body>
</html>

upload.html

d. 仅上传文件

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FileUploadParser class TestView(APIView):
parser_classes = [FileUploadParser, ] def post(self, request, filename, *args, **kwargs):
print(filename)
print(request.content_type) # 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data">
<input type="text" name="user" />
<input type="file" name="img"> <input type="submit" value="提交"> </form>
</body>
</html>

upload.html

e. 同时多个Parser

当同时使用多个parser时,rest framework会根据请求头content-type自动进行比对,并使用对应parser

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser class TestView(APIView):
parser_classes = [JSONParser, FormParser, MultiPartParser, ] def post(self, request, *args, **kwargs):
print(request.content_type) # 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

f. 全局使用

REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':[
'rest_framework.parsers.JSONParser'
'rest_framework.parsers.FormParser'
'rest_framework.parsers.MultiPartParser'
] }

settings.py

from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response class TestView(APIView):
def post(self, request, *args, **kwargs):
print(request.content_type) # 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

views.py

注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取

6. 序列化

序列化用于对用户请求数据进行验证和数据进行序列化。

a. 自定义字段

from django.conf.urls import url, include
from web.views.s6_serializers import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models class PasswordValidator(object):
def __init__(self, base):
self.base = base def __call__(self, value):
if value != self.base:
message = 'This field must be %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 UserSerializer(serializers.Serializer):
ut_title = serializers.CharField(source='ut.title')
user = serializers.CharField(min_length=6)
pwd = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('')]) class TestView(APIView):
def get(self, request, *args, **kwargs): # 序列化,将数据库查询字段序列化为字典
data_list = models.UserInfo.objects.all()
ser = UserSerializer(instance=data_list, many=True)
# 或
# obj = models.UserInfo.objects.all().first()
# ser = UserSerializer(instance=obj, many=False)
return Response(ser.data) def post(self, request, *args, **kwargs):
# 验证,对请求发来的数据进行验证
ser = UserSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors) return Response('POST请求,响应内容')

views.py

b. 基于Model自动生成字段

from django.conf.urls import url, include
from web.views.s6_serializers import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models class PasswordValidator(object):
def __init__(self, base):
self.base = str(base) def __call__(self, value):
if value != self.base:
message = 'This field must be %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 ModelUserSerializer(serializers.ModelSerializer): user = serializers.CharField(max_length=32) class Meta:
model = models.UserInfo
fields = "__all__"
# fields = ['user', 'pwd', 'ut']
depth = 2
extra_kwargs = {'user': {'min_length': 6}, 'pwd': {'validators': [PasswordValidator(666), ]}}
# read_only_fields = ['user'] class TestView(APIView):
def get(self, request, *args, **kwargs): # 序列化,将数据库查询字段序列化为字典
data_list = models.UserInfo.objects.all()
ser = ModelUserSerializer(instance=data_list, many=True)
# 或
# obj = models.UserInfo.objects.all().first()
# ser = UserSerializer(instance=obj, many=False)
return Response(ser.data) def post(self, request, *args, **kwargs):
# 验证,对请求发来的数据进行验证
print(request.data)
ser = ModelUserSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors) return Response('POST请求,响应内容')

views.py

c. 生成URL

from django.conf.urls import url, include
from web.views.s6_serializers import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
url(r'detail/(?P<pk>\d+)/', TestView.as_view(), name='detail'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models class PasswordValidator(object):
def __init__(self, base):
self.base = str(base) def __call__(self, value):
if value != self.base:
message = 'This field must be %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 ModelUserSerializer(serializers.ModelSerializer):
ut = serializers.HyperlinkedIdentityField(view_name='detail')
class Meta:
model = models.UserInfo
fields = "__all__" extra_kwargs = {
'user': {'min_length': 6},
'pwd': {'validators': [PasswordValidator(666),]},
} class TestView(APIView):
def get(self, request, *args, **kwargs): # 序列化,将数据库查询字段序列化为字典
data_list = models.UserInfo.objects.all()
ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
# 或
# obj = models.UserInfo.objects.all().first()
# ser = UserSerializer(instance=obj, many=False)
return Response(ser.data) def post(self, request, *args, **kwargs):
# 验证,对请求发来的数据进行验证
print(request.data)
ser = ModelUserSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors) return Response('POST请求,响应内容')

views.py

d. 自动生成URL

from django.conf.urls import url, include
from web.views.s6_serializers import TestView urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
url(r'detail/(?P<pk>\d+)/', TestView.as_view(), name='xxxx'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models class PasswordValidator(object):
def __init__(self, base):
self.base = str(base) def __call__(self, value):
if value != self.base:
message = 'This field must be %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 ModelUserSerializer(serializers.HyperlinkedModelSerializer):
ll = serializers.HyperlinkedIdentityField(view_name='xxxx')
tt = serializers.CharField(required=False) class Meta:
model = models.UserInfo
fields = "__all__"
list_serializer_class = serializers.ListSerializer extra_kwargs = {
'user': {'min_length': 6},
'pwd': {'validators': [PasswordValidator(666), ]},
'url': {'view_name': 'xxxx'},
'ut': {'view_name': 'xxxx'},
} class TestView(APIView):
def get(self, request, *args, **kwargs):
# # 序列化,将数据库查询字段序列化为字典
data_list = models.UserInfo.objects.all()
ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
# # 如果Many=True
# # 或
# # obj = models.UserInfo.objects.all().first()
# # ser = UserSerializer(instance=obj, many=False)
return Response(ser.data) def post(self, request, *args, **kwargs):
# 验证,对请求发来的数据进行验证
print(request.data)
ser = ModelUserSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors) return Response('POST请求,响应内容')

views.py

7. 分页

a. 根据页码进行分页

from django.conf.urls import url, include
from rest_framework import routers
from web.views import s9_pagination urlpatterns = [
url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

urs.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models from rest_framework.pagination import PageNumberPagination class StandardResultsSetPagination(PageNumberPagination):
# 默认每页显示的数据条数
page_size = 1
# 获取URL参数中设置的每页显示数据条数
page_size_query_param = 'page_size' # 获取URL参数中传入的页码key
page_query_param = 'page' # 最大支持的每页显示的数据条数
max_page_size = 1 class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class UserViewSet(APIView):
def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all().order_by('-id') # 实例化分页对象,获取数据库中的分页数据
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self) # 序列化对象
serializer = UserSerializer(page_user_list, many=True) # 生成分页和数据
response = paginator.get_paginated_response(serializer.data)
return response

views.py

b. 位置和个数进行分页

from django.conf.urls import url, include
from web.views import s9_pagination urlpatterns = [
url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination class StandardResultsSetPagination(LimitOffsetPagination):
# 默认每页显示的数据条数
default_limit = 10
# URL中传入的显示数据条数的参数
limit_query_param = 'limit'
# URL中传入的数据位置的参数
offset_query_param = 'offset'
# 最大每页显得条数
max_limit = None class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class UserViewSet(APIView):
def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all().order_by('-id') # 实例化分页对象,获取数据库中的分页数据
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self) # 序列化对象
serializer = UserSerializer(page_user_list, many=True) # 生成分页和数据
response = paginator.get_paginated_response(serializer.data)
return response

views.py

c. 游标分页

from django.conf.urls import url, include
from web.views import s9_pagination urlpatterns = [
url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination class StandardResultsSetPagination(CursorPagination):
# URL传入的游标参数
cursor_query_param = 'cursor'
# 默认每页显示的数据条数
page_size = 2
# URL传入的每页显示条数的参数
page_size_query_param = 'page_size'
# 每页显示数据最大条数
max_page_size = 1000 # 根据ID从大到小排列
ordering = "id" class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class UserViewSet(APIView):
def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all().order_by('-id') # 实例化分页对象,获取数据库中的分页数据
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self) # 序列化对象
serializer = UserSerializer(page_user_list, many=True) # 生成分页和数据
response = paginator.get_paginated_response(serializer.data)
return response

views.py

8. 路由系统

a. 自定义路由

from django.conf.urls import url, include
from web.views import s11_render urlpatterns = [
url(r'^test/$', s11_render.TestView.as_view()),
url(r'^test\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()),
url(r'^test/(?P<pk>[^/.]+)/$', s11_render.TestView.as_view()),
url(r'^test/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view())
]

urls.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .. import models class TestView(APIView):
def get(self, request, *args, **kwargs):
print(kwargs)
print(self.renderer_classes)
return Response('...')

views.py

b. 半自动路由

from django.conf.urls import url, include
from web.views import s10_generic urlpatterns = [
url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
url(r'^test/(?P<pk>\d+)/$', s10_generic.UserViewSet.as_view(
{'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class UserViewSet(ModelViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserSerializer

views.py

c. 全自动路由

from django.conf.urls import url, include
from rest_framework import routers
from web.views import s10_generic router = routers.DefaultRouter()
router.register(r'users', s10_generic.UserViewSet) urlpatterns = [
url(r'^', include(router.urls)),
]

urls.py

from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class UserViewSet(ModelViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserSerializer

views.py

9. 视图

a. GenericViewSet

from django.conf.urls import url, include
from web.views.s7_viewset import TestView urlpatterns = [
url(r'test/', TestView.as_view({'get':'list'}), name='test'),
url(r'detail/(?P<pk>\d+)/', TestView.as_view({'get':'list'}), name='xxxx'),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework import viewsets
from rest_framework.response import Response class TestView(viewsets.GenericViewSet):
def list(self, request, *args, **kwargs):
return Response('...') def add(self, request, *args, **kwargs):
pass def delete(self, request, *args, **kwargs):
pass def edit(self, request, *args, **kwargs):
pass

views.py

b. ModelViewSet(自定义URL)

from django.conf.urls import url, include
from web.views import s10_generic urlpatterns = [
url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
url(r'^test/(?P<pk>\d+)/$', s10_generic.UserViewSet.as_view(
{'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class UserViewSet(ModelViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserSerializer

views.py

c. ModelViewSet(rest framework路由)

from django.conf.urls import url, include
from rest_framework import routers
from app01 import views router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet) # Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
]

urls.py

from rest_framework import viewsets
from rest_framework import serializers class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.User
fields = ('url', 'username', 'email', 'groups') class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Group
fields = ('url', 'name') class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer class GroupViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Group.objects.all()
serializer_class = GroupSerializer

views.py

10. 渲染器

根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件。
用户请求URL:

  • http://127.0.0.1:8000/test/?format=json
  • http://127.0.0.1:8000/test.json

用户请求头:

  • Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

a. json

访问URL:

  • http://127.0.0.1:8000/test/?format=json
  • http://127.0.0.1:8000/test.json
  • http://127.0.0.1:8000/test/
from django.conf.urls import url, include
from web.views import s11_render urlpatterns = [
url(r'^test/$', s11_render.TestView.as_view()),
url(r'^test\.(?P<format>[a-z0-9]+)', s11_render.TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers from rest_framework.renderers import JSONRenderer from .. import models class TestSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class TestView(APIView):
renderer_classes = [JSONRenderer, ] def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all()
ser = TestSerializer(instance=user_list, many=True)
return Response(ser.data)

views.py

b. 表格

访问URL:

  • http://127.0.0.1:8000/test/?format=admin
  • http://127.0.0.1:8000/test.admin
  • http://127.0.0.1:8000/test/
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers from rest_framework.renderers import AdminRenderer from .. import models class TestSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class TestView(APIView):
renderer_classes = [AdminRenderer, ] def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all()
ser = TestSerializer(instance=user_list, many=True)
return Response(ser.data)

views.py

c. Form表单

访问URL:

  • http://127.0.0.1:8000/test/?format=form
  • http://127.0.0.1:8000/test.form
  • http://127.0.0.1:8000/test/
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers from rest_framework.renderers import JSONRenderer
from rest_framework.renderers import AdminRenderer
from rest_framework.renderers import HTMLFormRenderer from .. import models class TestSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class TestView(APIView):
renderer_classes = [HTMLFormRenderer, ] def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all().first()
ser = TestSerializer(instance=user_list, many=False)
return Response(ser.data)

views.py

d. 自定义显示模板

访问URL:

  • http://127.0.0.1:8000/test/?format=html
  • http://127.0.0.1:8000/test.html
  • http://127.0.0.1:8000/test/
from django.conf.urls import url, include
from web.views import s11_render urlpatterns = [
url(r'^test/$', s11_render.TestView.as_view()),
url(r'^test\.(?P<format>[a-z0-9]+)', s11_render.TestView.as_view()),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import TemplateHTMLRenderer from .. import models class TestSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class TestView(APIView):
renderer_classes = [TemplateHTMLRenderer, ] def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all().first()
ser = TestSerializer(instance=user_list, many=False)
return Response(ser.data, template_name='user_detail.html')

views.py

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ user }}
{{ pwd }}
{{ ut }}
</body>
</html>

userdetail.html

e. 浏览器格式API+JSON

访问URL:

  • http://127.0.0.1:8000/test/?format=api
  • http://127.0.0.1:8000/test.api
  • http://127.0.0.1:8000/test/
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers from rest_framework.renderers import JSONRenderer
from rest_framework.renderers import BrowsableAPIRenderer from .. import models class TestSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__" class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
def get_default_renderer(self, view):
return JSONRenderer() class TestView(APIView):
renderer_classes = [CustomBrowsableAPIRenderer, ] def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all().first()
ser = TestSerializer(instance=user_list, many=False)
return Response(ser.data, template_name='user_detail.html')

views.py

注意:如果同时多个存在时,自动根据URL后缀来选择渲染器。

url设计规范的更多相关文章

  1. SSM框架学习之高并发秒杀业务--笔记4-- web层

    在前面几节中已经完成了service层和dao层,到目前为止只是后端的设计与编写,这节就要设计到前端的设计了.下面开始总结下这个秒杀业务前端有哪些要点: 1. 前端页面的流程 首先是列表页,点某个商品 ...

  2. DRF(1) - REST、DRF(View源码解读、APIView源码解读)

    一.REST 1.什么是编程? 数据结构和算法的结合. 2.什么是REST? 首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下: /books/ /get_all_books/ 访问所 ...

  3. Restful接口设计

    URL设计规范:/模块/资源/{标示}/集合1/... eg: /user/{uid}/friends ->好友列表 例子:秒杀系统API设计 1.请求参数绑定:@PathVariable(&q ...

  4. Restful 1 -- REST、DRF(View源码解读、APIView源码解读)及框架实现

    一.REST 1.什么是编程? 数据结构和算法的结合 2.什么是REST? - url用来唯一定位资源,http请求方式来区分用户行为 首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下 ...

  5. 一文搞懂RESTful API

    RESTful接口实战 原创公众号:bigsai 转载请联系bigsai 文章收藏在回车课堂 前言 在学习RESTful 风格接口之前,即使你不知道它是什么,但你肯定会好奇它能解决什么问题?有什么应用 ...

  6. SQL Server数据库设计规范

    数据库设计规范 1.简介 数据库设计是指对一个给定的应用环境,构造最优的数据库模式,建立数据库及其他应用系统,使之能有效地存储数据,满足各种用户的需求.数据库设计过程中命名规范很是重要,命名规范合理的 ...

  7. SQL Server 数据库设计规范

    数据库设计规范 1.简介 数据库设计是指对一个给定的应用环境,构造最优的数据库模式,建立数据库及其他应用系统,使之能有效地存储数据,满足各种用户的需求.数据库设计过程中命名规范很是重要,命名规范合理的 ...

  8. Restful API设计规范及实战

    Restful API的概念在此就不费口舌了,博友们网上查哈定义文章很多,直入正题吧: 首先抛出一个问题:判断id为 用户下,名称为 使命召唤14(COD14) 的产品是否存在(话说我还是很喜欢玩类似 ...

  9. MongoDB数据库的设计规范

    MongoDB是非关系型数据库的典型代表,DB-Engines Ranking 数据显示,近年来,MongoDB在NoSQL领域一直独占鳌头.MongoDB是为快速开发互联网应用 而设计的数据库系统, ...

随机推荐

  1. Spring 两大核心 IOC 和 AOP

    如果你的简历上写着Spring (请详述一下spring的两大核心)这个问题一定会被问到. 一.什么叫IOC 1. IOC 全称(Inversion of Control)-- 控制反转. IOC 只 ...

  2. 关于ASP.NET中WEBAPI中POST请求中FromBody修饰的string类型的参数服务器端获取不到值FromBody空值的简单解决方法

    其实解决办法很简单,就是POST请求的时候,来自实体的参数,content-type:application/x-www-form-urlencoded情况下,是默认按照键值对来解析的,比如param ...

  3. 阿里云机器维护-gitlab Forbidden

    gitlab这台机子运行了一两年了,今天突然拉代码不能拉了,看了下接口403 登录网页 Forbidden 看了下是前两天挖矿病毒引发的,大致因为大量请求导致ip被封了 我们只要把这台机子加入配置白名 ...

  4. 编写一个函数来反转某个二进制型里的字节顺序(erlang)

    reverse_byte(<<>>) -> <<>>; reverse_byte(<<Header:8, Tail/bits>& ...

  5. springboot使用Jpa连接数据库

    springboot使用Jpa连接数据库 1.pom.xml: <?xml version="1.0" encoding="UTF-8"?> < ...

  6. 代码整洁 vs 代码肮脏

    写出整洁的代码,是每个程序员的追求.<clean code>指出,要想写出好的代码,首先得知道什么是肮脏代码.什么是整洁代码:然后通过大量的刻意练习,才能真正写出整洁的代码. WTF/mi ...

  7. FreeSql (八)插入数据时指定列

    插入数据时指定列,和忽略列对应,未被指定的列将被忽略. var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Passwor ...

  8. UGUI_创建旋转物体,使用Slider控制小球旋转速度

    using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : ...

  9. TestNG(一) TestNG实战在idea中创建module

    1.在ider里创建一个Module 2.直接点击下一步 3.输入Groupld h和Artifactid名称,点击下一步 4.点击Finish 创建完成

  10. CentOS7下LVM的基本操作

    CentOS7下LVM的基本操作-创建LVM 环境 物理主机:windows10 虚拟软件:VMWare14 虚拟机:CentOS Linux release 7.6.1810 (Core) 软件环境 ...