一开始在项目中使用的是OpenResty来实现REST API,但使用起来一直觉得不方便。主要是因为Lua没有ORM,也没有REST架构风格的框架。直到最近在用Django时,接触到Django REST framework,在深感便利的同时,也进一步加深了对REST架构风格的理解。所以写下这篇文章,一方面记录Django REST framework的体验过程,同时借此解读下REST架构风格。
1. 体验Django REST framework
1.1 安装
pip install djangopip install djangorestframework
1.2 创建Django项目并初始化
django-admin startproject rest_example # 创建Django项目django-admin.py startapp app # 创建应用python manage.py migrate # 执行迁移python manage.py createsuperuser--email admin@example.com--username admin # 创建超级用户
1.3 项目配置
在项目INSTALL_APP配置中加入rest_framework,修改rest_example/settings.py。
INSTALLED_APPS=(...'rest_framework',)
1.4 REST framework配置
在项目配置中加入REST_FRAMEWORK配置,在rest_example/settings.py加入以下内容。
1.5 创建序列化器
创建rest_example/app/serializers.py文件,内容如下。
序列化器会自动将模型序列化。对于RESTful架构来说,超链模型序列化器是非常合适的,因为可以提供连通性。
1.6 创建视图
修改rest_example/app/views文件,内容如下。
fromdjango.contrib.auth.modelsimportUser,Groupfromrest_frameworkimportviewsetsfromrest_example.app.serializersimportUserSerializer,GroupSerializerclassUserViewSet(viewsets.ModelViewSet):""" API endpoint that allows users to be viewed or edited.""" queryset=User.objects.all().order_by('-date_joined')serializer_class=UserSerializerclassGroupViewSet(viewsets.ModelViewSet):""" API endpoint that allows groups to be viewed or edited.""" queryset=Group.objects.all()serializer_class=GroupSerializer
ViewSet封装了通用的视图,实现了get、post、put、delete等请求方法对应的通用处理方法,直接继承可以极大地简化代码。
1.7 配置路由
修改rest_example/urls文件,内容如下。
fromdjango.conf.urlsimporturl,includefromrest_frameworkimportroutersfromrest_example.appimportviewsrouter=routers.DefaultRouter()router.register(r'users',views.UserViewSet)router.register(r'groups',views.GroupViewSet)# Wire up our API using automatic URL routing.# Additionally,we include login URLsforthe browsable API.urlpatterns=[url(r'^',include(router.urls)),url(r'^api-auth/',include('rest_framework.urls',namespace='rest_framework'))]
其中,api-auth路由是用于对REST API进行鉴权。
1.8 大功告成
启动Django,在浏览器中访问http://127.0.0.1:8000/,可以看到如下图所示界面。
image.png
只通过简单的配置,就得到了一个完善的REST API,可谓相当的便利。
2. 解读REST架构风格
2.1 资源URI
先看最简单的资源GET /,响应如下。
HTTP200OKAllow:GET,HEAD,OPTIONSContent-Type:application/jsonVary:Accept{"users":"http://127.0.0.1:8000/users/","groups":"http://127.0.0.1:8000/groups/"}
上述响应表示存在两种资源(可引用的对象):user资源和group资源,其URI分别为http://127.0.0.1:8000/users/和http://127.0.0.1:8000/groups/。
REST是面向资源的架构,在REST中,URI代表某个或某种资源,所以URI中只能有名词,而且一般是复数形式。
再看user资源GET /users,响应如下。
HTTP200OKAllow:GET,POST,HEAD,OPTIONSContent-Type:application/jsonVary:Accept[{"url":"http://127.0.0.1:8000/users/1/","username":"admin","email":"admin@example.com","groups":[]}]
上述响应表示当前只有一个user资源,用户名是admin,对应的URI是http://127.0.0.1:8000/users/1/。
因为URI表示的是具体的资源,所以应该在URI中包含user id。假如写为http://127.0.0.1:8000/users?id=1,这样代表的是从所有的user资源中过滤出id=1的资源集合,而不是表示id=1的具体资源。
在资源URI中,id需要放在URI路径中,不能放在请求参数中。请求参数适用于放过滤条件、分页信息等内容。
2.2 连通性
在GET /请求的响应中,包含了user资源和对应group资源的URI。
在GET /users请求的响应中,也包含了groups资源,只是因为当前没有group资源,所以是空数组。我们在页面登录后,创建名称为superuser的group,再把admin用户加入到superuser组中。此时再请求GET /users,响应如下。
HTTP200OKAllow:GET,POST,HEAD,OPTIONSContent-Type:application/jsonVary:Accept[{"url":"http://127.0.0.1:8000/users/1/","username":"admin","email":"admin@example.com","groups":["http://127.0.0.1:8000/groups/1/"]}]
可以看到,groups数组表明admin用户只属于一个组,该group资源对应的URI为http://127.0.0.1:8000/groups/1/。如果需要了解该group资源的具体信息,则可以通过请求GET http://127.0.0.1:8000/groups/1获取。
在资源响应中包含关联资源的URI,可以提供后续操作的入口,将各种资源串联起来,便于客户端进行下一步操作。
2.3 统一请求方法
REST通过统一请求方法,只知道资源URI就可以进行一系列增删查改的操作。反应到页面上,在GET /users资源时,页面可以提供如下页面来创建一个新的user资源,而这一切都是因为约定了POST是创建操作,资源描述通过body传递,资源ID由服务器自动生成,新生成资源的URI会通过请求响应返回。
image.png
同时,因为统一了请求方法,并且在Header中声明了该资源支持的请求方法,所以页面可以针对该资源,提供增删查改的一系列操作入口。如下图右上角所示。
image.png
2.4 资源的表述
在《那些年,我们一起误解过的REST》文中我提到过,同一个资源可以有多个不同的表述,每个表述需要是自描述的。例如,请求GET /users/1,可以选择返回json格式还是api格式,如下图右上角所示。
image.png
当请求json格式时,REST API返回纯json的表述;当请求api格式(实际上是html格式)时,REST API返回渲染过的html页面,所以才有上文的各种功能丰富的截图。这两种表述都是对相同资源的表述,本质上是相同的。至于究竟返回的是什么格式的表述,则需要通过响应Header中的Content-type字段说明。
领取专属 10元无门槛券
私享最新 技术干货