RESTful设计方法

1. 域名

应该尽量将API部署在专用域名之下。

  1. https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

  1. https://example.org/api/

2. 版本(Versioning)

应该将API的版本号放入URL。

  1. http://www.example.com/app/1.0/foo
  2.  
  3. http://www.example.com/app/1.1/foo
  4.  
  5. http://www.example.com/app/2.0/foo

另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL。版本号可以在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):

  1. Accept: vnd.example-com.foo+json; version=1.0
  2.  
  3. Accept: vnd.example-com.foo+json; version=1.1
  4.  
  5. Accept: vnd.example-com.foo+json; version=2.0

3. 路径(Endpoint)

路径又称"终点"(endpoint),表示API的具体网址,每个网址代表一种资源(resource)

(1) 资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。

举例来说,以下是不好的例子:

  1. /getProducts
  2. /listOrders
  3. /retreiveClientByOrder?orderId=1

对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

  1. GET /products :将返回所有产品清单
  2. POST /products :将产品新建到集合
  3. GET /products/4 :将获取产品 4
  4. PATCH(或)PUT /products/4 :将更新产品 4

(2) API中的名词应该使用复数。无论子资源或者所有资源。

举例来说,获取产品的API可以这样定义

  1. 获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
  2. 获取所有产品: http://127.0.0.1:8080/AppName/rest/products

3. HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面四个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • DELETE(DELETE):从服务器删除资源。

还有三个不常用的HTTP动词。

  • PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

  1. GET /zoos:列出所有动物园
  2. POST /zoos:新建一个动物园(上传文件)
  3. GET /zoos/ID:获取某个指定动物园的信息
  4. PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
  5. PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
  6. DELETE /zoos/ID:删除某个动物园
  7. GET /zoos/ID/animals:列出某个指定动物园的所有动物
  8. DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

4. 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数。

  1. ?limit=10:指定返回记录的数量
  2. ?offset=10:指定返回记录的开始位置。
  3. ?page=2&per_page=100:指定第几页,以及每页的记录数。
  4. ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
  5. ?animal_type_id=1:指定筛选条件

参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoos/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。

6. 状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

  • 200 OK - [GET]:服务器成功返回用户请求的数据
  • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
  • 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
  • 204 NO CONTENT - [DELETE]:用户删除数据成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
  • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
  • 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
  • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  • 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
  • 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
  • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见

https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

7. 错误处理(Error handling)

如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

  1. {
  2. error: "Invalid API key"
  3. }

8. 返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

  • GET /collection:返回资源对象的列表(数组)
  • GET /collection/resource:返回单个资源对象
  • POST /collection:返回新生成的资源对象
  • PUT /collection/resource:返回完整的资源对象
  • PATCH /collection/resource:返回完整的资源对象
  • DELETE /collection/resource:返回一个空文档

9. 超媒体(Hypermedia API)

RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么。

比如,Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

  1. {
  2. "current_user_url": "https://api.github.com/user",
  3. "authorizations_url": "https://api.github.com/authorizations",
  4. // ...
  5. }

从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

  1. {
  2. "message": "Requires authentication",
  3. "documentation_url": "https://developer.github.com/v3"
  4. }

上面代码表示,服务器给出了提示信息,以及文档的网址。

10. 其他

服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

  1. urlpatterns = {
  2. url(r'^index/$', views.index),
  3. url(r'^books/$', views.BooksAPIView.as_view()),
  4. url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),
  5. url(r'^heros/$', views.HerosAPIView.as_view()),
  6. url(r'^heros/(?P<pk>\d+)/$', views.HeroAPIView.as_view()),
  7. }

demo:

django中的restframework风格的API接口的简单实现;

  1. import json
  2.  
  3. from django.forms import model_to_dict
  4. from django.http import JsonResponse
  5.  
  6. from django.shortcuts import HttpResponse
  7. from django.views import View
  8.  
  9. from app001.models import BookInfo
  10. from app001.models import HeroInfo
  11.  
  12. def index(request):
  13. return HttpResponse("ok")
  14.  
  15. class BooksAPIView(View):
  16. def get(self, request):
  17. """
  18. 查询所有图书
  19. :param request:
  20. :return:
  21. """
  22. queryset = BookInfo.objects.all().values()
  23. book_list = []
  24. book_list = list(queryset)
  25.  
  26. return JsonResponse(book_list, safe=False)
  27.  
  28. def post(self, request):
  29. """
  30. 新增图书
  31. :param request:
  32. :return:
  33. """
  34. json_bytes = request.body
  35. json_str = json_bytes.decode()
  36. book_dict = json.loads(json_str)
  37.  
  38. # book = BookInfo.objects.create(
  39. # btitle = book_dict.get("btitle"),
  40. # bpub_date = datetime.strptime(book_dict.get("bpub_date"),'%Y-%m-%d').date(),
  41. # )
  42. book = BookInfo.objects.create(
  43. **book_dict
  44. )
  45.  
  46. return JsonResponse({
  47. "id": book.id,
  48. "btitle": book.btitle,
  49. 'bpub_date': book.bpub_date,
  50. 'bread': book.bread,
  51. 'bcomment': book.bcomment,
  52. # 'image': book.image.url if book.image else ''
  53. }, status=201)
  54.  
  55. class BookAPIView(View):
  56. """
  57. 获取单个图书的信息
  58. """
  59.  
  60. def get(self, request, pk):
  61. try:
  62. book = BookInfo.objects.get(id=pk)
  63. except Exception as e:
  64. print(e)
  65. return HttpResponse(status=404)
  66.  
  67. book_dict = model_to_dict(book)
  68. return JsonResponse(book_dict)
  69.  
  70. def put(self, request, pk):
  71. """
  72. 修改图书信息
  73. :param request:
  74. :param pk:
  75. :return:
  76. """
  77. try:
  78. book = BookInfo.objects.get(id=pk)
  79. except Exception as e:
  80. print(e)
  81. return HttpResponse(status=404)
  82.  
  83. # 校验参数
  84.  
  85. json_bytes = request.body
  86. json_str = json_bytes.decode()
  87. book_dict = json.loads(json_str)
  88.  
  89. BookInfo.objects.filter(id=pk).update(**book_dict)
  90. # from django.forms.models import model_to_dict
  91. book_dict = model_to_dict(BookInfo.objects.get(id=pk))
  92. return JsonResponse(book_dict)
  93.  
  94. def delete(self, request, pk):
  95. """
  96. 删除
  97. :param request:
  98. :param pk:
  99. :return:
  100. """
  101. try:
  102. book = BookInfo.objects.get(id=pk)
  103. except Exception as e:
  104. print(e)
  105. return HttpResponse(status=404)
  106.  
  107. book.delete()
  108. return HttpResponse(status=204)
  109.  
  110. class HerosAPIView(View):
  111. def get(self, request):
  112. hero = HeroInfo.objects.all().values()
  113. hero = list(hero)
  114. return JsonResponse(hero, safe=False)
  115.  
  116. def post(self, request):
  117. json_bytes = request.body
  118. json_str = json_bytes.decode()
  119. hero_dict = json.loads(json_str)
  120. hero = ""
  121. try:
  122. hero = HeroInfo.objects.create(**hero_dict)
  123. except Exception as e:
  124. print(e)
  125. return HttpResponse(status=404)
  126.  
  127. return JsonResponse(model_to_dict(hero), status=201)
  128.  
  129. class HeroAPIView(View):
  130. def get(self, request, pk):
  131. hero = HeroInfo.objects.get(id=pk)
  132. return JsonResponse(model_to_dict(hero))
  133.  
  134. def put(self, request, pk):
  135. """
  136. 修改图书
  137. :param request:
  138. :param pk:
  139. :return:
  140. """
  141. try:
  142. hero = HeroInfo.objects.get(id=pk)
  143. except Exception as e:
  144. print(e)
  145. return HttpResponse(status=404)
  146.  
  147. json_bytes = request.body
  148. json_str = json_bytes.decode()
  149. json_dict = json.loads(json_str)
  150.  
  151. hero = HeroInfo.objects.filter(id=pk).update(**json_dict)
  152. hero_dict = model_to_dict(HeroInfo.objects.get(id=pk))
  153. return JsonResponse(hero_dict)
  154.  
  155. def delete(self, request, pk):
  156. try:
  157. hero = HeroInfo.objects.get(id=pk)
  158. except Exception as e:
  159. print(e)
  160. return HttpResponse(status=404)
  161. hero.delete()
  162. return HttpResponse(status=204)

Views函数

Django REST framework API开发的更多相关文章

  1. Django Rest Framework API指南

    Django Rest Framework API指南 Django Rest Framework 所有API如下: Request 请求 Response 响应 View 视图 Generic vi ...

  2. Django REST Framework API Guide 06

    本节大纲 1.Validators 2.Authentication Validators 在REST框架中处理验证的大多数时间,您将仅仅依赖于缺省字段验证,或在序列化器或字段类上编写显式验证方法.但 ...

  3. Django REST Framework API Guide 01

    之前按照REST Framework官方文档提供的简介写了一系列的简单的介绍博客,说白了就是翻译了一下简介,而且翻译的很烂.到真正的生产时,就会发现很鸡肋,连熟悉大概知道rest framework都 ...

  4. Django REST Framework API Guide 04

    本节大纲 1.serializers 1.Serializers Serializers允许复杂的数据,像queryset和模型实例转换成源生的Python数据类型.从而可以更简单的被渲染成JSON, ...

  5. Django REST Framework API Guide 03

    本节大纲 1.Routers 2.Parsers 3.Renderers Routers Usage from rest_framework import routers router = route ...

  6. Django REST Framework API Guide 02

    本节大纲 1.Generic Views 2.ViewSets  1.Generic Views CBV的主要的一个优点就是极大的允许了对于代码的从用.自然,rest framework取其优势,提供 ...

  7. tastypie Django REST framework API [Hello JSON]

    tastypie is a good thing. Haven't test it thoroughly. Gonna need some provement. Now I will introduc ...

  8. Django REST Framework API Guide 08

    1.Filtering 2.Pagination FIltering GenericAPIView的子类筛选queryset的简单方法是重写.get_quueryset()方法. 1.根据当前用户进行 ...

  9. Django REST Framework API Guide 07

    本节大纲 1.Permissions 2.Throttling Permissions 权限是用来授权或者拒绝用户访问API的不同部分的不同的类的.基础的权限划分 1.IsAuthenticated ...

随机推荐

  1. Redis的搭建和Redis的集群搭建

    1.Redis的官网:https://redis.io/      Redis的测试网站:http://try.redis.io/ 2.参考博客:https://www.cnblogs.com/maf ...

  2. Vuex详解笔记1

    vuex 是什么Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 什么是状态?状态这里泛指 ...

  3. js 2017 - 2

    设置360为极速模式   <meta name='renderer' content='webkit'> css3超出隐藏 .ellipsis { // 超出一行 width: 100%; ...

  4. console输出彩色字体

    console.log("%c%s","color: red; background: yellow; font-size: 24px;","警告!& ...

  5. 【译】理解JavaScript中的柯里化

    译文开始 函数式编程是一种编程风格,这种编程风格就是试图将传递函数作为参数(即将作为回调函数)和返回一个函数,但没有函数副作用(函数副作用即会改变程序的状态). 有很多语言采用这种编程风格,其中包括J ...

  6. FastJson 数组、List、Set、Map基本序列化与日期格式化

    摘要: FastJson可以直接序列化数组.List.Set.Map等,也可以直接支持日期类型数据的格式化,包括java.util.Date.java.sql.Date.java.sql.Timest ...

  7. sql 跨服务器查询

    创建链接服务器 exec sp_addlinkedserver 'ITSV ', ' ', 'SQLOLEDB ', '远程服务器名或ip地址 ' exec sp_addlinkedsrvlogin ...

  8. linux系统虚拟机下安装nginx基础

    虽然安装nginx什么的 .以及如何配置等等一系列的资料案例已经很多了 但是作为菜鸟的我还是搞了半天哈 官网上面也有.但是一些细节方面的并没有说明.导致踩了半天坑才搞好 本案例的系统环境     wi ...

  9. TCP简介

    TCP(Transmission Control Protocol) 传输控制协议,是一种面向连接的.可靠的.基于字节流的传输层通信协议. TCP是一种面向连接(连接导向)的.可靠的基于字节流的传输层 ...

  10. DT:DT实现根据乳腺肿瘤特征向量高精度预测肿瘤的是恶性还是良性—Jason niu

    %DT:DT实现根据乳腺肿瘤特征向量高精度预测肿瘤的是恶性还是良性 load data.mat a = randperm(569); Train = data(a(1:500),:); Test = ...