RESTful规范

一 Web开发模式

在开发web应用中,有两种应用模式:

1.1 前后端不分离

模板在后端渲染完成后,以字符串形式封装到html中返回。render函数,打开模板文件,渲染完成,把字符串内容放到HttpResponse对象中返回,前端看到页面效果。每来一次请求,都要在服务端用模板渲染,返回html数据,对服务器压力很大-->发展为前后端分离。


1.2 前后端分离

浏览器向静态文件服务器发请求(跟django框架无关),拿回一个静态页面;浏览器运行js代码,发送ajax请求到django后端,后端处理逻辑,操作数据库,返回json或xml格式数据,浏览器把数据渲染到静态页面中。


1.3 伪静态页面与页面静态化

“伪静态”顾名思义就是一种表面上看似是静态网页(以.html、.htm等结尾),其实是动态网页,存在数据交互的网站,具有这种特性的网页被称为“伪静态网页”。我们看到的伪静态网页其实是经过处理的,将动态网页的id等参数通过URL重写来隐藏,让查看者以为是静态网页。
伪静态页面有两个好处,首先是对搜索引擎友好,伪静态URL可以当作URL来使,同样内容的页面,在搜索引擎来看,静态地址要比动态地址权重高,因为搜索引擎认为动态地址不稳定,而且容易陷入链接的死循环,动态页面是搜索引擎优化的大忌。其次,伪静态页面可以将页面地址伪装成很短的地址,便于访问者识别。

页面静态化,把一个动态页面的数据提前处理好,请求来时返回处理后的静态页面,面对高并发时,不用每次请求都操作数据库。比如网站首页,访问量大,每次请求都操作数据库,会给数据库带来巨大压力,网站首页的数据不会经常变动,因此可以把这个页面(接口)做成静态页面。

 

二 API接口

为了在团队内部形成共识,防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。

通过网络,规定了前后端信息交互规则的url链接,也就是前后端信息交互的媒介。

Web API接口和一般的url连接还是有区别的,Web API接口有以下四大特点:

1、url:长得像返回数据的url链接    如:百度地图接口 https://api.map.baidu.com/place/v2/search

2、请求方式:get、post、put、patch、delete

3、请求参数:json或xml格式的key-value类型数据

  上方百度地图接口,在url?拼接参数

  https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295&region=上海&query=肯德基&output=xml

  ak:6E823f587c95f0148c19993539b99295   token验证,对数据+签名的串又做了一个base64的加密,不能直接展示用户数据

  region:上海

  query:肯德基

  output:json   决定了响应数据的格式

4、响应结果:json或xml格式的数据

 

三 Postman的使用

postman是目前最好用的,模拟发送http请求的工具,可以作为接口调试工具。双击安装,安装完成自动打开。

除了创建单个测试地址,还可以创建批量测试的连接。

      

 

四 Restful规范

REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征性状态转移)。 它首次出现在2000年Roy Fielding的博士论文中。

RESTful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中。

这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。事实上,我们可以使用任何一个框架都可以实现符合restful规范的API接口。

RESTful 10条规范

1 数据的安全保障:url链接一般都采用https协议进行传输,采用https协议,可以提高数据交互过程中的安全性。

2 接口特征表现,一看就知道是个api接口
    - 用api关键字标识接口url:
    - https://api.baidu.com      放域名里
    - https://www.baidu.com/api  拼在路径后
注:看到api字眼,就代表该请求url链接是完成前后台数据交互的
    -路飞的接口:https://api.luffycity.com/api/v1/course/free/
    
3 多数据版本共存
    - 在url链接中标识数据版本
    - https://api.baidu.com/v1
    - https://api.baidu.com/v2
    注:url链接中的v1、v2就是不同数据版本的体现(只有在一种数据资源有多版本情况下)
    服务端升级版本不能在原版本上修改,针对手机客户端,如果用户没有升级,还得保留老版本的服务端给用户使用
    于是有了v1/v2版本的区别,api链接要能识别数据版本;浏览器客户端不存在,因为服务端更新后,客户端自然对应的是新版本
    
4 数据即是资源,均使用名词(可复数)
    - 接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
    - https://api.baidu.com/users
    - https://api.baidu.com/books
    - https://api.baidu.com/book

    注:一般提倡用资源的复数形式,在url链接中尽量不要出现操作资源的动词,错误示范:https://api.baidu.com/delete-user
    如果仅用名词user,如何表示是获取user还是删除user?于是有第五条规范
    - 特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源(不涉及到数据库的资源),或是动词就是接口的核心含义
    - https://api.baidu.com/place/search 搜索接口
    - https://api.baidu.com/login        登录接口
    
5 资源操作由请求方式决定(method)
    - 操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查动作
    - https://api.baidu.com/books      - get请求:获取所有书
    - https://api.baidu.com/books/1    - get请求:获取主键为1的书
    - https://api.baidu.com/books      - post请求:新增一本书书
    - https://api.baidu.com/books/1    - put请求:整体修改主键为1的书
    - https://api.baidu.com/books/1    - patch请求:局部修改主键为1的书
    - https://api.baidu.com/books/1    - delete请求:删除主键为1的书
    
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 响应状态码
    7.1 正常响应,响应状态码2xx
    - 200:常规请求
    - 201:创建成功
    7.2 重定向响应,响应状态码3xx
    - 301:永久重定向
    - 302:暂时重定向
    7.3 客户端异常,响应状态码4xx
    - 403:请求无权限
    - 404:请求路径不存在
    - 405:请求方法不存在
    7.4 服务器异常,响应状态码5xx
    - 500:服务器异常
        
8 响应走时应该返回成功或错误信息,error或message当做key
    {
        error: "无权限操作"
    }
    {
        message: "ok"
    }
    
9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
    GET /collection:            查所有:返回资源对象的列表(数组)
    GET /collection/resource:   查单个:返回单个资源对象,即一个字典
    POST /collection:           新增一个:返回新生成的资源对象,前端拿到对象可以直接访问,不用再发一次get请求
    PUT /collection/resource:   修改一个:返回完整的资源对象,即修改后的对象
    PATCH /collection/resource: 修改一个:返回完整的资源对象
    DELETE /collection/resource:删除一个:返回一个空文档
    
10 需要url请求的资源,需要访问资源的请求链接
# Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么
{
    "status": 0,
    "msg": "ok",
    "results":[
        {
            "name":"肯德基(罗餐厅)",
            "img": "https://image.baidu.com/kfc/001.png"
        }
        ...
    ]
}

 

五 Django-Rest-Framework 

drf 是一个建立在Django基础之上的Web应用开发框架,可以快速的开发REST API接口应用。核心思想:缩减编写api接口的代码。

drf 的功能

1 序列化器Serialzier的定义:简化序列化与反序列化的过程
2 视图类、扩展类、视图集:简化视图的编写工作
3 提供了认证、权限、限流、过滤、分页、接口文档等功能支持
4 提供了一个API的Web可视化界面,可以用来查看测试接口(推荐用postman)

drf 的安装

pip3 install djangorestframework

drf 的简单使用

1 在setting.py 的app中注册
    INSTALLED_APPS = [
        'rest_framework'
    ]
    
2 在models.py中创建表模型-->执行数据库迁移命令-->手动创建几条数据
    class Book(models.Model):
        nid=models.AutoField(primary_key=True)
        name=models.CharField(max_length=32)
        price=models.DecimalField(max_digits=5,decimal_places=2)
        author=models.CharField(max_length=32)
        
3 应用下任意建一个py文件(如:ser.py),定义一个序列化类
    from rest_framework.serializers import ModelSerializer
    from app01.models import Book
    class BookModelSerializer(ModelSerializer):
        class Meta:
            model = Book
            fields = "__all__"
                
4 views.py中定义一个视图类
    from rest_framework.viewsets import ModelViewSet
    from models import Book
    from ser import BookModelSerializer
    class BooksViewSet(ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookModelSerializer
            
5 写路由关系
    from app01 import views
    from rest_framework.routers import DefaultRouter
    router = DefaultRouter()                      # 可以处理视图的路由器
    router.register('books', views.BooksViewSet)  # 向路由器中注册视图集,让'books'路径响应到BooksViewSet视图类中去
                                                  # BooksViewSet视图类,相当于是一个请求集合,只要匹配上books路径,就调用类
    urlpatterns = [
        path('admin/', admin.site.urls),
    ]
    urlpatterns += router.urls  # router.urls是视图集路由列表,将路由器中的所有路由信息追加到django的路由列表中
        
6 启动,在postman中测试即可

 

六 CBV源码分析

上述drf简单使用中,我们定义了一个视图类BooksViewSet,继承ModelViewSet-->继承APIView--->继承django原生的View。

我们知道,CBV基于类的视图都要继承View,我们研究View的源码,先定义一个简单的视图类:

from django.views import View

# Create your views here.

class Books(View):
    def get(self, request):
        return HttpResponse('ok')

配置路由:

path('books/', views.Books.as_view())

研究View源码,重点研究as_view 方法和 dispatch方法

as_view()方法 是绑定给类的方法,类调用把自己传进去,如上述路由,则是把Books这个类传进去。as_view()嵌套一个view()函数,执行完后返回view函数的内存地址。

请求来,匹配上books路径,会执行view函数,并把当次请求的request对象当做参数传入。

返回self.dispatch(request),self是Books对象,它自身没有dispatch属性-->产生对象的Books类也没有-->查找到父类View有dispatch属性。

def dispatch(self, request, *args, **kwargs):
    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
        return handler(request, *args, **kwargs)

对象调用绑定方法把对象自己传入,request是当次请求的request。

request.method.lower()拿到当次请求方式-->转小写,View类中全局定义了http_method_names  它是请求方式列表,先判断请求方式是否合法,不合法报错;

getattr反射,反射拿对象的属性,拿不到报错,self是Books对象,如果当次是get请求,就相当于handler=getattr(self, 'get'),先在对象自己这里找get属性-->没有-->父类Books里有get属性-->把get内存地址赋值给handler;

return handler(request, *args, **kwargs),返回的是handler的调用结果,传入request,就是在调用Books对象的get方法,传入当次请求的request,绕一圈还是执行视图函数(FBV)。

 

七 APIView源码分析

通过View源码分析,了解路由匹配和视图类执行的原理,是通过as_view()。drf的执行流程,就是重写as_view方法,加入序列化、验证等功能。

views.py中定义一个视图类,继承APIView

from rest_framework.views import APIView

class BooksAPIView(APIView):
    def get(self, request):
        return HttpResponse('ok')

配置路由:

视图类调用as_view方法,BooksAPIView类里没有-->父类APIView里有,不是django原生的as_view,是drf组件里APIView的方法。

path('books/apiview/', views.BooksAPIView.as_view())

研究APIView源码

view = super().as_view(**initkwargs)

调用父类,即django原生View的as_view,赋值给view,相当于还是返回View类中,内层函数view内存地址。

但是返回前做了处理 return csrf_exempt(view),csrf_exempt是局部禁用csrf装饰器,跟语法糖@csrf_exempt,直接加在视图函数头顶作用一样。

请求来了-->路由匹配上--->调用view(request)--->最终返回self.dispatch(request),self是BooksAPIView的对象,查找dispatch方法,查找到APIView的dispatch方法。

APIView的 dispatch 方法:

self.initialize_request(request) BooksAPIView的对象调用initialize_request()方法,把当次请求的request当做参数传入。

APIView的 initialize_request 方法:

Request类是drf框架的一个类,from rest_framework.request import Request

Request类源码需要研究的几个方法

只要继承了APIView,视图类中的request对象,都是新的,也就是Request的对象了。

Request类重写了__getattr__方法,所以使用新的request对象,跟使用原生的request是一模一样。

Request类的data方法:

前端传urlencoded编码-->后端返回 QueryDict对象(就是一个字典,比字典更强大)

前端json编码-->后端返回普通字典,原生request把json格式数据放在body中,获取比较麻烦

Request类的query_params方法:

用的原生request.GET 获取地址中的参数,drf中推荐使用requset.query_params获取get请求传过来数据。拿文件还是request.FILES。

回到 dispatch方法,继续往下执行:

三大认证模块,即 APIView的 initial 方法

def initial(self, request, *args, **kwargs):
    # 认证组件:校验用户 - 游客、合法用户、非法用户
    # 游客:代表校验通过,直接进入下一步校验(权限校验)
    # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
    # 非法用户:代表校验失败,抛出异常,返回403权限异常结果
    self.perform_authentication(request)
    # 权限组件:校验用户权限 - 必须登录、所有用户、登录读写、游客只读、自定义用户角色
    # 认证通过:可以进入下一步校验(频率认证)
    # 认证失败:抛出异常,返回403权限异常结果
    self.check_permissions(request)
    # 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
    # 没有达到限次:正常访问接口
    # 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
    self.check_throttles(request)

响应模块

跟View类的dispatch逻辑一样,通过反射拿到视图类中的方法,但它没有直接返回,而生赋值给一个变量response。

异常模块

在返回前有个总的异常捕获,前后端分离,只用返回json格式数据,如果程序出错,也用json格式返回一个异常信息,如"服务器内部错误"。

渲染模块 self.response = self.finalize_response,返回前把response对象进行包装,返回给浏览器一个可视化页面,上面有json格式数据;

返回给postman直接是json格式数据。

 

 

 

posted @   不会钓鱼的猫  阅读(2148)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示