热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

restful规范与drf的安装使用&drf中APIView源码分析

内容概要web开发模式API接口postman测试软件的使用restful规范drf的安装与使用cbv的View源码分析APIView源码分析

restful 规范与 drf 的安装使用 & drf 中 APIView 源码分析

内容概要
  • web 开发模式
  • API 接口
  • postman 测试软件的使用
  • restful 规范
  • drf 的安装与使用
  • cbv 的 View 源码分析
  • APIView 源码分析
  • drf 的 Request 类
  • drf 的 APIView 类执行过程

内容详细

web 开发模式

1、前后端不分离

在开发一个网站的过程中,前端页面需要使用后端框架的模板语法(DTL) 来渲染,比如 Django 自带的模板语法或者 jinjia2,这种前端页面在后端渲染完成之后才会把页面文档传送给前端

image

2、前后端分离

目前主流的开发模式,这种模式中前端先在后端的静态文件服务器(nfs)中获取静态文件(static 中的HTML、CSS、JS代码),需要数据的时候向后端发送请求只获取json格式的数据,再在前端运用js的BOM与DOM操作对页面进行渲染。

这样便实现了前后端分离,前端只需要从后端获取一定格式的数据,那么前端就可以是一个网页、app或者小程序,提高开发水平。

image

API 接口

简介: api 接口是前后端交互数据的媒介

api 接口包含:

  • 1、url 地址: 向后端请求数据的地址
    • https://api.example.com
  • 2、请求方式 : get、post、put、patch、delete
  • 3、请求参数:在 url 地址后面紧跟着的 ?name="elijah",可以看作是过滤条件
    • https://api.example.com/v1/zoos?limit=10
  • 4、响应结果:包括响应首行(响应状态码)、响应头、响应体(json格式数据)

postman 测试软件的使用

过去我们开发网站开放出来的接口一般使用浏览器发送get请求来进行测试,但基于api接口的restful设计规范,我们还需要使用 post请求、put请求等,而浏览器只能发送 get请求

postman 是一种测试接口共组,在 postman 中可以满足我们的测试需求

市面上也有许多测试软件(postwoman),我们使用主流的postman

下载地址https://www.getpostman.com/downloads/

使用:

1、发送请求

image

前后端数据交互的编码格式:

  • urlencoded:正常的post请求提交数据:name=elijah
  • formdata:post请求上传文件:带文件二进制形式
  • json:在body体中的数据格式为:{"name":"elijah","age":18} (用的多)

发起请求时,可以在 body 中书写请求体:

image

image

2、建立集合批量发起测试

image

image

image

同时,这个批量测试也支持导出文件,然后发送给别人,再从文件中导出

image

restful 规范

REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。它首次出现在2000年Roy Fielding的博士论文中。

在前后端分离的开发模式中,为了方便数据交互,我们在设计前后端进行数据交互的 api 接口时,需要符合 restful 规范,它是一种写前后端分离的标准

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

10条 restful 规范:

1、通信协议一般使用 https 协议,为保障数据安全

2、设计的接口地址是可以让人一眼看出来是 api 接口(在接口中带api字眼)

  • https://api.baidu.com/books
  • https://127.0.0.1:8080/api/books

3、多数据版本共存

接口更新之后,老版本的接口依旧一起使用,等过了期限才弃用老版本(维护老版本接口),以便可以留存更多用户。

  • https://api.baidu.com/v1/login
  • https://api.baidu.com/v2/login

4、交互数据即是资源,使用名词(可以是复数)

  • https://api.baidu.com/v1/users
  • https://api.example.com/v1/animals

不建议使用动词,如 get_resource ,除非是 login 或者 register 这种接口

  • https://api.baidu.com/v2/login

5、用不同的请求方式表示不同的资源操作

  • get: 获取资源
  • post: 新增资源
  • put: 修改资源(全局修改)
  • patch: 修改资源(局部修改)
  • delete: 删除资源

6、过滤,通过在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:指定筛选条件

7、响应状态码(http的响应状态码,响应体的json数据中带状态码)

http 协议的状态码:

更多状态码请参考: https://www.cnblogs.com/elijah-li/p/16069638.html

1xx:请求正在处理
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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

更多状态码请参考: https://www.cnblogs.com/elijah-li/p/16069638.html

服务端自定义的状态码

响应体中返回json格式数据,数据中有 服务端自定义的状态码(code,status)

  • 1001 :用户名错误
  • 1002 :没有权限

8、返回错误信息(错误处理)

返回数据是json格式,带状态码和错误信息

{
    code: 1002
    error: "Invalid API key"
}

9、不同的请求方式(不同操作)应该返回不同的资源

  • get:/collection/resources 获取多个资源 --》 返回资源对象的列表 [{name:elijah,age:18},{name:pyy,age:33}]
  • get /collection/resource 返回单个资源对象 {name:elijah,age:18}
  • post: 新增资源 --》 返回新增的资源对象 {name:elijah,age:18}
  • put: 修改资源(全局修改) --》 返回修改的资源对象 {name:elijah,age:18}
  • patch: 修改资源(局部修改) --》 返回修改的资源对象 {name:elijah,age:18}
  • delete: 删除资源 --》 返回空文档(或者错误文档) {code:100,msg:删除成功}

10、返回资源中要跳转的链接地址

{"link": {
  "rel":   "collection https://www.example.com/zoos",
  "href":  "https://api.example.com/zoos",   """连接地址"""
  "title": "List of zoos",
  "type":  "application/vnd.yourformat+json"
}}

drf 的安装与使用

1、安装 djangorestframework

pip install djangorestframework==3.10.3

2、在 settings.py 文件中 注册 rest_framework

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "app01.apps.App01Config",
    "rest_framework",
]

3、在 models.py 中写表模型

class Book(models.Model):
    name = models.CharField(verbose_name="书名", max_length=32)
    price = models.DecimalField(verbose_name="价格", max_digits=5, decimal_places=2)
    author = models.CharField(verbose_name="作者", max_length=32)

    class Meta:
        verbose_name_plural = "书籍表"

4、新建一个序列化类

在应用文件夹下创建一个 ser.py 作为序列化模块

from rest_framework.serializers import ModelSerializer
from app01.models import Book


class BookModelSerializer(ModelSerializer):  # 继承的是 ModelSerializer
    class Meta:
        model = Book
        fields = "__all__"  # 注意别漏写 fields

5、书写视图函数(CBV)

from rest_framework.viewsets import ModelViewSet
from .ser import BookModelSerializer, Book


# Create your views here.


class BookModelViewSet(ModelViewSet):  # 继承的是 ModelViewSet
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

6、书写 url

from app01 import views
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register("books", views.BookModelViewSet)


urlpatterns = [
    path("admin/", admin.site.urls),
]
urlpatterns += router.urls

9、迁移数据库

python manage.py makemigrations
python manage.py migrate

10、在浏览器中测试接口

image

11、在 postman 中测试接口

注意: 在发送 put 请求(修改资源)、patch 请求(修改资源)、delete 请求(删除资源)时,得在名词后面添加具体的资源 id 值,并且要加上 “/” ,因为 postman 不会自动加斜杆

获取资源

image

新增资源:

image

修改资源:

image

删除资源:

image

cbv 的 View 源码分析

从路由文件 urls.py 的调用语句中看起

urlpatterns = [
    path("CBV/", views.MyClass.as_view()),
]
# as_view() 函数加括号优先调用 as_view函数

按住 ctrl 键,鼠标点击查看 as_view 源码

@classonlymethod
def as_view(cls, **initkwargs):
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)		# self 是我们自定义类产生的对象
        if hasattr(self, "get") and not hasattr(self, "head"):
            self.head = self.get
        self.setup(request, *args, **kwargs)
        if not hasattr(self, "request"):	# 自定义类必须接收 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 类中的dispath函数
    # ... 中间省略部分源码
    return view		# 返回view函数对象

从源码中可以看出,as_view 调用之后得到的是 view 函数对象,这是个闭包函数,当视图函数被触发,调用的是 view函数,view函数return一个 dispatch 函数调用结果

我们再查看 dispatch 的源码:

http_method_names = ["get", "post", "put", "patch", "delete", "head", "options", "trace"]


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:	# 如果是request中的属性,则用反射调用自身的响应的函数
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)

APIView 源码分析

APIView 是 rest_framework.views 中的一个类,是 drf 提供给我们更多功能的视图函数类,也继承了 Django 的 View

基于 CBV 先定义一个视图函数

views.py :

from rest_framework.views import APIView

class BookAPIView(APIView):
    def get(self, request):
        pass

    def post(self, request):
        pass

urls.py :

urlpatterns = [
    path("admin/", admin.site.urls),
    path("book/",views.BookAPIView.as_view())  # 调用 as_view() 返回的是 view 函数的内存地址
]

调用的是 APIView 的 as_view 方法:

APIView 类继承的是 Django 的 View,所以 super().as_view(**initkwargs) 调用了父类(View)中的 as_view,返回的是 View 中 as_view 返回的 view,在 view 函数里调用的 self.dispatch(request, *args, kwargs) 根据类的查找顺序,调用的就是 APIView 中的 dispatch

class APIView(View):
    @classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs
        return csrf_exempt(view)  # 所有继承了 APIView 的视图函数都忽略了 csrf 认证
  • 调用了 APIView 的 dispatch方法
  • 忽略了视图函数的 csrf 认证
csrf_exempt(index) 相当于
@csrf_exempt
def index(request)
	pass
# index=csrf_exempt(index)

APIView 中的 dispatch:

    def dispatch(self, request, *args, **kwargs):
        # 重写了 Django 传过来的 request 请求数据对象,给request增添了新属性
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request

        try:
            # 进行三大认证
            self.initial(request, *args, **kwargs)
            # 反射机制调用视图函数类中的方法,与原生 dispatch 一致
            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)

        # 处理放回数据,具有 drf 特色的页面
        self.respOnse= self.finalize_response(request, response, *args, **kwargs)
        return self.response

APIView 中的 initialize_request:

对原生的 request 进行处理,新增了一些功能和属性

    def initialize_request(self, request, *args, **kwargs):
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_cOntext=parser_context
        )

initialize_request 中的 Request():

把原生的 request 赋给了 _request,原 request 请求对象的方法在 _request 中获取就可以

class Request:
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_cOntext=None):

        self._request = request
        self._data = Empty
request 的变化

新的:
老的:

三大认证:

# 三大认证人如何走的
self.initial(request, *args, **kwargs)---》APIView的
核心代码:
   self.perform_authentication(request)  # 认证
   self.check_permissions(request)   #权限
   self.check_throttles(request) # 频率

drf 的 Request 类

重写了 request 请求数据对象之后,原生的 request 方法一样能用,因为 Request 类中的 getattr 方法(对象的 "." 点拦截)中用放射机制获取了原生 request 的所有属性

Request --> __getattr__

    def __getattr__(self, attr):
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

前端post请求传入的数据,在原来的request.POST中只能处理urlencoded和formdata编码格式,json格式不能处理。

而 Request 类实例化之后,提供了一个 data 方法,无论前端用什么编码post提交的数据,都从 request.data 中获取。

    @property
    def data(self):
        if not _hasattr(self, "_full_data"):
            self._load_data_and_files()
        return self._full_data

推荐阅读
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • 函子(Functor)是函数式编程中的一个重要概念,它不仅是一个特殊的容器,还提供了一种优雅的方式来处理值和函数。本文将详细介绍函子的基本概念及其在函数式编程中的应用,包括如何通过函子控制副作用、处理异常以及进行异步操作。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • Django与Python及其他Web框架的对比
    本文详细介绍了Django与其他Python Web框架(如Flask和Tornado)的区别,并探讨了Django的基本使用方法及与其他语言(如PHP)的比较。 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • Requests库的基本使用方法
    本文介绍了Python中Requests库的基础用法,包括如何安装、GET和POST请求的实现、如何处理Cookies和Headers,以及如何解析JSON响应。相比urllib库,Requests库提供了更为简洁高效的接口来处理HTTP请求。 ... [详细]
  • OBS Studio自动化实践:利用脚本批量生成录制场景
    本文探讨了如何利用OBS Studio进行高效录屏,并通过脚本实现场景的自动生成。适合对自动化办公感兴趣的读者。 ... [详细]
  • 入门指南:使用FastRPC技术连接Qualcomm Hexagon DSP
    本文旨在为初学者提供关于如何使用FastRPC技术连接Qualcomm Hexagon DSP的基础知识。FastRPC技术允许开发者在本地客户端实现远程调用,从而简化Hexagon DSP的开发和调试过程。 ... [详细]
  • 本文探讨了在一个物理隔离的环境中构建数据交换平台所面临的挑战,包括但不限于数据加密、传输监控及确保文件交换的安全性和可靠性。同时,作者结合自身项目经验,分享了项目规划、实施过程中的关键决策及其背后的思考。 ... [详细]
  • 本文介绍了一种方法,通过使用Python的ctypes库来调用C++代码。具体实例为实现一个简单的加法器,并详细说明了从编写C++代码到编译及最终在Python中调用的全过程。 ... [详细]
  • 小编给大家分享一下Vue3中如何提高开发效率,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获, ... [详细]
  • linux网络子系统分析(二)—— 协议栈分层框架的建立
    目录一、综述二、INET的初始化2.1INET接口注册2.2抽象实体的建立2.3代码细节分析2.3.1socket参数三、其他协议3.1PF_PACKET3.2P ... [详细]
  • 深入理解Java SE 8新特性:Lambda表达式与函数式编程
    本文作为‘Java SE 8新特性概览’系列的一部分,将详细探讨Lambda表达式。通过多种示例,我们将展示Lambda表达式的不同应用场景,并解释编译器如何处理这些表达式。 ... [详细]
  • 流处理中的计数挑战与解决方案
    本文探讨了在流处理中进行计数的各种技术和挑战,并基于作者在2016年圣何塞举行的Hadoop World大会上的演讲进行了深入分析。文章不仅介绍了传统批处理和Lambda架构的局限性,还详细探讨了流处理架构的优势及其在现代大数据应用中的重要作用。 ... [详细]
author-avatar
Viola大人
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有