前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DRF使用超链接API实现真正RESTful

DRF使用超链接API实现真正RESTful

作者头像
dongfanger
发布2020-12-23 15:12:19
1.2K0
发布2020-12-23 15:12:19
举报
文章被收录于专栏:dongfangerdongfanger

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

代码语言:javascript
复制
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的根目录发出请求,会得到这样一个文档:

代码语言:javascript
复制
{"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

代码语言:javascript
复制
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中:

代码语言:javascript
复制
path('', views.api_root),

创建SnippetHighlight的Endpoint

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

image-20201220210707722
image-20201220210707722

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

代码语言:javascript
复制
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中:

代码语言:javascript
复制
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中把我们之前的代码:

代码语言:javascript
复制
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']

修改为:

代码语言:javascript
复制
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中修改一下:

代码语言:javascript
复制
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文件:

代码语言:javascript
复制
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/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-12-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 超链接(Hypermedia)API
  • 创建api_root的Endpoint
  • 创建SnippetHighlight的Endpoint
  • HyperlinkedModelSerializer
  • 修改url pattern
  • 添加分页
  • 东方说
相关产品与服务
Serverless HTTP 服务
Serverless HTTP 服务基于腾讯云 API 网关 和 Web Cloud Function(以下简称“Web Function”)建站云函数(云函数的一种类型)的产品能力,可以支持各种类型的 HTTP 服务开发,实现了 Serverless 与 Web 服务最优雅的结合。用户可以快速构建 Web 原生框架,把本地的 Express、Koa、Nextjs、Nuxtjs 等框架项目快速迁移到云端,同时也支持 Wordpress、Discuz Q 等现有应用模版一键快速创建。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档