cbv+resful+APIView源码分析

CBV源码分析

1概念:什么是cbv和fbv 已经什么是API

class bass View ---基于类的视图
function bass View ---基于函数的视图

API(Application Programming Interface)
API 就是应用程序编程接口。它是能用来操作组件、应用程序或者操作系统的一组函数


2什么是幂等性

现如今我们的系统大多拆分为分布式SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用RPC通信或者restful,既然是通信,那么就有可能再服务器处理完毕后返回结果的时候挂掉,这个时候用户端发现很久没有反应,那么就会多次点击按钮,这样请求有多次,那么处理数据的结果是否要统一呢?那是肯定的!尤其再支付场景。

 

幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。举个最简单的例子,那就是支付,用户购买商品使用约支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...

 

在以前的单应用系统中,我们只需要把数据操作放入事务中即可,发生错误立即回滚,但是再响应客户端的时候也有可能出现网络中断或者异常等等。

 

在增删改查4个操作中,尤为注意就是增加或者修改,

查询对于结果是不会有改变的,

删除只会进行一次,用户多次点击产生的结果一样

修改在大多场景下结果一样

增加在重复提交的场景下会出现

 

那么如何设计接口才能做到幂等呢?

方法一、单次支付请求,也就是直接支付了,不需要额外的数据库操作了,这个时候发起异步请求创建一个唯一的ticketId,就是门票,这张门票只能使用一次就作废,具体步骤如下:

异步请求获取门票

调用支付,传入门票

根据门票ID查询此次操作是否存在,如果存在则表示该操作已经执行过,直接返回结果;如果不存在,支付扣款,保存结果

返回结果到客户端

如果步骤4通信失败,用户再次发起请求,那么最终结果还是一样的

 

方法二、分布式环境下各个服务相互调用

这边就要举例我们的系统了,我们支付的时候先要扣款,然后更新订单,这个地方就涉及到了订单服务以及支付服务了。

用户调用支付,扣款成功后,更新对应订单状态,然后再保存流水。

而在这个地方就没必要使用门票ticketId了,因为会比较闲的麻烦

(支付状态:未支付,已支付)

步骤:

1、查询订单支付状态

2、如果已经支付,直接返回结果

3、如果未支付,则支付扣款并且保存流水

4、返回支付结果

如果步骤4通信失败,用户再次发起请求,那么最终结果还是一样的

对于做过支付的朋友,幂等,也可以称之为冲正,保证客户端与服务端的交易一致性,避免多次扣款。

 

最后来看一下我们的订单流程,虽然不是很复杂,但是最后在支付环境是一定要实现幂等性的



 
什么是幂等性

用户对同一操作发起的一起请求和多次请求的结果一致,不回应为多次操作而产生副作用

3如何阅读源码

左侧工程栏--->设置图标-->点击--->show members(能看到py文件,类的方法)

4源码分析如下:

 

-def as_view 类方法
    -def view:类方法内部,闭包函数定义:内层函数包含对外部作用域的引用
    -python中一切皆对象:函数也是对象
    -hasattr(self, 'get')--判断self类中是不是有该(get)方法  
    -反射 setattr(self,get,get_all):相当于把get函数,变成了get_all 
    -getattr(self, 'get'):拿到get函数的内存地址
        - def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            #执行:dispatch:谁的dispatch方法?写的cbv的那个c,视图中的那个视图类
            #我这个类如果没有写dispatch,会执行View中的dispatch方法
            return self.dispatch(request, *args, **kwargs)
        -def dispatch(self, request, *args, **kwargs):
            #request.method 前台请求的方法,转成了小写
            #http_method_names View中定义的一个列表:是一堆请求方式
            if request.method.lower() in self.http_method_names:
                #getattr的第三个参数是默认值:self.http_method_not_allowed
                #拿到get方法的内存地址
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            #get(request,*args, **kwargs)
            return handler(request, *args, **kwargs)
            
        -def dispatch(self, request, *args, **kwargs):
            #request.method 前台请求的方法,转成了小写
            #http_method_names View中定义的一个列表:是一堆请求方式
            if request.method.lower() in self.http_method_names:
                #getattr的第三个参数是默认值:self.http_method_not_allowed
                #拿到get方法的内存地址
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            #get(request,*args, **kwargs)
            return handler(request, *args, **kwargs)
cbv源码分析详情在这儿
    总结:*******请求来了--->as_view---->view---->dispatch--->分发到不同的函数,执行函数,拿到结果,然后返回

 resful规范

1什么是resful

REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”

REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态

所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性

对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

简而言之就是

1是一个规范
2面向资源编程:把网络中所有东西,想象成资源

2 resfuk API 设计(resful的十条规范)

  • API与用户的通信协议,总是使用HTTPs协议
  • -域名
    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
    同一都用这个:
    https://api.example.com/v1/books

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

  • 过滤,通过在url上传参的形式传递搜索条件
    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:返回一个空文档
  • Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API 方法,使得用户不查文档,也知道下一步应该做什么。
    {
    status:100
    msg:成功
    url:127.0.0.1/books/1
    }
    核心:返回结果中提供链接

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

3基于Django写resful规范的接口

路由系统

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # as_view一定要加括号,as_view()哪里来的?从View中继承过来的
    # as_view用类来调用的,它是一个类方法
    # 猜:as_view这个方法执行完成以后,应该是个函数的内存地址
    # 如果是get请求,会响应到类内部,执行get方法post请求,一样
    # as_view 类方法,自动把自己传过来
    url(r'^login/', views.Test.as_view()),
    url(r'^users/$', views.Users.as_view()),
    url(r'^users2/$', views.user2),
 url(r'^books/(?P<pk>d+)', views.Books.as_view()), ]

视图函数

import json

def  user2(request):
    if request.method=='GET':
        dic = {'status':200,'name': 'lqz2', 'age': 18}
        return HttpResponse(json.dumps(dic))
    elif request.method=='POST':
        dic = {'status': 200, 'msg': '修改成功'}
        return JsonResponse(dic)

class Users(View):
    def get(self, request):
        dic = {'status':200,'name': 'lqz', 'age': 18}
        return HttpResponse(json.dumps(dic))

    def post(self, request):
        dic = {'status': 200, 'msg': '修改成功'}
        return JsonResponse(dic)

rest-framework之APIView 

一:安装djangorestframework

方式一:pip3 install djangorestframework

方式二:pycharm图形化界面安装

方式三:pycharm命令行下安装(装在当前工程所用的解释器下)

2djangorestframework的APIView分析

@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)

as_view方法
as_view
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

dispatch
dispatch
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
        )

initialize_request
initialize_request
def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

initial方法(内部调用认证,权限和频率)
init方法(内部调用,权限和频率)

3djangorestframework的Request对象简单介绍

原文地址:https://www.cnblogs.com/ouyang99-/p/10102598.html