很多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 Fieldinghttps://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_viewfrom rest_framework.response import Responsefrom 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 renderersfrom rest_framework.response import Responseclass 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//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 pathfrom rest_framework.urlpatterns import format_suffix_patternsfrom snippets import views# API endpointsurlpatterns = format_suffix_patterns([    path('', views.api_root),    path('snippets/', views.SnippetList.as_view(), name='snippet-list'),    path('snippets//', views.SnippetDetail.as_view(), name='snippet-detail'),    path('snippets//highlight/', views.SnippetHighlight.as_view(), name='snippet-highlight'),    path('users/', views.UserList.as_view(), name='user-list'),    path('users//', 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/

restful可以转发么_DRF使用超链接API实现真正RESTful相关推荐

  1. restful和rest_HATEOAS的RESTful服务:JVM上的REST API和超媒体

    restful和rest 1.简介 到目前为止,我们已经花了很多时间谈论了相当数量的关于角色的的超媒体和HATEOAS在REST风格的 Web服务和API,扫视不同规格和可用性方面. 听起来好像支持超 ...

  2. restful可以转发么_RESTful 的收益是什么?

    RESTful 是那种咋一听很有道理,仔细思考一下又觉得然并卵的东西.在乘法云的设计中,IO Adapter 实质上就是把各种外部的 API,统一适配成 RESTful 的风格.回答了 RESTful ...

  3. Python调用OpenStack API 《通过RESTful编写Python运维》

    目录 Python调用OpenStack API   <通过RESTful编写Python运维> 赛题实施 1. 认证服务:用户管理 (1&#x

  4. API接口的RESTful设计

    前言:今天我们来聊一聊在基于SpringBoot前后端分离开发模式下,如何友好的返回统一的标准格式以及如何优雅的处理全局异常. 一般系统的大致整体架构图如下: 因为本篇博客主要介绍的是API接口,其他 ...

  5. restful可以转发么_什么是RESTFUL?REST的请求方法有哪些,有什么区别?

    大家好,我是IT修真院上海分院第3期学员,一枚正直善良的java程序员,今天给大家分享一下,修真院java任务中的一个知识点:什么是RESTFUL?REST的请求方法有哪些,有什么区别? restFu ...

  6. api restful_HATEOAS的RESTful服务。 记录超媒体API

    api restful 1.简介 希望本教程的前一部分不仅揭示了超媒体和HATEOAS的深远意义,而且使我们确信这些都是RESTful Web服务和API的基本构建模块. 在这一部分中,我们将继续侧重 ...

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

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

  8. django html5 关系,Django REST FrameWork中文教程5:关系和超链接API

    目前我们的API中的关系是用主键表示的.我们将通过使用超链接来提高我们API的内部联系. 为我们的API创建一个根路径 现在我们有'snippets'和'users'的路径,但是我们的API没有一个入 ...

  9. SpringCloud工作笔记048---RESTful API 中 HTTP 状态码的定义_以及把RESTFul版本号_放到http协议header中_以及RestFul设计时的两个误区

    JAVA技术交流QQ群:170933152 ------------------------- RESTful架构有一些典型的设计误区. 最常见的一种设计错误,就是URI包含动词.因为"资源 ...

最新文章

  1. 读CLR via C#总结(9) 索引器(有参属性)
  2. LINUX 下面SQL定时备份
  3. Git之hotfix热修复分支
  4. eslint是什么_为什么eslint没有 no-magic-string?
  5. 设计模式(三)单实例模式
  6. 数学连乘和累加运算符号_期中复习:小学数学各年级知识点和重点、难点大全!...
  7. 如何设置xampp的phpmyadmin外网访问?
  8. MIPS之u-boot流程分析
  9. 基于FaceNet人脸识别的人脸对比认证(一) -- 环境部署、认证实验
  10. 阿里发布的Redis开发文档,涵盖了所有的redis操作
  11. 【车载音乐】数10G车载音乐包,MP3高品质无损音乐素材合集
  12. 机器学习(11)——时间序列分析
  13. 断舍离---新生活方式指引
  14. matlab 互换 函数,Matlab矩阵中元素交换
  15. 《软件工程之美》打卡第六周,春招我借这份PDF的复习思路
  16. 好想与你天长地久,共度岁月
  17. 为啥联通卡显示无服务器,天津联通物联卡显示无服务器
  18. 美团2021-10-4最优二叉树Ⅱ
  19. 图片无损压缩 图片无损等比例缩放
  20. 2020区块链春风不止,河北、湖南、北京等5省发布区块链专项发展政策|链塔智库...

热门文章

  1. laravel5.4 关于数据填充的知识
  2. linux实践-弱密码导致服务器被黑
  3. 【VMCloud云平台】SCO(四)流程准备
  4. 软件定义光网络-SDON
  5. php 为啥报错,php Soap 报错 求大神帮忙看看为什么
  6. 关系数据库非关系数据库_如何与关系数据库最佳配合
  7. aws lambda_恐怕您正在考虑AWS Lambda的冷启动完全错误
  8. 悉尼大学计算机研究生学制,悉尼大学研究生学制
  9. 什么是python第三方库
  10. 表白这件事,比解 bug 要难多少?