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®ion=上海&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"
}
...
]
}
五
drf 是一个建立在Django基础之上的Web应用开发框架,可以快速的开发REST API接口应用。核心思想:缩减编写api接口的代码。
drf 的功能
1 序列化器Serialzier的定义:简化序列化与反序列化的过程
2 视图类、扩展类、视图集:简化视图的编写工作
3 提供了认证、权限、限流、过滤、分页、接口文档等功能支持
4 提供了一个API的Web可视化界面,可以用来查看测试接口(推荐用postman)
pip3 install djangorestframework
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中测试即可
六
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
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格式数据。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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训练数据并当服务器共享给他人