rest_framework学习(一)resful规范介绍及Django的CBV介绍

一、什么是resful规范

resful是一种规范,它是一种面向资源编程的规范,操作只是一种请求方式。

二、规范内容

1.API与用户的通信协议,总是使用HTTPs协议:https比http安全

2.域名

https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
https://example.org/api/ API很简单
例如写一个查询所有图书的api接口:
https://api.example.com/books
https://127.0.0.1/api/books

3.版本:每个接口都应该有版本

URL,如:https://api.example.com/v1/
https://127.0.0.1/api/v2/books(推荐用这种)
请求头 跨域时,引发发送多次请求

4.路径,视网络上任何东西都是资源,均使用名词表示(可复数)

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

5.method

GET :从服务器取出资源(一项或多项)
POST :在服务器新建一个资源
PUT :在服务器更新资源(客户端提供改变后的完整资源)
PATCH :在服务器更新资源(客户端提供改变的属性)
DELETE:从服务器删除资源

6.过滤,通过在url上传参的形式传递搜索条件

https://api.example.com/v1/zoos?limit=10:指定返回记录的数量

7.状态码

请求回去,需要有状态码,也可以自定义状态码
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

8.错误处理

应返回错误信息,error当做key.
{status:100,error:'错误信息写上'}

9.返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范:
GET /books:返回资源对象的列表(数组)
GET /books/1:返回单个资源对象
POST /books:返回新生成的资源对象 -新增,传数据,一旦新增完成,把新的资源对象返回
PUT /books/1:返回完整的资源对象
PATCH /books/1:返回完整的资源对象
DELETE /books/1:返回一个空文档

10.Hypermedia API

RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API
{
        status:100
        msg:成功
        url:127.0.0.1/books/1
}
核心:返回结果中提供链接

三、django rest framework

1、安装

pip install djangorestframework

2、使用

restframework是一个app,需要在settings.py引入该app

INSTALLED_APPS = [
    '''
    'rest_framework',
]

3、restframework和原始django对比

在之前学习的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模式实现视图函数。

CBV(Class Base View)

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对象。

posted @ 2019-05-20 16:41  Wuliwawa  阅读(149)  评论(0编辑  收藏  举报