1.1. API 基础

CouchDB API 是与 CouchDB 实例交互的主要方法。请求使用 HTTP 发送,用于从数据库请求信息、存储新数据以及执行视图和对存储在文档中的信息的格式化操作。

对 API 的请求可以根据您访问的 CouchDB 系统的不同区域以及用于发送请求的 HTTP 方法进行分类。不同的方法意味着不同的操作,例如,从数据库检索信息通常由 GET 操作处理,而更新则由 POSTPUT 请求处理。不同方法所需提供的的信息之间存在一些差异。有关基本 HTTP 方法和请求结构的指南,请参见 请求格式和响应

对于几乎所有操作,提交的数据和返回的数据结构都在 JavaScript 对象表示法 (JSON) 对象中定义。有关 JSON 内容和数据类型的基本信息,请参见 JSON 基础

访问 CouchDB API 时发生的错误使用标准 HTTP 状态码报告。有关 CouchDB 返回的通用代码的指南,请参见 HTTP 状态码

访问 CouchDB API 的特定区域时,将提供有关 HTTP 方法和请求、JSON 结构以及错误代码的具体信息和示例。

1.1.1. 请求格式和响应

CouchDB 支持以下 HTTP 请求方法

  • GET

    请求指定项目。与正常的 HTTP 请求一样,URL 的格式定义了返回的内容。在 CouchDB 中,这可以包括静态项目、数据库文档以及配置和统计信息。在大多数情况下,信息以 JSON 文档的形式返回。

  • HEAD

    HEAD 方法用于获取 GET 请求的 HTTP 头部,但不包括响应主体。

  • POST

    上传数据。在 CouchDB 中,POST 用于设置值,包括上传文档、设置文档值以及启动某些管理命令。

  • PUT

    用于放置指定资源。在 CouchDB 中,PUT 用于创建新对象,包括数据库、文档、视图和设计文档。

  • DELETE

    删除指定的资源,包括文档、视图和设计文档。

  • COPY

    一种特殊方法,可用于复制文档和对象。

如果您对不支持的 HTTP 请求类型使用不支持指定类型的 URL,则将返回 405 - Method Not Allowed,其中列出了支持的 HTTP 方法。例如

{
    "error":"method_not_allowed",
    "reason":"Only GET,HEAD allowed"
}

1.1.2. HTTP 头部

由于 CouchDB 使用 HTTP 进行所有通信,因此您需要确保提供正确的 HTTP 头部(并在检索时进行处理),以便获得正确的格式和编码。不同的环境和客户端对这些 HTTP 头部(尤其是在不存在时)的影响会更加严格。在可能的情况下,您应该尽可能具体。

1.1.2.1. 请求头部

  • Accept

    指定服务器返回的已接受数据类型列表(即客户端接受/理解的数据类型)。格式应为一个或多个 MIME 类型列表,以冒号分隔。

    对于大多数请求,定义应为 JSON 数据 (application/json)。对于附件,您可以显式指定 MIME 类型,或者使用 */* 指定支持所有文件类型。如果未提供 Accept 头部,则假定为 */* MIME 类型(即客户端接受所有格式)。

    在 CouchDB 的查询中,使用 Accept 不是必需的,但强烈建议使用,因为它有助于确保返回的数据可以由客户端处理。

    如果您使用 Accept 头部指定数据类型,CouchDB 将在返回的 Content-type 头部字段中使用指定的类型。例如,如果您在请求的 Accept 中显式请求 application/json,则返回的 HTTP 头部将在返回的 Content-type 字段中使用该值。

    例如,在发送没有显式 Accept 头部的请求时,或者在指定 */*

    GET /recipes HTTP/1.1
    Host: couchdb:5984
    Accept: */*
    

    返回的头部为

    HTTP/1.1 200 OK
    Server: CouchDB (Erlang/OTP)
    Date: Thu, 13 Jan 2011 13:39:34 GMT
    Content-Type: text/plain;charset=utf-8
    Content-Length: 227
    Cache-Control: must-revalidate
    

    注意

    返回的内容类型为 text/plain,即使请求返回的信息为 JSON 格式。

    显式指定 Accept 头部

    GET /recipes HTTP/1.1
    Host: couchdb:5984
    Accept: application/json
    

    返回的头部包括 application/json 内容类型

    HTTP/1.1 200 OK
    Server: CouchDB (Erlang/OTP)
    Date: Thu, 13 Jan 2013 13:40:11 GMT
    Content-Type: application/json
    Content-Length: 227
    Cache-Control: must-revalidate
    
  • Content-type

    指定请求中提供的信息的内容类型。规范使用 MIME 类型规范。对于大多数请求,这将是 JSON (application/json)。对于某些设置,MIME 类型将为纯文本。上传附件时,它应该是附件的相应 MIME 类型或二进制 (application/octet-stream)。

    强烈建议在请求中使用 Content-type

1.1.2.2. 响应头部

响应头部由服务器在发送回内容时返回,包括许多不同的头部字段,其中许多是标准 HTTP 响应头部,对 CouchDB 操作没有意义。下面列出了对 CouchDB 重要的响应头部列表。

  • Cache-control

    缓存控制 HTTP 响应头部为客户端缓存机制提供有关如何处理返回信息的建议。CouchDB 通常返回 must-revalidate,这表示应尽可能重新验证信息。这用于确保正确更新内容的动态特性。

  • Content-length

    返回内容的长度(以字节为单位)。

  • Content-type

    指定返回数据的 MIME 类型。对于大多数请求,返回的 MIME 类型为 text/plain。所有文本都以 Unicode (UTF-8) 编码,并在返回的 Content-type 中显式声明,为 text/plain;charset=utf-8

  • Etag

    Etag HTTP 头部字段用于显示文档或视图的修订版。

    ETag 已分配给映射/归约组(单个设计文档中的视图集合)。对这些视图的任何索引进行的任何更改都会为单个设计文档中的所有视图 URL 生成新的 ETag,即使该特定视图的结果没有更改也是如此。

    每个 _view URL 都有自己的 ETag,该 ETag 仅在影响该索引的数据库发生更改时才会更新。如果该特定视图的索引没有更改,则该视图会保留原始的 ETag 头部(因此更频繁地发送回 304 - Not Modified)。

  • Transfer-Encoding

    如果响应使用编码,则将在该头部字段中指定。

    Transfer-Encoding: chunked 表示响应以分块形式发送,这是一种称为 分块传输编码 的方法。当 CouchDB 事先不知道要发送的数据大小(例如,变更馈送)时,就会使用这种方法。

  • X-CouchDB-Body-Time

    接收请求主体所花费的时间(以毫秒为单位)。

    当请求中包含主体内容时可用。

  • X-Couch-Request-ID

    请求的唯一标识符。

1.1.3. JSON 基础

大多数对 CouchDB 的请求和响应使用 JavaScript 对象表示法 (JSON) 来格式化数据和响应的内容和结构。

使用 JSON 是因为它是 Web 浏览器中处理数据的最简单、最容易的解决方案,因为 JSON 结构可以在 Web 浏览器环境中被评估并用作 JavaScript 对象。JSON 还与 CouchDB 中使用的服务器端 JavaScript 集成。

JSON 支持与 JavaScript 支持的相同基本类型,它们是

  • 数组 - 由方括号括起来的多个值的列表。例如

    ["one", "two", "three"]
    
  • 布尔值 - truefalse 值。您可以直接使用这些字符串。例如

    { "value": true}
    
  • 数字 - 整数或浮点数。

  • 对象 - 一组键值对(即关联数组或哈希)。键必须是字符串,但值可以是任何支持的 JSON 值。例如

    {
        "servings" : 4,
        "subtitle" : "Easy to make in advance, and then cook when ready",
        "cooktime" : 60,
        "title" : "Chicken Coriander"
    }
    

    在 CouchDB 中,JSON 对象用于表示各种结构,包括主要的 CouchDB 文档。

  • 字符串 - 这应该用双引号括起来,并支持 Unicode 字符和反斜杠转义。例如

    "A String"
    

通过 JavaScript 中的 JSON.parse() 函数或通过各种库将 JSON 解析为 JavaScript 对象,这些库将为您执行内容解析为 JavaScript 对象的操作。许多语言(包括 Perl、Python、Ruby、Erlang 等)都提供用于解析和生成 JSON 的库。

警告

应注意确保您的 JSON 结构有效,无效结构会导致 CouchDB 返回 HTTP 状态码 500(服务器错误)。

1.1.3.1. 数字处理

刚接触计算机数字处理的开发人员和用户在期望以 JSON 格式存储的数字不一定按字符逐个比较返回为相同的数字时,经常会遇到意外情况。

在 JSON 中定义的任何包含小数点或指数的数字都将通过 Erlang VM 对“双精度”数据类型的理解。在视图中使用的任何数字都将通过视图服务器对数字的理解(常见的 JavaScript 案例意味着即使是整数也会通过双精度,因为 JavaScript 对数字的定义)。

考虑我们写入 CouchDB 的这个文档

{
    "_id":"30b3b38cdbd9e3a587de9b8122000cff",
    "number": 1.1
}

现在让我们从 CouchDB 中读取该文档

{
    "_id":"30b3b38cdbd9e3a587de9b8122000cff",
    "_rev":"1-f065cee7c3fd93aa50f6c97acde93030",
    "number":1.1000000000000000888
}

发生的情况是 CouchDB 正在更改对它接收到的内容进行解码的结果的文本表示,将其转换为某种数字格式。在大多数情况下,这是一种 IEEE 754 双精度浮点数,这与几乎所有其他语言使用的浮点数完全相同。

Erlang 与其他语言略有不同,它不会尝试美化输出结果以使用最少的字符数。例如,这就是为什么我们有这种关系的原因

ejson:encode(ejson:decode(<<"1.1">>)).
<<"1.1000000000000000888">>

这里令人困惑的是,在内部,这两种格式解码为相同的 IEEE-754 表示。更重要的是,当通过我们所知的几乎所有主要解析器传递时,它将解码为相当接近的表示。

虽然我们只讨论了文本表示发生变化的情况,但另一个重要的情况是,当输入值包含的精度超过双精度所能实际表示的精度时。(您可以争辩说,如果您不接受数字存储在双精度中,那么这种情况实际上是“丢失”数据)。

以下是在作者机器上的一些更常见的 JSON 库的日志

Ejson(CouchDB 的当前解析器)在 CouchDB sha 168a663b

$ ./utils/run -i
Erlang R14B04 (erts-5.8.5) [source] [64-bit] [smp:2:2] [rq:2]
[async-threads:4] [hipe] [kernel-poll:true]

Eshell V5.8.5  (abort with ^G)
1> ejson:encode(ejson:decode(<<"1.01234567890123456789012345678901234567890">>)).
<<"1.0123456789012346135">>
2> F = ejson:encode(ejson:decode(<<"1.01234567890123456789012345678901234567890">>)).
<<"1.0123456789012346135">>
3> ejson:encode(ejson:decode(F)).
<<"1.0123456789012346135">>

节点

$ node -v
v0.6.15
$ node
JSON.stringify(JSON.parse("1.01234567890123456789012345678901234567890"))
'1.0123456789012346'
var f = JSON.stringify(JSON.parse("1.01234567890123456789012345678901234567890"))
undefined
JSON.stringify(JSON.parse(f))
'1.0123456789012346'

Python

$ python
Python 2.7.2 (default, Jun 20 2012, 16:23:33)
[GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
import json
json.dumps(json.loads("1.01234567890123456789012345678901234567890"))
'1.0123456789012346'
f = json.dumps(json.loads("1.01234567890123456789012345678901234567890"))
json.dumps(json.loads(f))
'1.0123456789012346'

Ruby

$ irb --version
irb 0.9.5(05/04/13)
require 'JSON'
=> true
JSON.dump(JSON.load("[1.01234567890123456789012345678901234567890]"))
=> "[1.01234567890123]"
f = JSON.dump(JSON.load("[1.01234567890123456789012345678901234567890]"))
=> "[1.01234567890123]"
JSON.dump(JSON.load(f))
=> "[1.01234567890123]"

注意

关于 Ruby 的一个小插曲,它需要一个顶级对象或数组,所以我只是将该值包装起来。应该很明显,它不会影响解析数字的结果。

Spidermonkey

$ js -h 2>&1 | head -n 1
JavaScript-C 1.8.5 2011-03-31
$ js
js> JSON.stringify(JSON.parse("1.01234567890123456789012345678901234567890"))
"1.0123456789012346"
js> var f = JSON.stringify(JSON.parse("1.01234567890123456789012345678901234567890"))
js> JSON.stringify(JSON.parse(f))
"1.0123456789012346"

如您所见,它们的行为几乎都相同,除了 Ruby 实际上似乎比其他库丢失了一些精度。

敏锐的观察者会注意到,ejson(CouchDB JSON 库)报告了额外的三位数字。虽然这很容易让人认为是由于某种内部差异造成的,但这只是上面描述的 1.1 输入的更具体情况。

这里要了解的重要一点是,双精度只能保存有限数量的值。我们在这里做的是生成一个字符串,当通过“标准”浮点解析算法(即 strtod)传递时,它将在内存中生成与我们开始时相同的位模式。或者,稍微不同的是,JSON 序列化数字中的字节被选择为引用双精度可以表示的单个特定值。

要理解的重要一点是,我们正在将一个无限集映射到一个有限集。一个容易理解这一点的方法是思考这一点

1.0 == 1.00 == 1.000 = 1.(infinite zeros)

显然,计算机无法保存无限字节,因此我们必须将无限大小的集合缩减为可以简洁表示的有限集合。

其他 JSON 库正在玩的游戏仅仅是

“我必须使用多少个字符才能选择双精度的这个特定值”

并且这个游戏有很多很多微妙的细节,在没有付出大量努力的情况下很难在 C 中复制(Python 花了一年多的时间才用他们精密的构建系统解决了这个问题,这些系统可以在许多不同的架构上自动运行)。

希望我们已经证明,CouchDB 通过更改输入并没有做任何“奇怪”的事情。它的行为与任何其他常见的 JSON 库的行为相同,只是它没有美化输出。

另一方面,如果您确实处于 IEEE-754 双精度不是您数字的令人满意的数据类型的位置,那么答案如前所述,不要将您的数字通过这种表示传递。在 JSON 中,这是通过将它们编码为字符串或使用整数类型来实现的(尽管如果您使用的是与正常不同的整数表示的平台,例如 JavaScript,那么整数类型仍然会咬您)。

可以轻松找到更多信息,包括 浮点指南David Goldberg 的参考

此外,如果有人真的有兴趣改变这种行为,我们非常乐意接受对 jiffy(理论上将在我们更新构建系统时替换 ejson)的贡献。我们寻找灵感的来源是 TCL 和 Python。如果您知道这种浮点打印算法的良好实现,请告诉我们。

1.1.4. HTTP 状态码

由于与 CouchDB 的接口通过 HTTP 工作,因此错误代码和状态使用 HTTP 状态码编号和响应数据主体中的相应数据相结合来报告。

下面列出了 CouchDB 返回的错误代码以及相关错误的通用描述。特定请求类型的不同状态码的含义在相应的 API 调用参考中提供。

  • 200 - OK

    请求已成功完成。

  • 201 - Created

    文档已成功创建。

  • 202 - Accepted

    请求已被接受,但相应的操作可能尚未完成。这用于后台操作,例如数据库压缩。

  • 304 - Not Modified

    请求的附加内容未被修改。这与 ETag 系统一起使用,用于识别返回的信息的版本。

  • 400 - Bad Request

    请求结构错误。错误可能表示请求 URL、路径或标头存在错误。提供的 MD5 哈希和内容之间的差异也会触发此错误,因为这可能表示消息已损坏。

  • 401 - Unauthorized

    使用提供的授权无法获得请求的项目,或者未提供授权。

  • 403 - Forbidden

    请求的项目或操作被禁止。

  • 404 - Not Found

    请求的内容未找到。如果可用,内容将包含更多信息,作为 JSON 对象。该结构将包含两个键,errorreason。例如

    {"error":"not_found","reason":"no_db_file"}
    
  • 405 - Method Not Allowed

    使用请求的 URL 的无效 HTTP 请求类型发出了请求。例如,您请求了 PUT,而需要的是 POST。此类错误也可能由无效的 URL 字符串触发。

  • 406 - Not Acceptable

    服务器不支持请求的内容类型。

  • 409 - Conflict

    请求导致更新冲突。

  • 412 - Precondition Failed

    来自客户端的请求标头与服务器的功能不匹配。

  • 413 - Request Entity Too Large

    文档超过了配置的 couchdb/max_document_size 值,或者整个请求超过了 chttpd/max_http_request_size 值。

  • 415 - Unsupported Media Type

    支持的内容类型以及正在请求或提交的信息的内容类型表明不支持该内容类型。

  • 416 - Requested Range Not Satisfiable

    服务器无法满足请求头中指定的范围。

  • 417 - Expectation Failed

    批量加载操作失败。

  • 500 - Internal Server Error

    请求无效,可能是因为提供的 JSON 无效,或者请求中提供了无效的信息。

  • 503 - Service Unavailable

    目前无法处理请求,可能是因为集群过载、正在维护或其他原因。请求可以稍后重试,可能需要几分钟。