很多API并不是真正的实现了RESTful,而应该叫做RPC (Remote Procedure Call 远程过程调用),Roy Fielding曾经提到了它们的区别,原文如下:

I am getting frustrated by the number of people calling any HTTP-based interface a REST API. Today’s example is the SocialSite REST API. That is RPC. It screams RPC. There is so much coupling on display that it should be given an X rating.

What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint? In other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period. Is there some broken manual somewhere that needs to be fixed?

— Roy Fielding
https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

大概意思是,如果应用状态引擎(API)不是超文本驱动的,那么就不是RESTful。

我的理解是,像超文本一样携带一个地址,可以寻址定位信息,如超文本的link属性。

超链接(Hypermedia)API

Hypermedia指的是,返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档:

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

上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。

摘取自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

创建api_root的Endpoint

回到教程的例子。在前面我们已经为snippetsusers创建了Endpoint,现在来创建根目录的Endpoint,编辑snippets/views.py

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse @api_view(['GET'])
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'snippets': reverse('snippet-list', request=request, format=format)
})

reverse()函数用来返回snippets/urls.py中viewname对应的url,如path('users/', views.UserList.as_view(), name='user-list')

然后添加到snippets/urls.py中:

path('', views.api_root),

创建SnippetHighlight的Endpoint

还记得在上篇文章中提到的Snippet.highlighted字段么:

我们现在为它创建Endpoint,继续编辑snippets/views.py

from rest_framework import renderers
from rest_framework.response import Response class SnippetHighlight(generics.GenericAPIView):
queryset = Snippet.objects.all()
renderer_classes = [renderers.StaticHTMLRenderer] def get(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)

然后添加到snippets/urls.py中:

path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view()),

因为snippet.highlighted不是JSON而是HTML,所以用[renderers.StaticHTMLRenderer]返回预渲染的(pre-rendered)HTML。

HyperlinkedModelSerializer

在Web API设计中,一般有以下几种方式来表示实体之间的关系:

  • 主键
  • 超链接
  • 关系实体(the related entity),唯一标识符字段(a unique identifying slug field)
  • 关系实体,默认字符串(the default string representation)
  • 关系实体,嵌入到父类中(the parent representation)
  • 其他自定义

前2个比较熟悉,后面几个有点不太懂,我理解是类似于数据库的关联关系表。

DRF支持以上所有方式,这里我们用DRF的HyperlinkedModelSerializer来实现真正的RESTful。在snippets/serializers.py中把我们之前的代码:

class SnippetSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username') class Meta:
model = Snippet
fields = ['id', 'title', 'code', 'linenos', 'language', 'style', 'owner'] class UserSerializer(serializers.ModelSerializer):
snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all()) class Meta:
model = User
fields = ['id', 'username', 'snippets']

修改为:

class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html') class Meta:
model = Snippet
fields = ['url', 'id', 'highlight', 'owner',
'title', 'code', 'linenos', 'language', 'style'] class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True) class Meta:
model = User
fields = ['url', 'id', 'username', 'snippets']

其中ModelSerializer换成了HyperlinkedModelSerializer,后者的区别如下:

  • 默认不包含id字段

  • 包含url字段,用HyperlinkedIdentityField表示

    源码:serializer_url_field = HyperlinkedIdentityField

  • 关系用HyperlinkedRelatedField表示,而不是PrimaryKeyRelatedField

    源码:serializer_related_field = HyperlinkedRelatedField

由于用了HyperlinkedModelSerializer,SnippetSerializer和UserSerializer的url字段默认指向的是'{model_name}-detail' url pattern,这是DRF定义的,在示例中就是'snippet-detail''user-detail'。新增的highlight字段和url字段是一样的类型,它指向的是'snippet-highlight',而不是'snippet-detail'

修改url pattern

既然已经提到了url pattern,那么在snippets/urls.py中修改一下:

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns from snippets import views # API endpoints
urlpatterns = format_suffix_patterns([
path('', views.api_root),
path('snippets/', views.SnippetList.as_view(), name='snippet-list'),
path('snippets/<int:pk>/', views.SnippetDetail.as_view(), name='snippet-detail'),
path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view(), name='snippet-highlight'),
path('users/', views.UserList.as_view(), name='user-list'),
path('users/<int:pk>/', views.UserDetail.as_view(), name='user-detail')
])

name就是在serializers.pyviews.py中用到的。

添加分页

REST设计基本原则提到了:处理好分页。DRF添加分页的方式很简单,编辑tutorial/settings.py文件:

REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}

东方说

我之前是在学SpringBoot的时候了解过RESTful API的超链接API,文章开头的那一段介绍就是当时写的笔记,DRF提供了HyperlinkedModelSerializer来实现,还是比较好理解的,其中的细节需要在实战中再多多熟悉。

参考资料:

https://www.django-rest-framework.org/tutorial/5-relationships-and-hyperlinked-apis/

https://spring.io/guides/tutorials/rest/

DRF使用超链接API实现真正RESTful的更多相关文章

  1. 在ASP.NET Core Web API中为RESTful服务增加对HAL的支持

    HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务 ...

  2. Web API 入门系列 - RESTful API 设计指南

    参考:https://developer.github.com/v3/  https://github.com/bolasblack/http-api-guide HTTP 协议 目前使用HTTP1. ...

  3. API认证&SDK&RESTful

    python API的安全认证   我们根据pid加客户端的时间戳进行加密md5(pid|时间戳)得到的单向加密串,与时间戳,或者其它字段的串的url给服务端. 服务端接收到请求的url进行分析 客户 ...

  4. Web Api:基于RESTful标准

    参考链接:http://www.cnblogs.com/lori/p/3555737.html 简单的了解到RESTful架构后,跟着以上链接做了一个小练习. Step1: 新建WebApi项目,新建 ...

  5. 【API设计】RESTful API 设计指南

    RESTful API URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作. 例如 . REST描述的是在网络中client和server的一种交互形式:REST本身不 ...

  6. 我是如何根据豆瓣api来理解Restful API设计的

    1.什么是REST REST全称是Representational State Transfer,表述状态转移的意思.它是在Roy Fielding博士论文首次提出.REST本身没有创造新的技术.组件 ...

  7. 初识web API接口及Restful接口规范

    一.web API接口 什么是web API接口?: 明确了请求方式,提供对应后台所需参数,请求url链接可以得到后台的响应数据 url : 返回数据的url https://api.map.baid ...

  8. 【Python之路】特别篇--服务商API认证、Restful、一致性哈希

    API加密方式 1/ 加密方式: Md5 (随机字符串 + 时间戳) 2/ 发送方式: http://127.0.0.1:8888/index?pid= MD5加密值 | 时间戳 | 序号 服务端接收 ...

  9. 百度API车牌识别——Restful方式

    源码下载地址:https://download.csdn.net/download/redhat588/11798294 Delphi xe 10.3.2 for windows 7 环境编译通过! ...

随机推荐

  1. FL Studio中如何使用插件混杂功能中的琶音器

    琶音指一串和弦音从低到高或从高到低依次连续奏出,可视为分解和弦的一种.通常作为一种专门的技巧训练用于练习曲中,有时作为短小的连接句或经过句出现在乐曲旋律声部中.在Trance类型电子音乐中,琶音的运用 ...

  2. 不一样的Flink入门教程

    前言 微信搜[Java3y]关注这个朴实无华的男人,点赞关注是对我最大的支持! 文本已收录至我的GitHub:https://github.com/ZhongFuCheng3y/3y,有300多篇原创 ...

  3. yii2.0 关于 ActiveForm 中 checkboxList 的使用

    在视图中创建复选框,列出复选框内的内容其中$id 为 列出在复选框中的数组 //$ids:所有要显示的checkbox(Array)<?=$form->field($model, 'id' ...

  4. python定时执行详解

    知识点 1. sched模块,准确的说,它是一个调度(延时处理机制),每次想要定时执行某任务都必须写入一个调度. (1)生成调度器:s = sched.scheduler(time.time,time ...

  5. LIKE 运算符

    运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算.例如:2+3,其操作数是2和3,而运算符则是"+".在vb2005中运算符大致可以分为5种类型:算术运算符.连接运算 ...

  6. Codeforces Round #672 (Div. 2) B. Rock and Lever题解(思维+位运算)

    题目链接 题目大意 给你一个长为n(n<=1e5)的数组,让你求有多少对a[i]和a[j] (i!=j)满足a[i]&a[j]>a[i]^a[j] 题目思路 这些有关位运算的题目肯 ...

  7. 《应用计算方法教程》matlab作业一

    运行界面: 运行界面: 运行界面: 运行界面: 运行界面: 运行界面: 运行界面: 运行界面: 运行界面: 运行界面: word文档百度云链接: 链接:https://pan.baidu.com/s/ ...

  8. 区块链V1版本实现之三

    部分程序代码(区块链的定义及遍历打印): 1 //创建区块链,使用Block数组模拟 2 type BlockChain struct { 3 Blocks []*Block 4 } 5 6 //实现 ...

  9. 怎么用fio测试存储性能

    1 /// -rw=read(100%顺序读) -rw=write(100%顺序写) -rw=randread(100%随机读) -rw=randwrite(100%随机写), 2 ///-rw=rw ...

  10. SpringCloud 源码系列(2)—— 注册中心 Eureka(中)

    五.服务注册 1.实例信息注册器初始化 服务注册的代码位置不容易发现,我们看 DiscoveryClient 初始化调度任务的这个方法,这段代码会去初始化一个实例信息复制器 InstanceInfoR ...