HTTP API 设计格式

发布时间: 更新时间: 总字数:10750 阅读时间:22m 作者: IP属地: 分享 复制网址

定义 HTTP API 设计格式,使 JSON 格式的 API 更加符合团队的需求。

扩展

  • 建议采用 OpenAPI 规范 定义 API
    • OpenAPI 规范(以前称为Swagger规范)是一种机器可读接口文件的规范,用于描述、生成、使用和可视化 RESTful Web 服务
    • httpbin.org 示例
    • Mocky 世界上最简单和最快的工具Mock的API
    • YApi 是一个可本地部署的、打通前后端及QA的、可视化的接口管理平台

介绍

  • JSON API 是数据交互规范,用以定义客户端如何获取与修改资源,以及服务器如何响应对应请求。
  • JSON API 设计用来最小化请求的数量,以及客户端与服务器间传输的数据量。在高效实现的同时,无需牺牲可读性、灵活性和可发现性。
  • JSON API需要使用JSON API媒体类型(application/vnd.api+json) 进行数据交互。
  • JSON API服务器支持通过GET方法获取资源。而且必须独立实现HTTP POST, PUT和DELETE方法的请求响应,以支持资源的创建、更新和删除。
  • JSON API服务器也可以选择性支持HTTP PATCH方法 [RFC5789]和JSON Patch格式 [RFC6902],进行资源修改。JSON Patch支持是可行的,因为理论上来说,JSON API通过单一JSON 文档,反映域下的所有资源,并将JSON文档作为资源操作介质。在文档顶层,依据资源类型分组。每个资源都通过文档下的唯一路径辨识。

规则约定

文档中的关键字"MUST"、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”, and “OPTIONAL” 依据RFC2119规范解释。

文档结构

这一章节描述JSON API文档结构,通过媒体类型application/vnd.api+json标示。JSON API文档使用javascript 对象(JSON)[RFC4627]定义。

尽管同种媒体类型用以请求和响应文档,但某些特性只适用于其中一种。差异在下面呈现。

Top Level

JSON 对象必须位于每个JSON API文档的根级。这个对象定义文档的“top level”。

文档的top level必须包含请求资源或者请求资源集合的实例 (即主要资源)。

主要资源应该以资源类型或者通用键"data"索引.

  • "meta": 资源的元信息,比如分页.
  • "links": 扩展资源关联URLs的URL模板.
  • "linked": 资源对象集合,按照类型分组,链接到主要资源或彼此(即链接资源)

资源表示

这一章节描述JSON API文档如何表示资源。适用于主要资源和链接资源。

个体资源表示

个体资源使用单一“资源对象”(如下描述)或者包含资源ID(如下描述)的字符串表示。

The following post is represented as a resource object: 下面的post表示一个资源对象:

{
  "posts": {
    "id": "1",
    // ... attributes of this post
  }
}

这个post用ID简单地表示:

{
  "posts": "1"
}

资源集合表示

任意数量资源的集合应该使用资源对象数组,或者IDs数组,或者一个简单的”集合对象“表示。

下面这个post使用资源对象数组表示:

{
  "posts": [{
    "id": "1"
    // ... attributes of this post
  }, {
    "id": "2"
    // ... attributes of this post
  }]
}

这个posts使用IDs数组表示:

{
  "posts": ["1", "2"]
}

这些comments使用单一集合对象表示:

{
  "comments": {
    "href": "http://example.com/comments/5,12,17,20",
    "ids": [ "5", "12", "17", "20" ],
    "type": "comments"
  }
}

多资源对象

多个资源对象有相同的内部结构,不管他们表示主要资源还是链接资源。

下面是一个可能出现在文档中的post(即”posts"类型的一个资源):

{
  "posts": {
    "id": "1",
    "title": "Rails is Omakase"
  }
}

在上面这个例子中,post的资源对象比较简单:

//...
  {
    "id": "1",
    "title": "Rails is Omakase"
  }
//...

这一章节专注于资源对象,在完整JSON API文档上下文环境之外。

资源属性

资源对象有四个保留字:

  • "id"
  • "type"
  • "href"
  • "links"

资源对象中的其它键表示一个“属性”。一个属性值可以是任何JSON值。

资源 IDs

每一个资源对象应该有一个唯一标示符,或者ID。如下所示,IDs可由服务器或者客户端指定,and SHOULD be unique for a resource when scoped by its type. ID应该使用 "id"键表示,值必须是字符串,且只包含字母,数字,连字符和下划线。

URL 模板可以使用IDs来获取关联资源,如下所示。

在特殊场景下,客户端与服务器之间的唯一标识符信息非必要,JSON API允许缺省IDs。

资源类型

每个资源对象的类型通常由它所在的上下文环境决定。如上面讨论,资源对象在文档中通过类型索引。

每一个资源对象可能包含 "type" 键来显示指定类型。

当资源的类型在文档中未声明时,"type"键不可缺省。

资源 URLs

每一个资源的URL可能使用"href"键声明。资源URLs应该由服务器指定,因此通常包含在响应文档中。

//...
  [{
    "id": "1",
    "href": "http://example.com/comments/1",
    "body": "Mmmmmakase"
  }, {
    "id": "2",
    "href": "http://example.com/comments/2",
    "body": "I prefer unagi"
  }]
//...

服务器对特定URLGET请求,响应内容必须包含资源。

通常在响应文档的根层级声明URL 模板会更高效,而不是在每一个资源对象内声明独立的URLs。

资源关联

"links"键的值是一个表示链接资源的JSON对象,通过关联名索引。

举例来说,下面的post与一个author和一个comments集合相关联:

//...
  {
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "author": "9",
      "comments": [ "5", "12", "17", "20" ]
    }
  }
//...
单对象关联

单对象关联必须使用上面所述单资源形式的一种来表示。

举例来说,下面的post与一个author相关联,通过ID标示:

//...
  {
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "author": "17"
    }
  }
//...

下面是一个示例,链接的author用一个资源对象表示:

//...
  {
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "author": {
        "href": "http://example.com/people/17",
        "id": "17",
        "type": "people"
      }
    }
  }
//...

空白的单对象关联应该用null值表示。举例来说,下面的post没有关联author:

//...
  {
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "author": null
    }
  }
//...
多对象关联

多对象关联必须使用上述资源集合形式的一种来表示。

举例来说,下面的post与多个comments关联,通过IDs标示:

//...
  {
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "comments": [ "5", "12", "17", "20" ]
    }
  }
//...

这是一个使用集合对象链接的comments数组:

//...
  {
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "comments": {
        "href": "http://example.com/comments/5,12,17,20",
        "ids": [ "5", "12", "17", "20" ],
        "type": "comments"
      }
    }
  }
//...

空白的多对象关联应该使用空数组表示。举例来说,下面的post没有comments:

//...
  {
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "comments": []
    }
  }
//...

集合对象

“集合对象”包含一个或多个元素:

  • "ids" - 关联资源的IDs数组。
  • "type" - 资源类型
  • "href" - 关联资源的URL(适用于响应文档)。

提供包含href属性集合对象的服务器,必须响应特定URL GET 请求,响应内容包含资源对象集合的关联资源。

URL模板

顶层的 "links" 对象可用来声明URL模板,从而依据资源对象类型获取最终URLs。

举例说明:

{
  "links": {
    "posts.comments": "http://example.com/comments?posts={posts.id}"
  },
  "posts": [{
    "id": "1",
    "title": "Rails is Omakase"
  }, {
    "id": "2",
    "title": "The Parley Letter"
  }]
}

在这个示例中,请求http://example.com/comments?posts=1 将会得到"Rails is Omakase"的comments,请求http://example.com/comments?posts=2 将会得到 "The Parley Letter"的comments.

下面是另外一个示例:

{
  "links": {
    "posts.comments": "http://example.com/comments/{posts.comments}"
  },
  "posts": [{
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "comments": [ "1", "2", "3", "4" ]
    }
  }]
}

在这个示例中,处理每个post"links"区块内的特定数组,以扩展posts.comments变量。URI模板规范 [RFC6570]声明默认处理方式,使用%编码(即encodeURIComponent() javascript原生方法)编码每一个元素,然后用逗号连接。在这个示例中,请求http://example.com/comments/1,2,3,4 ,将会获取一个comments列表。

顶层 "links"对象具有以下行为:

  • 每个键使用点分隔路径,指向重复的关联。路径以特定资源类型名开头,遍历相关的资源。举例来 说,"posts.comments"指向每个"posts"对象的"comments"关联.
  • 每个键的值作为URL模板处理。
  • 每个path指向的资源,就像是使用实际指定的非URL值扩展URL模板形成的关联。

这是另外一个使用单对象关联的示例:

{
  "links": {
    "posts.author": "http://example.com/people/{posts.author}"
  },
  "posts": [{
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "author": "12"
    }
  }, {
    "id": "2",
    "title": "The Parley Letter",
    "links": {
      "author": "12"
    }
  }, {
    "id": "3",
    "title": "Dependency Injection is Not a Virtue",
    "links": {
      "author": "12"
    }
  }]
}

这个实例中,三个posts指向author的URL都为http://example.com/people/12.

顶层URL模板允许指定关联作为IDs,但是不要求客户端硬编码来获取URLs的信息.

注意:为防止冲突,单独资源对象的links对象优先级高于顶层的links对象。

复合文档

为减少HTTP请求,响应需要返回所请求的主要资源,同时可以选择性的包含链接资源。这样的响应称作“复合文档”。

在复合文档中,链接资源必须作为资源对象,包含在文档顶层"linked"对象中,依据类型,组合到不同数组中。

每个关联的类型,可以在资源层级,或顶层"links"对象层级,使用"type"键指定。能够辅助客户端查询链接资源对象。

{
  "links": {
    "posts.author": {
      "href": "http://example.com/people/{posts.author}",
      "type": "people"
    },
    "posts.comments": {
      "href": "http://example.com/comments/{posts.comments}",
      "type": "comments"
    }
  },
  "posts": [{
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "author": "9",
      "comments": [ "1", "2", "3" ]
    }}, {
    "id": "2",
    "title": "The Parley Letter",
    "links": {
      "author": "9",
      "comments": [ "4", "5" ]
   }}, {
    "id": "1",
    "title": "Dependency Injection is Not a Virtue",
    "links": {
      "author": "9",
      "comments": [ "6" ]
    }
  }],
  "linked": {
    "people": [{
      "id": "9",
      "name": "@d2h"
    }],
    "comments": [{
      "id": "1",
      "body": "Mmmmmakase"
    }, {
      "id": "2",
      "body": "I prefer unagi"
    }, {
      "id": "3",
      "body": "What's Omakase?"
    }, {
      "id": "4",
      "body": "Parley is a discussion, especially one between enemies"
    }, {
      "id": "5",
      "body": "The parsley letter"
    }, {
      "id": "6",
      "body": "Dependency Injection is Not a Vice"
    }]
  }
}

这种处理方式,保证随每个响应返回每个文档的单例,即使当相同的文档被多次引用时(这个实例中三个posts的author)。沿着这种方式,如果主要文档链接到另外的主要或链接文档,在"linked"对象中也不应该重复。

URLs

关联文档

确定API的URL结构时,考虑把所有的资源放置于单一“关联文档"是有用的,在关联文档中,每个资源都被分配唯一路径。在文档顶层,资源依据类型分组。在分类资源集合中,单独资源通过ID索引。其属性和links,依据资源对象结构,唯一分配。

关联文档的概念,用于为资源及资源关系确定合适的URLs。重要的一点,出于不同目标和限制,用于传输资源的不同文档中,关联文档的结构有轻微差异。例如,在关联文档中的资源集当做集合处理,因为元素必须通过ID访问,在传输文档中的资源集当做数组处理,因为顺序比较重要。

资源集合URLs

资源集合的URL应该依据资源类型确定。

例如,”photos”类型的资源集合应该使用这种URL:

/photos

单独资源URLs

资源集应该作为集合,依据资源ID索引。单独资源的URL通过为集合URL添加资源ID生成。

例如,ID为"1"的photo使用这种URL:

/photos/1

多个单独资源的URL通过为集合URL添加逗号分隔的资源IDs列表生成。

例如,IDs为"1", "2", "3"的photos使用这种URL:

/photos/1,2,3

替代性 URLs

资源的替代性URLs可以选择在响应中指定,或者通过"href"或URL模板指定。

关联 URLs

添加/links/<relationship-name>到资源URL后面,即得到访问特定资源的关联资源URL。相对路径与资源对象内部结构保持一致。

例如,photo的comments链接集使用这种URL:

/photos/1/links/comments

A photo’s reference to an individual linked photographer will have the URL: photo的photographer链接使用这种URL:

/photos/1/links/photographer

服务器使用单独资源响应单对象关联,使用资源集合响应多对象关联。

资源获取

资源,或者资源集合,通过向URL发出GET请求获取。

响应内容可以使用如下所示的特点,进一步细化。

过滤

服务器可以选择性支持,依据指定标准进行资源过滤。

通过向资源集合的基准URL添加过滤参数,来支持资源过滤。

例如,下面是请求与特定post关联的所有comments:

GET /comments?posts=1

使用这种方案,单一请求可以使用多过滤器:

GET /comments?posts=1&author=12

这种规范仅支持基于严格匹配的资源过滤。API允许使用的额外过滤器应该在它的侧写中指定。 (见 Extending)

内链资源

服务器可以选择性支持,返回包含主要资源和链接资源对象的复合文档。

默认情况下,后端返回链接主要资源的资源对象。

后端也可以基于请求中include的参数,支持自定义链接资源。参数应该指定一个或者多个,相对于主要资源的相对路径。如果指定参数值,只有请求的链接资源,应该随主要资源返回。

例如,comments可以通过post请求:

GET /posts/1?include=comments

为请求链接到其他资源的资源,需要指定每个关联的点分隔路径。

GET /posts/1?include=comments.author

注意:对comments.author的请求,在响应中不应该自动包含comments资源(尽管comments也需要显式查询,以获取authors响应请求)。

多链接资源可以使用点分隔列表请求:

GET /posts/1?include=author,comments,comments.author

稀疏字段

服务器可以选择性支持,仅返回资源对象的指定字段。

后端可以基于fields参数,以支持返回主要资源的指定字段。

GET /people?fields=id,name,age

后端可以基于fields[TYPE]参数,以支持返回任意类型资源的特定字段。

GET /posts?include=author&fields[posts]=id,title&fields[people]=id,name

若没有指定类型对象的字段,或者后端不支持fieldfields[TYPE]参数,后端会默认返回资源对象的所有字段。

后端可以选择总是返回有限的,未指定的字段集,例如id or href.

注意: fieldsfields[TYPE]不能混合使用。如果使用后者,那么必须与主要资源类型同时使用。

排序

服务器可以选择性支持,基于特定标准对资源集合排序。

后端基于sort参数,以支持主要资源类型的排序。

GET /people?sort=age

后端支持多字段排序,将sort值设置为点分隔值即可。排序标准用以获取特定顺序。

GET /people?sort=age,name

默认排序方式为升序排序。任意排序字段,使用-前缀指定降序排序。

GET /posts?sort=-created,title

上面的示例应该首先返回最新的posts。同一天创建的posts,依据title值进行字母升序排列。

后端基于sort[TYPE]参数,以支持对任意资源类型排序。

GET /posts?include=author&sort[posts]=-created,title&sort[people]=name

如果没有指定排序方式,或者后端不支持sortsort[TYPE],后端将会返回使用重复算法排序的资源对象。换言之,资源应该总是以相同顺序返回,即使排序规则没有指定。

注意:sortsort[TYPE]不能混用。如果使用后者,必须与主要资源一同使用。

创建,更新,删除资源

服务器可能支持资源获取,创建,更新和删除。

服务器允许单次请求,更新多个资源,如下所述。多个资源更新必须完全成功或者失败,不允许部分更新成功。

任何包含内容的请求,必须包含Content-Type:application/vnd.api+json请求头。

创建资源

支持资源创建的服务器,必须支持创建单独的资源,可以选择性支持一次请求,创建多个资源。

向表示待创建资源所属资源集的URL,发出POST请求,创建一个或多个资源。

创建单独资源

创建单独资源的请求必须包含单一主要资源对象。

例如,新photo可以通过如下请求创建:

POST /photos
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "photos": {
    "title": "Ember Hamster",
    "src": "http://example.com/images/productivity.png"
  }
}

创建多个资源

创建多个资源的请求必须包含主要主要资源集合。

例如,多个photos通过如下请求创建:

POST /photos
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "photos": [{
    "title": "Ember Hamster",
    "src": "http://example.com/images/productivity.png"
  }, {
    "title": "Mustaches on a Stick",
    "src": "http://example.com/images/mustaches.png"
  }]
}

响应

201 状态码

服务器依据[HTTP semantics](http://tools.ietf.org/html/draft-ietf- httpbis-p2-semantics-22#section-6.3)规范,响应成功的资源创建请求。

当一个或多个资源创建成功,服务器返回201 Created状态码。

响应必须包含Location头,用以标示请求创建所有资源的位置。

如果创建了单个资源,且资源对象包含href键,Location URL必须匹配href值。

响应必须含有一个文档,用以存储所创建的主要资源。如果缺失,客户端则判定资源创建时,传输的文档未经修改。

HTTP/1.1 201 Created
Location: http://example.com/photos/550e8400-e29b-41d4-a716-446655440000
Content-Type: application/vnd.api+json

{
  "photos": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "href": "http://example.com/photos/550e8400-e29b-41d4-a716-446655440000",
    "title": "Ember Hamster",
    "src": "http://example.com/images/productivity.png"
  }
}
其它响应

服务器可能使用其它HTTP错误状态码反映错误。客户端必须依据HTTP规范处理这些错误信息。如下所述,错误细节可能会一并返回。

客户端生成 IDs

请求创建一个或多个资源时,服务器可能接受客户端生成IDs。IDs必须使用"id"键来指定,其值必须正确生成,且为格式化的UUID

例如:

POST /photos
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "photos": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Ember Hamster",
    "src": "http://example.com/images/productivity.png"
  }
}

更新资源

支持资源更新的服务器必须支持单个资源的更新,可以选择性的支持单次请求更新多个资源。

向表示单独资源或多个单独资源的URL发出PUT请求,即可进行资源更新。

更新单独资源

为更新单独资源,向表示资源的URL发出PUT请求。请求必须包含一个顶层资源对象。

例如:

PUT /articles/1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "articles": {
    "id": "1",
    "title": "To TDD or Not"
  }
}

更新多个资源

向表示多个单独资源(不是全部的资源集合)的URL发出PUT请求,即可更新多个资源。请求必须包含顶层资源对象集合,且每个资源具有“id"元素。

例如:

PUT /articles/1,2
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "articles": [{
    "id": "1",
    "title": "To TDD or Not"
  }, {
    "id": "2",
    "title": "LOL Engineering"
  }]
}

更新属性

要更新资源的一个或多个属性,主要资源对象应该只包括待更新的属性。资源对象缺省的属性将不会更新。

例如,下面的PUT请求,仅会更新article的titletext属性。

PUT /articles/1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "articles": {
    "id": "1",
    "title": "To TDD or Not",
    "text": "TLDR; It's complicated... but check your test coverage regardless."
  }
}

更新关联

更新单对象关联

单对象关联更新,可以在PUT请求资源对象中包含links键,从而与其它属性一起更新。

例如,下面的PUT请求将会更新article的titleauthor属性:

PUT /articles/1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "articles": {
    "title": "Rails is a Melting Pot",
    "links": {
      "author": "1"
    }
  }
}

若要移除单对象关联,指定null作为值即可。

另外,单对象关联也可以通过它的关联URL访问。

向关联URL发出带有主要资源的POST请求,即可添加单对象关联。

例如:

POST /articles/1/links/author
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "people": "12"
}

向关联URL发出DELETE请求,即可删除单对象关联。例如:

DELETE /articles/1/links/author
更新多关联对象

更新多对象关联,可以在PUT请求中资源对象包含links键,从而与其它属性一起更新。

例如,下面PUT请求完全替换article的tags

PUT /articles/1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "articles": {
    "id": "1",
    "title": "Rails is a Melting Pot",
    "links": {
      "tags": ["2", "3"]
    }
  }
}

若要移除多对象关联,指定空数组[]为值即可。

在分布式系统中,完全替换一个数据集合并不总是合适。替换方案是允许单独的添加或移除关联。

为促进细化访问,多对象关联也可以通过关联URL访问。

向关联URL发出带有主要资源的POST请求,即可添加多对象关联。

POST /articles/1/links/comments
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "comments": ["1", "2"]
}

向关联URL发出DELETE请求,即可删除多对象对象关联。例如:

DELETE /articles/1/links/tags/1

向关联URL发出DELETE请求,即可删除多个多对象对象关联。例如:

DELETE /articles/1/links/tags/1,2

响应

204 No Content

如果更新成功,且客户端属性保持最新,服务器必须返回204 No Content状态码。适用于PUT请求,以及仅调整links,不涉及其它属性的POST, DELETE请求。

200 OK

如果服务器接受更新,但是在请求指定内容之外做了资源修改,必须响应200 OK以及更新的资源实例,像是向此URL发出GET请求。

其它响应

服务器使用其它HTTP错误状态码反映错误。客户端必须依据HTTP规范处理这些错误信息。如下所述,错误细节可能会一并返回。

资源删除

向资源URL发出DELETE请求即可删除单个资源。

DELETE /photos/1

服务器可以选择性的支持,在一个请求里删除多个资源。

DELETE /photos/1,2,3

响应

204 No Content

如果删除请求成功,服务器必须返回204 No Content 状态码。

其它响应

服务器使用其它HTTP错误状态码反映错误。客户端必须依据HTTP规范处理这些错误信息。如下所述,错误细节可能会一并返回。

Errors

错误对象是特殊化的资源对象,可能在响应中一并返回,用以提供执行操作遭遇问题的额外信息。在在JSON API文档顶层,"errors"对应值即为错误对象集合,此时文档不应该包含其它顶层资源。

错误对象可能有以下元素:

  • "id" - 特定问题的唯一标示符。
  • "href" - 提供特定问题更多细节的URI。
  • "status" - 适用于这个问题的HTTP状态码,使用字符串表示。
  • "code" - 应用特定的错误码,以字符串表示。
  • "title" - 简短的,可读性高的问题总结。除了国际化本地化处理之外,不同场景下,相同的问题,值是不应该变动的。
  • "detail" - 针对该问题的高可读性解释。
  • "links" - 可以在请求文档中取消应用的关联资源。
  • "path" - 关联资源中相关属性的相对路径。在单资源或单类型资源中出现的问题,这个值才是合适的。

额外的元素可以在错误对象中指定。

实现接口可以选择使用其它的errors媒体类型。

PATCH Support

JSON API服务器可以选择性支持,遵循JSON Patch规范[RFC6902]的HTTP PATCH请求。上面提到使用POST, PUTDELETE进行的操作,JSON Patch都拥有等效操作。从这里开始,PATCH请求发出的JSON Patch操作,都被简单的称作“PATCH“操作。

PATCH请求必须声明Content-Type:application/json-patch+json头。

PATCH操作必须作为数组发送,以遵循JSON Patch规范。服务器可能会限制顶层数组类型,顺序和操作数量。

请求 URLs

每个PATCH请求的URL应该映射待更新的资源或关联。

PATCH操作内的每个"path"应该相对于请求URL。请求URL和PATCH操作的"path"是附加的,合并后获取特定资源,集合,属性,或者关联目标。

PATCH操作可以在API的根URL使用。此时,PATCH操作的"path"必须包含完整的资源URL。API表示的任意资源都可以进行常规的"fire hose"更新。如上所述,服务器可能会限制类型,排序和批量操作数量。

创建资源with PATCH

要创建资源,执行"add"操作, "path"指向对应资源集合的末尾 ("/-")。"value"必须包含一个资源对象。

比如,新photo通过如下请求创建:

PATCH /photos
Content-Type: application/json-patch+json
Accept: application/json

[
  {
    "op": "add",
    "path": "/-",
    "value": {
      "title": "Ember Hamster",
      "src": "http://example.com/images/productivity.png"
    }
  }
]

更新属性with PATCH

要更新属性,执行"replace" 操作,"path"值指定属性名。

例如,下面请求只更新/photos/1src值。

PATCH /photos/1
Content-Type: application/json-patch+json

[
  {
  "op": "replace",
  "path": "/src",
  "value": "http://example.com/hamster.png"
 }
]

更新关联with PATCH

要更新关联,向对应的关联URL发出合适的PATCH操作即可。

服务器可能支持更高层级的更新,像资源的URL(甚至是API的根URL)。如上所述,请求URL和每个操作的"path"是附加的,合并后获得特定关联URL目标。

关联更新with PATCH

要更新单对象关联,对指向关联的URL和"path" 执行"replace"操作。

例如:下面请求更新article的author

PATCH /article/1/links/author
Content-Type: application/json-patch+json

[
 {
  "op": "replace",
  "path": "/",
  "value": "1"
 }
]

要移除单对象关联,对关联执行remove操作。例如:

PATCH /article/1/links/author
Content-Type: application/json-patch+json

[
  {
    "op": "remove",
    "path": "/"
  }
]

更新多对象关联 with PATCH

尽管在GET响应中,多对象关联以JSON数组形式表示,但是更新时更像是集合。

要添加元素到多对象关联,执行指向关联URL的"add" 请求。由于操作指向集合末尾, "path" 必须以"/-"结尾。

例如,考虑下面的GET请求:

GET /photos/1
Content-Type: application/vnd.api+json

{
  "links": {
    "comments": "http://example.com/comments/{comments}"
  },
  "photos": {
    "id": "1",
    "href": "http://example.com/photos/1",
    "title": "Hamster",
    "src": "images/hamster.png",
    "links": {
      "comments": [ "1", "5", "12", "17" ]
    }
  }
}

PATCH请求执行add操作,即可将comment 30 转移到这个photo。

PATCH /photos/1/links/comments
Content-Type: application/json-patch+json

[
  {
    "op": "add",
    "path": "/-",
    "value": "30"
  }
]

要移除多对象关联,对指向关联对象的URL执行"remove"操作。因为操作目标是元素集合,"path"必须以"/<id>"结尾。

比如,要移除photo的comment 5,执行"remove"操作:

PATCH /photos/1/links/comments
Content-Type: application/json-patch+json

[
  {
    "op": "remove",
    "path": "/5"
  }
]

删除资源with PATCH

要删除资源,对指向资源的 URL 和"path"执行 "remove" 操作。

例如,photo 1 能使用下面请求删除:

PATCH /photos/1
Content-Type: application/json-patch+json
Accept: application/vnd.api+json

[
  {
    "op": "remove",
    "path": "/"
  }
]

响应

204 No Content

PATCH请求成功,客户端当前的属性保持最新,服务器必须响应204 No Content 状态码。

200 OK

如果服务器接受更新,但是在请求指定内容之外做了资源修改,必须响应200 OK以及更新的资源实例。

服务器必须指定Content-Type:application/json头。响应内容必须包含JSON对象数组,每个对象必须遵循JSON API媒体类型(application/vnd.api+json)。数组中的响应对象必须有序,并且对应请求文档操作。

例如,一个请求以分离操作,创建两个photos。

PATCH /photos
Content-Type: application/json-patch+json
Accept: application/json

[
  {
    "op": "add",
    "path": "/-",
    "value": {
      "title": "Ember Hamster",
      "src": "http://example.com/images/productivity.png"
    }
  },
  {
    "op": "add",
    "path": "/-",
    "value": {
      "title": "Mustaches on a Stick",
      "src": "http://example.com/images/mustaches.png"
    }
  }
]

响应内容在数组中包含对应的JSON API文档。

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "photos": [{
      "id": "123",
      "title": "Ember Hamster",
      "src": "http://example.com/images/productivity.png"
    }]
  }, {
    "photos": [{
      "id": "124",
      "title": "Mustaches on a Stick",
      "src": "http://example.com/images/mustaches.png"
    }]
  }
]

其它响应

当服务器执行PATCH请求时出现一个或多个问题,应该在响应中指定最合适的HTTP状态码。客户端依据HTTP规范解析错误。

服务器可以选择在第一个问题出现时,立刻终止PATCH 操作,或者继续执行,遇到多个问题。例如,服务器可能多属性更新,然后返回在一个响应里返回多个校验问题。

当服务器单个请求遇到多个问题,响应中应该指定最通用可行的HTTP错误码。例如,400 Bad Request适用于多个4xx errors,500 Internal Server Error适用于多个5xx errors。

服务器可能会返回与每个操作对应的错误对象。服务器需要指定Content-Type:application/json头,响应体必须包含JSON对象数组,每个对象必须遵循JSON API媒体类型 (application/vnd.api+json)。数组中的响应对象,必须是有序,且与请求文档中的操作相对应。每个响应对象应该仅包含error对象,当错误发生时,没有操作会完全成功。每个特定操作的错误码,应该在每个error对象 "status" 元素反映。

HTTP 缓存

服务器可能会使用遵循HTTP 1.1规范的HTTP 缓存头 (ETag, Last-Modified)。

Header头规范

标准头

Content-Type: application/vnd.api+json
Accept: application/vnd.{团队名称}.{版本号: v1, v2, ...}+json

说明:

  • application/vnd.api+json : JSON API 在 IANA 机构完成注册的 MIME 类型
  • application/vnd.{团队名称}.{版本号: v1, v2, ...}+json 服务器接收类型

认证头

Authorization: Bearer {用户token}

其他需要注意的内容(建议)

User-Agent: {Agent info}

错误描述

{
   "message": "Validation Failed",
   "errors": [
     {
       "resource": "Issue",
       "field": "title",
       "code": "missing_field"
     }
   ]
 }

所有错误包括下面信息

  • missing: 说明某个字段的值代表的资源不存在
  • invalid: 某个字段的值非法,接口文档中会提供相应的信息
  • missing_field: 缺失某个必须的字段
  • already_exist: 发送的资源中的某个字段的值和服务器中已有的某个资源冲突,常见于某些值全局唯一的字段,比如 @ 用的用户名(这个错误我有纠结,因为其实有 409 状态码可以表示,但是在修改某个资源时,很一般显然请求中不止是一种错误,如果是 409 的话,多种错误的场景就不合适了)

参考

  1. https://github.com/justjavac/json-api-zh_CN
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数