RESTful API设计原则与规范

时间:2019-05-21 21:00:51   收藏:0   阅读:1148

RESTful API设计原则与规范

 

 

一、背景与基础概念 2

二、RESTful API应遵循的原则 3

1、协议(Protocol) 3

2、域名(ROOT URL) 3

3、版本(Versioning) 3

4、路径(Endpoints) 3

5HTTP动词(HTTP Verbs) 4

6、过滤信息(Filtering 5

7、状态码(Status Codes 5

8、错误处理(Error handling 6

9、返回结果(Response 6

10、使用HATEOASHypermedia API 6

11、认证(Authentication 7

三、Swagger API标准 7

 

REST,即Representational State Transfer的缩写。RESTful架构,是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制,所以正得到越来越多网站的采用。如果一个架构符合REST原则,就称它为RESTful架构。

本文即将描述的,即是创建RESTful架构的API所要遵循的原则与规范。

一、背景与基础概念

Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。

- GET 用来获取资源

- POST 用来新建资源

- PUT 用来更新资源

- DELETE 用来删除资源

 

 

二、RESTful API应遵循的原则

1、协议(Protocol)

API与用户的通信协议,尽量使用HTTPs协议。HTTPs协议的所有信息都是加密传播,第三方无法窃听,具有校验机制,一旦被篡改,通信双方会立刻发现,配备身份证书,防止身份被冒充。

2、域名(ROOT URL)

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

https://api.example.com

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

https://example.org/api/

3、版本(Versioning)

应该将API的版本号放入URL

https://api.example.com/v1/

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

注:需要注意版本规划,以便以后API的升级和维护。使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点如2.5

 

4、路径(Endpoints)

路径表示API的具体网址URL。在RESTful架构中,每个URL代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与代表的对象名称对应。一般来说,某一同种记录的集合collection),所以API中的名词也应该使用复数。

 

具体细则:

1、使用名词而不是动词。举例来说,某个URL/cards/show/1,其中show是动词,这个URL就设计错了,正确的写法应该是/cards/1,然后用GET方法表示show。如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:POST /accounts/1/transfer/500/to/2。正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:POST /transaction?from=1&to=2&amount=500.00

2、使用复数名词。不要混淆名词单数和复数,为了保持简单,只对所有资源使用复数。

举例:

/cars 而不是 /car

/users 而不是 /user

/products 而不是 /product

/settings 不是 /setting

3、使用子资源表达关系。如果一个资源与另外一个资源有关系,使用子资源。

举例:

GET /cars/911/drivers/      返回 car 911的所有司机

GET /cars/911/drivers/8    返回 car 9118号司机

 

5HTTP动词(HTTP Verbs)

对于资源的具体操作类型,由HTTP动词表示。常用的HTTP动词有下面五个:

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

注:Get方法和查询参数不应该涉及状态改变。使用PUT, POST DELETE方法而不是 GET 方法来改变状态。

 

6、过滤信息(Filtering

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。为集合提供过滤、排序、选择和分页等功能。

下面是一些常见的参数。

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

注:

移动端能够显示其中一些字段,它们其实不需要一个资源的所有字段,给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。

为了将总数发给客户端,使用订制的HTTP头: X-Total-Count.

 

7、状态码(Status Codes

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

8、错误处理(Error handling

如果状态码是4xx,就应该向用户返回出错信息。尽量使用详细的错误包装信息:

{

  "errors": [

   {

    "userMessage": "Sorry, the requested resource does not exist",

    "internalMessage": "No car found in the database",

    "code": 4xx,

    "more info": "http://dev.example.com/api/v1/errors/12345"

   }

  ]

}

9、返回结果(Response

服务器返回的数据格式,应该尽量使用JSON,避免使用XML。针对不同操作,服务器向用户返回的结果应该符合以下规范。

10、使用HATEOASHypermedia API

RESTful API最好使用Hypermedia as the Engine of Application State(超媒体作为应用状态的引擎),即返回结果中提供链接,连向其他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表示返回类型。

11认证(Authentication

API的身份认证尽量使用OAuth 2.0框架。

 

 

 

三、Swagger API标准

API的文档管理和信息描述,将使用Swagger进行。

Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。Swagger的目标是对REST API定义一个标准的和语言无关的接口,可让人和计算机无需访问源码、文档或网络流量监测就可以发现和理解服务的能力。

Swagger规范定义了一组描述一个API所需的文件格式,类似于描述Web服务的WSDL。通过Swagger进行REST API的正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger消除了调用服务时可能会有的猜测。

注:Microsoft,PayPal等公司也已经引入Swagger 来定义自己的REST API 文档。

Swagger API可以使用YAMLJSON来表示。

Swagger这类API文档工具可以满足下列需求:

通常情况下,APISwagger描述为JSON文件,也可使用YAML描述的文件。

Swagger文件示例:

{

  "swagger": "2.0",

  "info": {

    "version": "1.0.0",

    "title": "Swagger Petstore (Simple)",

    "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",

    "termsOfService": "http://helloreverb.com/terms/",

    "contact": {

      "name": "Swagger API team",

      "email": "foo@example.com",

      "url": "http://swagger.io"

    },

    "license": {

      "name": "MIT",

      "url": "http://opensource.org/licenses/MIT"

    }

  },

  "host": "petstore.swagger.io",

  "basePath": "/api",

  "schemes": [

    "http"

  ],

  "consumes": [

    "application/json"

  ],

  "produces": [

    "application/json"

  ],

  "paths": {

    "/pets": {

      "get": {

        "description": "Returns all pets from the system that the user has access to",

        "operationId": "findPets",

        "produces": [

          "application/json",

          "application/xml",

          "text/xml",

          "text/html"

        ],

        "parameters": [

          {

            "name": "tags",

            "in": "query",

            "description": "tags to filter by",

            "required": false,

            "type": "array",

            "items": {

              "type": "string"

            },

            "collectionFormat": "csv"

          },

          {

            "name": "limit",

            "in": "query",

            "description": "maximum number of results to return",

            "required": false,

            "type": "integer",

            "format": "int32"

          }

        ],

        "responses": {

          "200": {

            "description": "pet response",

            "schema": {

              "type": "array",

              "items": {

                "$ref": "#/definitions/pet"

              }

            }

          },

          "default": {

            "description": "unexpected error",

            "schema": {

              "$ref": "#/definitions/errorModel"

            }

          }

        }

      },

      "post": {

        "description": "Creates a new pet in the store.  Duplicates are allowed",

        "operationId": "addPet",

        "produces": [

          "application/json"

        ],

        "parameters": [

          {

            "name": "pet",

            "in": "body",

            "description": "Pet to add to the store",

            "required": true,

            "schema": {

              "$ref": "#/definitions/newPet"

            }

          }

        ],

        "responses": {

          "200": {

            "description": "pet response",

            "schema": {

              "$ref": "#/definitions/pet"

            }

          },

          "default": {

            "description": "unexpected error",

            "schema": {

              "$ref": "#/definitions/errorModel"

            }

          }

        }

      }

    },

    "/pets/{id}": {

      "get": {

        "description": "Returns a user based on a single ID, if the user does not have access to the pet",

        "operationId": "findPetById",

        "produces": [

          "application/json",

          "application/xml",

          "text/xml",

          "text/html"

        ],

        "parameters": [

          {

            "name": "id",

            "in": "path",

            "description": "ID of pet to fetch",

            "required": true,

            "type": "integer",

            "format": "int64"

          }

        ],

        "responses": {

          "200": {

            "description": "pet response",

            "schema": {

              "$ref": "#/definitions/pet"

            }

          },

          "default": {

            "description": "unexpected error",

            "schema": {

              "$ref": "#/definitions/errorModel"

            }

          }

        }

      },

      "delete": {

        "description": "deletes a single pet based on the ID supplied",

        "operationId": "deletePet",

        "parameters": [

          {

            "name": "id",

            "in": "path",

            "description": "ID of pet to delete",

            "required": true,

            "type": "integer",

            "format": "int64"

          }

        ],

        "responses": {

          "204": {

            "description": "pet deleted"

          },

          "default": {

            "description": "unexpected error",

            "schema": {

              "$ref": "#/definitions/errorModel"

            }

          }

        }

      }

    }

  },

  "definitions": {

    "pet": {

      "type": "object",

      "required": [

        "id",

        "name"

      ],

      "properties": {

        "id": {

          "type": "integer",

          "format": "int64"

        },

        "name": {

          "type": "string"

        },

        "tag": {

          "type": "string"

        }

      }

    },

    "newPet": {

      "type": "object",

      "required": [

        "name"

      ],

      "properties": {

        "id": {

          "type": "integer",

          "format": "int64"

        },

        "name": {

          "type": "string"

        },

        "tag": {

          "type": "string"

        }

      }

    },

    "errorModel": {

      "type": "object",

      "required": [

        "code",

        "message"

      ],

      "properties": {

        "code": {

          "type": "integer",

          "format": "int32"

        },

        "message": {

          "type": "string"

        }

      }

    }

  }

}

 

 

 

Swagger文件的构成以及规范信息,在Swagger官方的规范指导《Swagger RESTful API文档规范》中有详细描述,请参看。

http://swagger.io/specification/

 

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!