https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
https://example.org/api/ API很简单
例如写一个查询所有图书的api接口:
https://api.example.com/books
https://127.0.0.1/api/books
URL,如:https://api.example.com/v1/
https://127.0.0.1/api/v2/books(推荐用这种)
请求头 跨域时,引发发送多次请求
https://api.example.com/v1/books
https://api.example.com/v1/animals
https://api.example.com/v1/employees
不能这么写:
-获取所有图书:https://127.0.0.1/api/get_all_books
-新增一本书:https://127.0.0.1/api/add_book
同一都用这个:获取通过get请求,新增通过post请求
https://api.example.com/v1/books
GET :从服务器取出资源(一项或多项)
POST :在服务器新建一个资源
PUT :在服务器更新资源(客户端提供改变后的完整资源)
PATCH :在服务器更新资源(客户端提供改变的属性)
DELETE:从服务器删除资源
https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
请求回去,需要有状态码,也可以自定义状态码
OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
NO CONTENT - [DELETE]:用户删除数据成功。
INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
应返回错误信息,error当做key.
{status:100,error:'错误信息写上'}
针对不同操作,服务器向用户返回的结果应该符合以下规范:
GET /books:返回资源对象的列表(数组)
GET /books/1:返回单个资源对象
POST /books:返回新生成的资源对象 -新增,传数据,一旦新增完成,把新的资源对象返回
PUT /books/1:返回完整的资源对象
PATCH /books/1:返回完整的资源对象
DELETE /books/1:返回一个空文档
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API
{
status:100
msg:成功
url:127.0.0.1/books/1
}
核心:返回结果中提供链接
pip install djangorestframework
restframework是一个app,需要在settings.py引入该app
INSTALLED_APPS = [
'''
'rest_framework',
]
在之前学习的django里对book表的增删改查可能会这样写
----book表增删改查
/books/ 获取图书列表
/books/add/ 添加图书信息
/books/(\d+)/change/ 修改图书信息
/books/(\d+)/delete/ 删除图书信息
按照resful规范,应该把图书信息看作是一种资源,url里应当全部是名词,操作类型应该根据请求方式定义。
----book表增删改查
/books/ -----get 获取图书列表 ----- 返回当前所有数据
/books/ -----post 添加图书信息 ----- 返回提交数据
/books/(\d+)-----get 获取单个图书信息 ----- 返回当前查看的单条数据
/books/(\d+)-----put 修改单个图书信息 ----- 返回更新数据
/books/(\d+)-----delete 删除单个图书信息 ----- 返回空
为了实现resful规范,就只能使用CBV模式实现视图函数。
django自带的CBV视图
urls.py
urlpatterns = [
'''
path('url1/', Url1View.as_view(), name="url1"),
]
views.py
from django.views import View
class Url1View(View):
def get(self, request):
pass
def post(self, request):
pass
django自带的CBV模式请求流程
首先用户的请求先到urls.py匹配视图函数,例如用户请求的是/url1/的话,会先执行Url1View.as_view()方法。Url1View类并没有.as_view()方法,在它的父类View中有.as_view()方法。View.as_view()源码是这样写的
@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
可以看到返回值是view函数对象 。在我们使用FBV模式的时候,url.py文件中是这样配置url的
from app import views
urlpatterns = [
'''
path('url1/', views.Url1), name="url1"),
]
url后指向的是一个函数对象,使用django自带的CBV模式在这里是一样的,url指向的是一个函数对象,这个函数对象是View.as_view()中定义的.view()方法。所以接收到用户请求,就会执行.view()方法。可以看到.view()方法返回的是self.dispatch(request, *args, **kwargs)。Url1View类中没有.dispatch()这个方法,最后在View类中找到了这个方法,先看这个方法的源码
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
# http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
可以看到.dispatch()是根据request请求的方式,找到与请求方式同名的views函数并返回执行结果,如果找不到则报405的错误提示。
总体过程如图
以上就是django自带CBV模式执行流程。
restframework的CBV模式的执行过程
restframework的的视图函数要继承rest_framework.views.APIView类,与自带的CBV中不同的是执行的.as_view()方法是APIView类中的,执行的.dispatch()方法是APIView类中的。APIView类重写了这个流程中的.as_view()、.dispatch()方法。
下面看APIView.as_view()源码。
@classmethod
def as_view(cls, **initkwargs):
"""
Store the original class on the view function.
This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation.
"""
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
return csrf_exempt(view)
可以看到返回的依然是View类中的.as_view()执行的结果,即View.as_view()中定义的view()函数对象。前面已经说过了.view()函数,它的返回值就是.dispatch()的返回值。
再来看APIView.dispatch()的源码
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
其他的都和View.dispatch()差不多,下面看这两行
request = self.initialize_request(request, *args, **kwargs)
self.request = request
先看一下initialize_request()的源码
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
这里就是把request封装到成了新的request对象。并重新赋给self.request,传到执行的视图函数中。
所以在视图函数中从request中取参数的方式就变了
request.data 是个方法,包装成了属性,前台传过来body体中数据的数据,放在里面
request.query_params 这个是原来GET中的数据
request._request 是原来的request对象
也可以用request.POST、request.GET取数据
总结起来就是APIView类重写了View类的.as_view()和.dispatch()方法,并重新定义了request对象。