Internet Engineering Task Force (IETF)                        N. Jenkins
Request for Comments: 8620                                      Fastmail
Category: Standards Track                                      C. Newman
ISSN: 2070-1721                                                   Oracle
                                                               July 2019
        
Internet Engineering Task Force (IETF)                        N. Jenkins
Request for Comments: 8620                                      Fastmail
Category: Standards Track                                      C. Newman
ISSN: 2070-1721                                                   Oracle
                                                               July 2019
        

The JSON Meta Application Protocol (JMAP)

JSON元应用程序协议(JMAP)

Abstract

摘要

This document specifies a protocol for clients to efficiently query, fetch, and modify JSON-based data objects, with support for push notification of changes and fast resynchronisation and for out-of-band binary data upload/download.

本文档为客户端指定了一个协议,用于高效地查询、获取和修改基于JSON的数据对象,并支持更改推送通知和快速重新同步以及带外二进制数据上载/下载。

Status of This Memo

关于下段备忘

This is an Internet Standards Track document.

这是一份互联网标准跟踪文件。

This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). Further information on Internet Standards is available in Section 2 of RFC 7841.

本文件是互联网工程任务组(IETF)的产品。它代表了IETF社区的共识。它已经接受了公众审查,并已被互联网工程指导小组(IESG)批准出版。有关互联网标准的更多信息,请参见RFC 7841第2节。

Information about the current status of this document, any errata, and how to provide feedback on it may be obtained at https://www.rfc-editor.org/info/rfc8620.

有关本文件当前状态、任何勘误表以及如何提供反馈的信息,请访问https://www.rfc-editor.org/info/rfc8620.

Copyright Notice

版权公告

Copyright (c) 2019 IETF Trust and the persons identified as the document authors. All rights reserved.

版权(c)2019 IETF信托基金和被确定为文件作者的人员。版权所有。

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.

本文件受BCP 78和IETF信托有关IETF文件的法律规定的约束(https://trustee.ietf.org/license-info)自本文件出版之日起生效。请仔细阅读这些文件,因为它们描述了您对本文件的权利和限制。从本文件中提取的代码组件必须包括信托法律条款第4.e节中所述的简化BSD许可证文本,并提供简化BSD许可证中所述的无担保。

Table of Contents

目录

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   4
     1.1.  Notational Conventions  . . . . . . . . . . . . . . . . .   4
     1.2.  The Id Data Type  . . . . . . . . . . . . . . . . . . . .   6
     1.3.  The Int and UnsignedInt Data Types  . . . . . . . . . . .   6
     1.4.  The Date and UTCDate Data Types . . . . . . . . . . . . .   7
     1.5.  JSON as the Data Encoding Format  . . . . . . . . . . . .   7
     1.6.  Terminology . . . . . . . . . . . . . . . . . . . . . . .   7
       1.6.1.  User  . . . . . . . . . . . . . . . . . . . . . . . .   7
       1.6.2.  Accounts  . . . . . . . . . . . . . . . . . . . . . .   7
       1.6.3.  Data Types and Records  . . . . . . . . . . . . . . .   8
     1.7.  The JMAP API Model  . . . . . . . . . . . . . . . . . . .   8
     1.8.  Vendor-Specific Extensions  . . . . . . . . . . . . . . .   9
   2.  The JMAP Session Resource . . . . . . . . . . . . . . . . . .   9
     2.1.  Example . . . . . . . . . . . . . . . . . . . . . . . . .  14
     2.2.  Service Autodiscovery . . . . . . . . . . . . . . . . . .  15
   3.  Structured Data Exchange  . . . . . . . . . . . . . . . . . .  16
     3.1.  Making an API Request . . . . . . . . . . . . . . . . . .  16
     3.2.  The Invocation Data Type  . . . . . . . . . . . . . . . .  16
     3.3.  The Request Object  . . . . . . . . . . . . . . . . . . .  16
       3.3.1.  Example Request . . . . . . . . . . . . . . . . . . .  18
     3.4.  The Response Object . . . . . . . . . . . . . . . . . . .  18
       3.4.1.  Example Response  . . . . . . . . . . . . . . . . . .  19
     3.5.  Omitting Arguments  . . . . . . . . . . . . . . . . . . .  19
     3.6.  Errors  . . . . . . . . . . . . . . . . . . . . . . . . .  19
       3.6.1.  Request-Level Errors  . . . . . . . . . . . . . . . .  20
       3.6.2.  Method-Level Errors . . . . . . . . . . . . . . . . .  21
     3.7.  References to Previous Method Results . . . . . . . . . .  22
     3.8.  Localisation of User-Visible Strings  . . . . . . . . . .  27
     3.9.  Security  . . . . . . . . . . . . . . . . . . . . . . . .  28
     3.10. Concurrency . . . . . . . . . . . . . . . . . . . . . . .  28
   4.  The Core/echo Method  . . . . . . . . . . . . . . . . . . . .  28
     4.1.  Example . . . . . . . . . . . . . . . . . . . . . . . . .  28
   5.  Standard Methods and Naming Convention  . . . . . . . . . . .  29
     5.1.  /get  . . . . . . . . . . . . . . . . . . . . . . . . . .  29
     5.2.  /changes  . . . . . . . . . . . . . . . . . . . . . . . .  30
     5.3.  /set  . . . . . . . . . . . . . . . . . . . . . . . . . .  34
     5.4.  /copy . . . . . . . . . . . . . . . . . . . . . . . . . .  40
     5.5.  /query  . . . . . . . . . . . . . . . . . . . . . . . . .  42
     5.6.  /queryChanges . . . . . . . . . . . . . . . . . . . . . .  48
     5.7.  Examples  . . . . . . . . . . . . . . . . . . . . . . . .  51
     5.8.  Proxy Considerations  . . . . . . . . . . . . . . . . . .  58
   6.  Binary Data . . . . . . . . . . . . . . . . . . . . . . . . .  58
     6.1.  Uploading Binary Data . . . . . . . . . . . . . . . . . .  59
     6.2.  Downloading Binary Data . . . . . . . . . . . . . . . . .  60
     6.3.  Blob/copy . . . . . . . . . . . . . . . . . . . . . . . .  61
        
   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   4
     1.1.  Notational Conventions  . . . . . . . . . . . . . . . . .   4
     1.2.  The Id Data Type  . . . . . . . . . . . . . . . . . . . .   6
     1.3.  The Int and UnsignedInt Data Types  . . . . . . . . . . .   6
     1.4.  The Date and UTCDate Data Types . . . . . . . . . . . . .   7
     1.5.  JSON as the Data Encoding Format  . . . . . . . . . . . .   7
     1.6.  Terminology . . . . . . . . . . . . . . . . . . . . . . .   7
       1.6.1.  User  . . . . . . . . . . . . . . . . . . . . . . . .   7
       1.6.2.  Accounts  . . . . . . . . . . . . . . . . . . . . . .   7
       1.6.3.  Data Types and Records  . . . . . . . . . . . . . . .   8
     1.7.  The JMAP API Model  . . . . . . . . . . . . . . . . . . .   8
     1.8.  Vendor-Specific Extensions  . . . . . . . . . . . . . . .   9
   2.  The JMAP Session Resource . . . . . . . . . . . . . . . . . .   9
     2.1.  Example . . . . . . . . . . . . . . . . . . . . . . . . .  14
     2.2.  Service Autodiscovery . . . . . . . . . . . . . . . . . .  15
   3.  Structured Data Exchange  . . . . . . . . . . . . . . . . . .  16
     3.1.  Making an API Request . . . . . . . . . . . . . . . . . .  16
     3.2.  The Invocation Data Type  . . . . . . . . . . . . . . . .  16
     3.3.  The Request Object  . . . . . . . . . . . . . . . . . . .  16
       3.3.1.  Example Request . . . . . . . . . . . . . . . . . . .  18
     3.4.  The Response Object . . . . . . . . . . . . . . . . . . .  18
       3.4.1.  Example Response  . . . . . . . . . . . . . . . . . .  19
     3.5.  Omitting Arguments  . . . . . . . . . . . . . . . . . . .  19
     3.6.  Errors  . . . . . . . . . . . . . . . . . . . . . . . . .  19
       3.6.1.  Request-Level Errors  . . . . . . . . . . . . . . . .  20
       3.6.2.  Method-Level Errors . . . . . . . . . . . . . . . . .  21
     3.7.  References to Previous Method Results . . . . . . . . . .  22
     3.8.  Localisation of User-Visible Strings  . . . . . . . . . .  27
     3.9.  Security  . . . . . . . . . . . . . . . . . . . . . . . .  28
     3.10. Concurrency . . . . . . . . . . . . . . . . . . . . . . .  28
   4.  The Core/echo Method  . . . . . . . . . . . . . . . . . . . .  28
     4.1.  Example . . . . . . . . . . . . . . . . . . . . . . . . .  28
   5.  Standard Methods and Naming Convention  . . . . . . . . . . .  29
     5.1.  /get  . . . . . . . . . . . . . . . . . . . . . . . . . .  29
     5.2.  /changes  . . . . . . . . . . . . . . . . . . . . . . . .  30
     5.3.  /set  . . . . . . . . . . . . . . . . . . . . . . . . . .  34
     5.4.  /copy . . . . . . . . . . . . . . . . . . . . . . . . . .  40
     5.5.  /query  . . . . . . . . . . . . . . . . . . . . . . . . .  42
     5.6.  /queryChanges . . . . . . . . . . . . . . . . . . . . . .  48
     5.7.  Examples  . . . . . . . . . . . . . . . . . . . . . . . .  51
     5.8.  Proxy Considerations  . . . . . . . . . . . . . . . . . .  58
   6.  Binary Data . . . . . . . . . . . . . . . . . . . . . . . . .  58
     6.1.  Uploading Binary Data . . . . . . . . . . . . . . . . . .  59
     6.2.  Downloading Binary Data . . . . . . . . . . . . . . . . .  60
     6.3.  Blob/copy . . . . . . . . . . . . . . . . . . . . . . . .  61
        
   7.  Push  . . . . . . . . . . . . . . . . . . . . . . . . . . . .  62
     7.1.  The StateChange Object  . . . . . . . . . . . . . . . . .  63
       7.1.1.  Example . . . . . . . . . . . . . . . . . . . . . . .  64
     7.2.  PushSubscription  . . . . . . . . . . . . . . . . . . . .  64
       7.2.1.  PushSubscription/get  . . . . . . . . . . . . . . . .  67
       7.2.2.  PushSubscription/set  . . . . . . . . . . . . . . . .  68
       7.2.3.  Example . . . . . . . . . . . . . . . . . . . . . . .  69
     7.3.  Event Source  . . . . . . . . . . . . . . . . . . . . . .  71
   8.  Security Considerations . . . . . . . . . . . . . . . . . . .  73
     8.1.  Transport Confidentiality . . . . . . . . . . . . . . . .  73
     8.2.  Authentication Scheme . . . . . . . . . . . . . . . . . .  73
     8.3.  Service Autodiscovery . . . . . . . . . . . . . . . . . .  73
     8.4.  JSON Parsing  . . . . . . . . . . . . . . . . . . . . . .  74
     8.5.  Denial of Service . . . . . . . . . . . . . . . . . . . .  74
     8.6.  Connection to Unknown Push Server . . . . . . . . . . . .  74
     8.7.  Push Encryption . . . . . . . . . . . . . . . . . . . . .  75
     8.8.  Traffic Analysis  . . . . . . . . . . . . . . . . . . . .  76
   9.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .  76
     9.1.  Assignment of jmap Service Name . . . . . . . . . . . . .  76
     9.2.  Registration of Well-Known URI Suffix for JMAP  . . . . .  76
     9.3.  Registration of the jmap URN Sub-namespace  . . . . . . .  77
     9.4.  Creation of "JMAP Capabilities" Registry  . . . . . . . .  77
       9.4.1.  Preliminary Community Review  . . . . . . . . . . . .  77
       9.4.2.  Submit Request to IANA  . . . . . . . . . . . . . . .  78
       9.4.3.  Designated Expert Review  . . . . . . . . . . . . . .  78
       9.4.4.  Change Procedures . . . . . . . . . . . . . . . . . .  78
       9.4.5.  JMAP Capabilities Registry Template . . . . . . . . .  79
       9.4.6.  Initial Registration for JMAP Core  . . . . . . . . .  79
       9.4.7.  Registration for JMAP Error Placeholder in JMAP
               Capabilities Registry . . . . . . . . . . . . . . . .  80
     9.5.  Creation of "JMAP Error Codes" Registry . . . . . . . . .  80
       9.5.1.  Expert Review . . . . . . . . . . . . . . . . . . . .  80
       9.5.2.  JMAP Error Codes Registry Template  . . . . . . . . .  81
       9.5.3.  Initial Contents for the JMAP Error Codes Registry  .  81
   10. References  . . . . . . . . . . . . . . . . . . . . . . . . .  86
     10.1.  Normative References . . . . . . . . . . . . . . . . . .  86
     10.2.  Informative References . . . . . . . . . . . . . . . . .  89
   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  90
        
   7.  Push  . . . . . . . . . . . . . . . . . . . . . . . . . . . .  62
     7.1.  The StateChange Object  . . . . . . . . . . . . . . . . .  63
       7.1.1.  Example . . . . . . . . . . . . . . . . . . . . . . .  64
     7.2.  PushSubscription  . . . . . . . . . . . . . . . . . . . .  64
       7.2.1.  PushSubscription/get  . . . . . . . . . . . . . . . .  67
       7.2.2.  PushSubscription/set  . . . . . . . . . . . . . . . .  68
       7.2.3.  Example . . . . . . . . . . . . . . . . . . . . . . .  69
     7.3.  Event Source  . . . . . . . . . . . . . . . . . . . . . .  71
   8.  Security Considerations . . . . . . . . . . . . . . . . . . .  73
     8.1.  Transport Confidentiality . . . . . . . . . . . . . . . .  73
     8.2.  Authentication Scheme . . . . . . . . . . . . . . . . . .  73
     8.3.  Service Autodiscovery . . . . . . . . . . . . . . . . . .  73
     8.4.  JSON Parsing  . . . . . . . . . . . . . . . . . . . . . .  74
     8.5.  Denial of Service . . . . . . . . . . . . . . . . . . . .  74
     8.6.  Connection to Unknown Push Server . . . . . . . . . . . .  74
     8.7.  Push Encryption . . . . . . . . . . . . . . . . . . . . .  75
     8.8.  Traffic Analysis  . . . . . . . . . . . . . . . . . . . .  76
   9.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .  76
     9.1.  Assignment of jmap Service Name . . . . . . . . . . . . .  76
     9.2.  Registration of Well-Known URI Suffix for JMAP  . . . . .  76
     9.3.  Registration of the jmap URN Sub-namespace  . . . . . . .  77
     9.4.  Creation of "JMAP Capabilities" Registry  . . . . . . . .  77
       9.4.1.  Preliminary Community Review  . . . . . . . . . . . .  77
       9.4.2.  Submit Request to IANA  . . . . . . . . . . . . . . .  78
       9.4.3.  Designated Expert Review  . . . . . . . . . . . . . .  78
       9.4.4.  Change Procedures . . . . . . . . . . . . . . . . . .  78
       9.4.5.  JMAP Capabilities Registry Template . . . . . . . . .  79
       9.4.6.  Initial Registration for JMAP Core  . . . . . . . . .  79
       9.4.7.  Registration for JMAP Error Placeholder in JMAP
               Capabilities Registry . . . . . . . . . . . . . . . .  80
     9.5.  Creation of "JMAP Error Codes" Registry . . . . . . . . .  80
       9.5.1.  Expert Review . . . . . . . . . . . . . . . . . . . .  80
       9.5.2.  JMAP Error Codes Registry Template  . . . . . . . . .  81
       9.5.3.  Initial Contents for the JMAP Error Codes Registry  .  81
   10. References  . . . . . . . . . . . . . . . . . . . . . . . . .  86
     10.1.  Normative References . . . . . . . . . . . . . . . . . .  86
     10.2.  Informative References . . . . . . . . . . . . . . . . .  89
   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  90
        
1. Introduction
1. 介绍

The JSON Meta Application Protocol (JMAP) is used for synchronising data, such as mail, calendars, or contacts, between a client and a server. It is optimised for mobile and web environments and aims to provide a consistent interface to different data types.

JSON元应用程序协议(JMAP)用于在客户端和服务器之间同步数据,如邮件、日历或联系人。它针对移动和web环境进行了优化,旨在为不同的数据类型提供一致的接口。

This specification is for the generic mechanism of data synchronisation. Further specifications define the data models for different data types that may be synchronised via JMAP.

本规范适用于数据同步的通用机制。进一步的规范定义了可通过JMAP同步的不同数据类型的数据模型。

JMAP is designed to make efficient use of limited network resources. Multiple API calls may be batched in a single request to the server, reducing round trips and improving battery life on mobile devices. Push connections remove the need for polling, and an efficient delta update mechanism ensures a minimum amount of data is transferred.

JMAP旨在有效利用有限的网络资源。多个API调用可以在向服务器发出的单个请求中进行批处理,从而减少往返次数并提高移动设备的电池寿命。推送连接消除了轮询的需要,高效的增量更新机制确保传输的数据量最小。

JMAP is designed to be horizontally scalable to a very large number of users. This is facilitated by separate endpoints for users after login, the separation of binary and structured data, and a data model for sharing that does not allow data dependencies between accounts.

JMAP被设计成可以水平扩展到大量用户。登录后用户的独立端点、二进制数据和结构化数据的分离,以及不允许帐户之间存在数据依赖关系的共享数据模型,都有助于实现这一点。

1.1. Notational Conventions
1.1. 符号约定

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

本文件中的关键词“必须”、“不得”、“必需”、“应”、“不应”、“建议”、“不建议”、“可”和“可选”在所有大写字母出现时(如图所示)应按照BCP 14[RFC2119][RFC8174]所述进行解释。

The underlying format used for this specification is JSON. Consequently, the terms "object" and "array" as well as the four primitive types (strings, numbers, booleans, and null) are to be interpreted as described in Section 1 of [RFC8259]. Unless otherwise noted, all the property names and values are case sensitive.

本规范使用的底层格式是JSON。因此,术语“对象”和“数组”以及四种基本类型(字符串、数字、布尔值和null)将按照[RFC8259]第1节中的描述进行解释。除非另有说明,否则所有属性名称和值都区分大小写。

Some examples in this document contain "partial" JSON documents used for illustrative purposes. In these examples, three periods "..." are used to indicate a portion of the document that has been removed for compactness.

本文档中的一些示例包含用于说明目的的“部分”JSON文档。在这些示例中,三个句号“…”用于表示文档中为紧凑而删除的部分。

For compatibility with publishing requirements, line breaks have been inserted inside long JSON strings, with the following continuation lines indented. To form the valid JSON example, any line breaks inside a string must be replaced with a space and any other white space after the line break removed.

为了与发布要求兼容,在长JSON字符串中插入了换行符,并缩进了以下续行。要形成有效的JSON示例,字符串中的任何换行符都必须在删除换行符后替换为空格和任何其他空白。

Unless otherwise specified, examples of API exchanges only show the methodCalls array of the Request object or the methodResponses array of the Response object. For compactness, the rest of the Request/ Response object is omitted.

除非另有规定,API交换示例仅显示请求对象的methodCalls数组或响应对象的methodResponses数组。为了紧凑,请求/响应对象的其余部分被省略。

Type signatures are given for all JSON values in this document. The following conventions are used:

本文档中的所有JSON值都提供了类型签名。使用以下约定:

o "*" - The type is undefined (the value could be any type, although permitted values may be constrained by the context of this value).

o “*”-类型未定义(该值可以是任何类型,但允许的值可能受到该值上下文的约束)。

o "String" - The JSON string type.

o “String”-JSON字符串类型。

o "Number" - The JSON number type.

o “Number”-JSON编号类型。

o "Boolean" - The JSON boolean type.

o “Boolean”-JSON布尔类型。

o "A[B]" - A JSON object where the keys are all of type "A", and the values are all of type "B".

o “A[B]”-一个JSON对象,其中键都是“A”类型,值都是“B”类型。

o "A[]" - An array of values of type "A".

o “A[]”-类型为“A”的值数组。

o "A|B" - The value is either of type "A" or of type "B".

o “A | B”-该值属于“A”类型或“B”类型。

Other types may also be given, with their representation defined elsewhere in this document.

也可提供其他类型,其表示形式在本文件其他地方定义。

Object properties may also have a set of attributes defined along with the type signature. These have the following meanings:

对象属性还可以具有一组与类型签名一起定义的属性。它们具有以下含义:

o "server-set" -- Only the server can set the value for this property. The client MUST NOT send this property when creating a new object of this type.

o “服务器集”--只有服务器可以设置此属性的值。创建此类型的新对象时,客户端不得发送此属性。

o "immutable" -- The value MUST NOT change after the object is created.

o “不可变”--创建对象后,值不得更改。

o "default" -- (This is followed by a JSON value). The value that will be used for this property if it is omitted in an argument or when creating a new object of this type.

o “default”-(后面跟着一个JSON值)。如果在参数中或创建此类型的新对象时忽略此属性,则将用于此属性的值。

1.2. The Id Data Type
1.2. Id数据类型

All record ids are assigned by the server and are immutable.

所有记录ID都由服务器分配,并且是不可变的。

Where "Id" is given as a data type, it means a "String" of at least 1 and a maximum of 255 octets in size, and it MUST only contain characters from the "URL and Filename Safe" base64 alphabet, as defined in Section 5 of [RFC4648], excluding the pad character ("="). This means the allowed characters are the ASCII alphanumeric characters ("A-Za-z0-9"), hyphen ("-"), and underscore ("_").

当“Id”作为数据类型给出时,它表示至少为1且最大为255个八位字节的“字符串”,并且它必须仅包含[RFC4648]第5节中定义的“URL和文件名安全”base64字母表中的字符,不包括填充字符(“=”)。这意味着允许的字符是ASCII字母数字字符(“A-Za-z0-9”)、连字符(“-”)和下划线(“”)。

These characters are safe to use in almost any context (e.g., filesystems, URIs, and IMAP atoms). For maximum safety, servers SHOULD also follow defensive allocation strategies to avoid creating risks where glob completion or data type detection may be present (e.g., on filesystems or in spreadsheets). In particular, it is wise to avoid:

这些字符几乎可以在任何上下文中安全使用(例如,文件系统、URI和IMAP原子)。为了最大限度的安全,服务器还应遵循防御性分配策略,以避免在可能存在全局完成或数据类型检测的情况下(例如,在文件系统或电子表格中)产生风险。特别是,明智的做法是避免:

o Ids starting with a dash

o 以破折号开头的ID

o Ids starting with digits

o 以数字开头的ID

o Ids that contain only digits

o 仅包含数字的ID

o Ids that differ only by ASCII case (for example, A vs. a)

o 仅按ASCII大小写不同的ID(例如,A与A)

o the specific sequence of three characters "NIL" (because this sequence can be confused with the IMAP protocol expression of the null value)

o 三个字符“NIL”的特定序列(因为此序列可能与null值的IMAP协议表达式相混淆)

A good solution to these issues is to prefix every id with a single alphabetical character.

解决这些问题的一个好办法是在每个id前面加一个字母字符。

1.3. The Int and UnsignedInt Data Types
1.3. Int和UnsignedInt数据类型

Where "Int" is given as a data type, it means an integer in the range -2^53+1 <= value <= 2^53-1, the safe range for integers stored in a floating-point double, represented as a JSON "Number".

其中“Int”作为数据类型给出,它表示-2^53+1<=value<=2^53-1范围内的整数,该范围是存储在浮点双精度中的整数的安全范围,表示为JSON“数字”。

Where "UnsignedInt" is given as a data type, it means an "Int" where the value MUST be in the range 0 <= value <= 2^53-1.

如果“UnsignedInt”作为数据类型给出,则表示“Int”,其中值必须在0<=value<=2^53-1的范围内。

1.4. The Date and UTCDate Data Types
1.4. 日期和UTCDate数据类型

Where "Date" is given as a type, it means a string in "date-time" format [RFC3339]. To ensure a normalised form, the "time-secfrac" MUST always be omitted if zero, and any letters in the string (e.g., "T" and "Z") MUST be uppercase. For example, "2014-10-30T14:12:00+08:00".

如果“日期”作为类型给出,则表示“日期-时间”格式的字符串[RFC3339]。为确保格式规范化,如果为零,“time secfrac”必须始终省略,字符串中的任何字母(例如,“T”和“Z”)必须为大写。例如,“2014-10-30T14:12:00+08:00”。

Where "UTCDate" is given as a type, it means a "Date" where the "time-offset" component MUST be "Z" (i.e., it must be in UTC time). For example, "2014-10-30T06:12:00Z".

如果“UTCDate”作为一种类型给出,则表示“日期”,其中“时间偏移”组件必须为“Z”(即,它必须为UTC时间)。例如,“2014-10-30T06:12:00Z”。

1.5. JSON as the Data Encoding Format
1.5. JSON作为数据编码格式

JSON is a text-based data interchange format as specified in [RFC8259]. The Internet JSON (I-JSON) format defined in [RFC7493] is a strict subset of this, adding restrictions to avoid potentially confusing scenarios (for example, it mandates that an object MUST NOT have two members with the same name).

JSON是[RFC8259]中指定的基于文本的数据交换格式。[RFC7493]中定义的Internet JSON(I-JSON)格式是该格式的严格子集,增加了限制以避免潜在的混淆场景(例如,它要求一个对象不能有两个同名成员)。

All data sent from the client to the server or from the server to the client (except binary file upload/download) MUST be valid I-JSON according to the RFC and is therefore case sensitive and encoded in UTF-8 [RFC3629].

根据RFC,从客户机发送到服务器或从服务器发送到客户机的所有数据(二进制文件上载/下载除外)必须是有效的I-JSON,因此区分大小写并以UTF-8[RFC3629]编码。

1.6. Terminology
1.6. 术语
1.6.1. User
1.6.1. 使用者

A user is a person accessing data via JMAP. A user has a set of permissions determining the data that they can see.

用户是通过JMAP访问数据的人。用户有一组权限来确定他们可以查看的数据。

1.6.2. Accounts
1.6.2. 账户

An account is a collection of data. A single account may contain an arbitrary set of data types, for example, a collection of mail, contacts, and calendars. Most JMAP methods take a mandatory "accountId" argument that specifies on which account the operations are to take place.

帐户是数据的集合。单个帐户可能包含任意一组数据类型,例如,邮件、联系人和日历的集合。大多数JMAP方法都采用强制的“accountId”参数,该参数指定操作将在哪个帐户上进行。

An account is not the same as a user, although it is common for a primary account to directly belong to the user. For example, you may have an account that contains data for a group or business, to which multiple users have access.

帐户与用户不同,尽管主帐户通常直接属于用户。例如,您可能有一个帐户,其中包含多个用户可以访问的组或业务的数据。

A single set of credentials may provide access to multiple accounts, for example, if another user is sharing their work calendar with the authenticated user or if there is a group mailbox for a support-desk inbox.

单个凭据集可以提供对多个帐户的访问,例如,如果另一个用户正在与经过身份验证的用户共享其工作日历,或者如果有用于支持台收件箱的组邮箱。

In the event of a severe internal error, a server may have to reallocate ids or do something else that violates standard JMAP data constraints for an account. In this situation, the data on the server is no longer compatible with cached data the client may have from before. The server MUST treat this as though the account has been deleted and then recreated with a new account id. Clients will then be forced to throw away any data with the old account id and refetch all data from scratch.

在发生严重内部错误的情况下,服务器可能必须重新分配ID或执行其他违反帐户标准JMAP数据约束的操作。在这种情况下,服务器上的数据不再与客户端以前可能拥有的缓存数据兼容。服务器必须将此视为该帐户已被删除,然后使用新帐户id重新创建。然后,客户端将被迫丢弃任何具有旧帐户id的数据,并从头开始重新蚀刻所有数据。

1.6.3. Data Types and Records
1.6.3. 数据类型和记录

JMAP provides a uniform interface for creating, retrieving, updating, and deleting various types of objects. A "data type" is a collection of named, typed properties, just like the schema for a database table. Each instance of a data type is called a "record".

JMAP为创建、检索、更新和删除各种类型的对象提供了统一的接口。“数据类型”是一组命名的、类型化的属性,就像数据库表的模式一样。数据类型的每个实例称为“记录”。

The id of a record is immutable and assigned by the server. The id MUST be unique among all records of the *same type* within the *same account*. Ids may clash across accounts or for two records of different types within the same account.

记录的id是不可变的,由服务器分配。该id在*相同帐户*内*相同类型*的所有记录中必须是唯一的。ID可能会在多个帐户之间发生冲突,或者在同一帐户中存在两个不同类型的记录。

1.7. The JMAP API Model
1.7. JMAP-API模型

JMAP uses HTTP [RFC7230] to expose API, push, upload, and download resources. All HTTP requests MUST use the "https://" scheme (HTTP over TLS [RFC2818]). All HTTP requests MUST be authenticated.

JMAP使用HTTP[RFC7230]公开API、推送、上载和下载资源。所有HTTP请求都必须使用“https://”方案(HTTP over TLS[RFC2818])。所有HTTP请求都必须经过身份验证。

An authenticated client can fetch the user's Session object with details about the data and capabilities the server can provide as shown in Section 2. The client may then exchange data with the server in the following ways:

经过身份验证的客户端可以获取用户的会话对象,其中包含有关服务器可以提供的数据和功能的详细信息,如第2节所示。然后,客户端可通过以下方式与服务器交换数据:

1. The client may make an API request to the server to get or set structured data. This request consists of an ordered series of method calls. These are processed by the server, which then returns an ordered series of responses. This is described in Sections 3, 4, and 5.

1. 客户端可以向服务器发出API请求以获取或设置结构化数据。此请求由一系列有序的方法调用组成。这些由服务器处理,然后服务器返回一系列有序的响应。第3、4和5节对此进行了描述。

2. The client may download or upload binary files from/to the server. This is detailed in Section 6.

2. 客户端可以从服务器下载或上传二进制文件。第6节对此进行了详细说明。

3. The client may connect to a push channel on the server, to be notified when data has changed. This is explained in Section 7.

3. 客户端可以连接到服务器上的推送通道,以便在数据更改时收到通知。第7节对此进行了解释。

1.8. Vendor-Specific Extensions
1.8. 特定于供应商的扩展

Individual services will have custom features they wish to expose over JMAP. This may take the form of extra data types and/or methods not in the spec, extra arguments to JMAP methods, or extra properties on existing data types (which may also appear in arguments to methods that take property names).

个别服务将具有他们希望通过JMAP公开的自定义功能。这可能采取规范中没有的额外数据类型和/或方法、JMAP方法的额外参数或现有数据类型的额外属性(也可能出现在采用属性名称的方法的参数中)的形式。

The server can advertise custom extensions it supports by including the identifiers in the capabilities object. Identifiers for vendor extensions MUST be a URL belonging to a domain owned by the vendor, to avoid conflict. The URL SHOULD resolve to documentation for the changes the extension makes.

服务器可以通过在capabilities对象中包含标识符来公布其支持的自定义扩展。供应商扩展的标识符必须是属于供应商拥有的域的URL,以避免冲突。URL应解析为扩展所做更改的文档。

The client MUST opt in to use an extension by passing the appropriate capability identifier in the "using" array of the Request object, as described in Section 3.3. The server MUST only follow the specifications that are opted into and behave as though it does not implement anything else when processing a request. This is to ensure compatibility with clients that don't know about a specific custom extension and for compatibility with future versions of JMAP.

客户机必须通过在请求对象的“使用”数组中传递适当的能力标识符来选择使用扩展,如第3.3节所述。在处理请求时,服务器必须只遵循所选择的规范,并表现为不实现任何其他功能。这是为了确保与不了解特定自定义扩展的客户端的兼容性,以及与JMAP的未来版本的兼容性。

2. The JMAP Session Resource
2. JMAP会话资源

You need two things to connect to a JMAP server:

连接到JMAP服务器需要两件事:

1. The URL for the JMAP Session resource. This may be requested directly from the user or discovered automatically based on a username domain (see Section 2.2 below).

1. JMAP会话资源的URL。这可以直接从用户处请求,也可以根据用户名域自动发现(见下面第2.2节)。

2. Credentials to authenticate with. How to obtain credentials is out of scope for this document.

2. 要进行身份验证的凭据。如何获取凭据超出了本文档的范围。

A successful authenticated GET request to the JMAP Session resource MUST return a JSON-encoded *Session* object, giving details about the data and capabilities the server can provide to the client given those credentials. It has the following properties:

对JMAP会话资源的成功身份验证GET请求必须返回一个JSON编码的*Session*对象,该对象给出了服务器在给定这些凭据的情况下可以向客户端提供的数据和功能的详细信息。它具有以下属性:

o capabilities: "String[Object]"

o 功能:“字符串[对象]”

An object specifying the capabilities of this server. Each key is a URI for a capability supported by the server. The value for each of these keys is an object with further information about the server's capabilities in relation to that capability.

指定此服务器功能的对象。每个密钥都是服务器支持的功能的URI。每个键的值都是一个对象,其中包含有关该功能的服务器功能的更多信息。

The client MUST ignore any properties it does not understand.

客户端必须忽略它不理解的任何属性。

The capabilities object MUST include a property called "urn:ietf:params:jmap:core". The value of this property is an object that MUST contain the following information on server capabilities (suggested minimum values for limits are supplied that allow clients to make efficient use of the network):

capabilities对象必须包含一个名为“urn:ietf:params:jmap:core”的属性。此属性的值是一个对象,该对象必须包含有关服务器功能的以下信息(提供了允许客户端有效使用网络的建议最小限制值):

* maxSizeUpload: "UnsignedInt"

* maxSizeUpload:“未签名”

The maximum file size, in octets, that the server will accept for a single file upload (for any purpose). Suggested minimum: 50,000,000.

服务器将为单个文件上载(出于任何目的)接受的最大文件大小(以八位字节为单位)。建议最低限额:50000000。

* maxConcurrentUpload: "UnsignedInt"

* maxConcurrentUpload:“未签名”

The maximum number of concurrent requests the server will accept to the upload endpoint. Suggested minimum: 4.

服务器将接受到上载终结点的最大并发请求数。建议最低限额:4。

* maxSizeRequest: "UnsignedInt"

* maxSizeRequest:“未签名”

The maximum size, in octets, that the server will accept for a single request to the API endpoint. Suggested minimum: 10,000,000.

服务器对API端点的单个请求将接受的最大大小(以八位字节为单位)。建议最低限额:10000000。

* maxConcurrentRequests: "UnsignedInt"

* maxConcurrentRequests:“未签名”

The maximum number of concurrent requests the server will accept to the API endpoint. Suggested minimum: 4.

服务器将接受到API终结点的最大并发请求数。建议最低限额:4。

* maxCallsInRequest: "UnsignedInt"

* maxCallsInRequest:“未签名”

The maximum number of method calls the server will accept in a single request to the API endpoint. Suggested minimum: 16.

在对API端点的单个请求中,服务器将接受的最大方法调用数。建议最低限额:16。

* maxObjectsInGet: "UnsignedInt"

* maxObjectsInGet:“未签名”

The maximum number of objects that the client may request in a single /get type method call. Suggested minimum: 500.

客户端在单个/get-type方法调用中可以请求的最大对象数。建议最低限额:500。

* maxObjectsInSet: "UnsignedInt"

* maxObjectsInSet:“未签名”

The maximum number of objects the client may send to create, update, or destroy in a single /set type method call. This is the combined total, e.g., if the maximum is 10, you could not create 7 objects and destroy 6, as this would be 13 actions, which exceeds the limit. Suggested minimum: 500.

在单个/集合类型方法调用中,客户端可以发送以创建、更新或销毁的最大对象数。这是总和,例如,如果最大值为10,则无法创建7个对象并销毁6个,因为这将是13个操作,超出了限制。建议最低限额:500。

* collationAlgorithms: "String[]"

* 排序规则算法:“字符串[]”

A list of identifiers for algorithms registered in the collation registry, as defined in [RFC4790], that the server supports for sorting when querying records.

在排序注册表中注册的算法的标识符列表,如[RFC4790]中所定义,服务器在查询记录时支持排序。

Specifications for future capabilities will define their own properties on the capabilities object.

未来功能的规范将在capabilities对象上定义它们自己的属性。

Servers MAY advertise vendor-specific JMAP extensions, as described in Section 1.8. To avoid conflict, an identifier for a vendor-specific extension MUST be a URL with a domain owned by the vendor. Clients MUST opt in to any capability it wishes to use (see Section 3.3).

服务器可能会公布特定于供应商的JMAP扩展,如第1.8节所述。为了避免冲突,特定于供应商的扩展的标识符必须是供应商拥有的域的URL。客户必须选择其希望使用的任何功能(见第3.3节)。

o accounts: "Id[Account]"

o 帐户:“Id[帐户]”

A map of an account id to an Account object for each account (see Section 1.6.2) the user has access to. An *Account* object has the following properties:

用户有权访问的每个帐户的帐户id到帐户对象的映射(见第1.6.2节)。*Account*对象具有以下属性:

* name: "String"

* 名称:“字符串”

A user-friendly string to show when presenting content from this account, e.g., the email address representing the owner of the account.

显示此帐户内容时显示的用户友好字符串,例如,代表帐户所有者的电子邮件地址。

* isPersonal: "Boolean"

* 个人:“布尔”

This is true if the account belongs to the authenticated user rather than a group account or a personal account of another user that has been shared with them.

如果该帐户属于经过身份验证的用户,而不是与他们共享的另一个用户的组帐户或个人帐户,则为真。

* isReadOnly: "Boolean"

* isReadOnly:“布尔”

This is true if the entire account is read-only.

如果整个帐户是只读的,则为真。

* accountCapabilities: "String[Object]"

* accountCapabilities:“字符串[对象]”

The set of capability URIs for the methods supported in this account. Each key is a URI for a capability that has methods you can use with this account. The value for each of these keys is an object with further information about the account's permissions and restrictions with respect to this capability, as defined in the capability's specification.

此帐户中支持的方法的功能URI集。每个键都是一个URI,表示一个具有可用于此帐户的方法的功能。每个密钥的值都是一个对象,其中包含有关该功能的帐户权限和限制的更多信息,如功能规范中所定义。

The client MUST ignore any properties it does not understand.

客户端必须忽略它不理解的任何属性。

The server advertises the full list of capabilities it supports in the capabilities object, as defined above. If the capability defines new methods, the server MUST include it in the accountCapabilities object if the user may use those methods with this account. It MUST NOT include it in the accountCapabilities object if the user cannot use those methods with this account.

服务器在capabilities对象中公布其支持的功能的完整列表,如上所述。如果功能定义了新方法,则如果用户可以将这些方法用于此帐户,服务器必须将其包含在accountCapabilities对象中。如果用户无法将这些方法用于此帐户,则不能将其包含在accountCapabilities对象中。

For example, you may have access to your own account with mail, calendars, and contacts data and also a shared account that only has contacts data (a business address book, for example). In this case, the accountCapabilities property on the first account would include something like "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:calendars", and "urn:ietf:params:jmap:contacts", while the second account would just have the last of these.

例如,您可以通过邮件、日历和联系人数据访问自己的帐户,也可以访问仅包含联系人数据的共享帐户(例如,业务通讯簿)。在本例中,第一个帐户上的accountCapabilities属性将包含类似“urn:ietf:params:jmap:mail”、“urn:ietf:params:jmap:calendars”和“urn:ietf:params:jmap:contacts”的内容,而第二个帐户将只包含最后一个。

Attempts to use the methods defined in a capability with one of the accounts that does not support that capability are rejected with an "accountNotSupportedByMethod" error (see "Method-Level Errors", Section 3.6.2).

尝试将功能中定义的方法用于不支持该功能的某个帐户时,将被拒绝,并出现“accountNotSupportedByMethod”错误(请参阅第3.6.2节“方法级错误”)。

o primaryAccounts: "String[Id]"

o primaryAccounts:“字符串[Id]”

A map of capability URIs (as found in accountCapabilities) to the account id that is considered to be the user's main or default account for data pertaining to that capability. If no account being returned belongs to the user, or in any other way there is no appropriate way to determine a default account, there MAY be no entry for a particular URI, even though that capability is supported by the server (and in the capabilities object). "urn:ietf:params:jmap:core" SHOULD NOT be present.

功能URI(如accountCapabilities中所示)到帐户id的映射,该帐户id被视为用户的主帐户或与该功能相关的数据的默认帐户。如果返回的帐户不属于用户,或者以任何其他方式没有确定默认帐户的适当方法,则可能没有特定URI的条目,即使服务器(以及capabilities对象)支持该功能。“urn:ietf:params:jmap:core”不应出现。

o username: "String"

o 用户名:“字符串”

The username associated with the given credentials, or the empty string if none.

与给定凭据关联的用户名,如果没有,则为空字符串。

o apiUrl: "String"

o apiUrl:“字符串”

The URL to use for JMAP API requests.

用于JMAP API请求的URL。

o downloadUrl: "String"

o 下载URL:“字符串”

The URL endpoint to use when downloading files, in URI Template (level 1) format [RFC6570]. The URL MUST contain variables called "accountId", "blobId", "type", and "name". The use of these variables is described in Section 6.2. Due to potential encoding issues with slashes in content types, it is RECOMMENDED to put the "type" variable in the query section of the URL.

下载文件时使用的URL端点,采用URI模板(级别1)格式[RFC6570]。URL必须包含名为“accountId”、“blobId”、“type”和“name”的变量。第6.2节描述了这些变量的使用。由于内容类型中斜杠可能存在编码问题,建议将“type”变量放在URL的查询部分。

o uploadUrl: "String"

o 上传URL:“字符串”

The URL endpoint to use when uploading files, in URI Template (level 1) format [RFC6570]. The URL MUST contain a variable called "accountId". The use of this variable is described in Section 6.1.

上载文件时使用的URL端点,采用URI模板(级别1)格式[RFC6570]。URL必须包含名为“accountId”的变量。第6.1节介绍了该变量的使用。

o eventSourceUrl: "String"

o eventSourceUrl:“字符串”

The URL to connect to for push events, as described in Section 7.3, in URI Template (level 1) format [RFC6570]. The URL MUST contain variables called "types", "closeafter", and "ping". The use of these variables is described in Section 7.3.

推送事件要连接到的URL,如第7.3节所述,采用URI模板(级别1)格式[RFC6570]。URL必须包含名为“type”、“closeafter”和“ping”的变量。第7.3节描述了这些变量的使用。

o state: "String"

o 状态:“字符串”

A (preferably short) string representing the state of this object on the server. If the value of any other property on the Session object changes, this string will change. The current value is also returned on the API Response object (see Section 3.4), allowing clients to quickly determine if the session information has changed (e.g., an account has been added or removed), so they need to refetch the object.

表示服务器上此对象状态的字符串(最好是短字符串)。如果会话对象上任何其他属性的值更改,则此字符串将更改。API响应对象上也会返回当前值(参见第3.4节),允许客户端快速确定会话信息是否已更改(例如,帐户已添加或删除),因此需要重新蚀刻对象。

To ensure future compatibility, other properties MAY be included on the Session object. Clients MUST ignore any properties they are not expecting.

为了确保将来的兼容性,会话对象上可能包含其他属性。客户端必须忽略他们不期望的任何属性。

Implementors must take care to avoid inappropriate caching of the Session object at the HTTP layer. Since the client should only refetch when it detects there is a change (via the sessionState property of an API response), it is RECOMMENDED to disable HTTP caching altogether, for example, by setting "Cache-Control: no-cache, no-store, must-revalidate" on the response.

实现者必须注意避免在HTTP层对会话对象进行不适当的缓存。由于客户端只应在检测到更改时(通过API响应的sessionState属性)重新蚀刻,因此建议完全禁用HTTP缓存,例如,通过在响应上设置“缓存控制:无缓存、无存储、必须重新验证”。

2.1. Example
2.1. 实例

In the following example Session object, the user has access to their own mail and contacts via JMAP, as well as read-only access to shared mail from another user. The server is advertising a custom "https://example.com/apis/foobar" capability.

在下面的会话对象示例中,用户可以通过JMAP访问自己的邮件和联系人,也可以以只读方式访问其他用户的共享邮件。服务器正在播发一个自定义“https://example.com/apis/foobar“能力。

   {
     "capabilities": {
       "urn:ietf:params:jmap:core": {
         "maxSizeUpload": 50000000,
         "maxConcurrentUpload": 8,
         "maxSizeRequest": 10000000,
         "maxConcurrentRequest": 8,
         "maxCallsInRequest": 32,
         "maxObjectsInGet": 256,
         "maxObjectsInSet": 128,
         "collationAlgorithms": [
           "i;ascii-numeric",
           "i;ascii-casemap",
           "i;unicode-casemap"
         ]
       },
       "urn:ietf:params:jmap:mail": {}
       "urn:ietf:params:jmap:contacts": {},
       "https://example.com/apis/foobar": {
         "maxFoosFinangled": 42
       }
     },
     "accounts": {
       "A13824": {
         "name": "john@example.com",
         "isPersonal": true,
         "isReadOnly": false,
         "accountCapabilities": {
           "urn:ietf:params:jmap:mail": {
             "maxMailboxesPerEmail": null,
             "maxMailboxDepth": 10,
             ...
           },
           "urn:ietf:params:jmap:contacts": {
             ...
           }
         }
       },
        
   {
     "capabilities": {
       "urn:ietf:params:jmap:core": {
         "maxSizeUpload": 50000000,
         "maxConcurrentUpload": 8,
         "maxSizeRequest": 10000000,
         "maxConcurrentRequest": 8,
         "maxCallsInRequest": 32,
         "maxObjectsInGet": 256,
         "maxObjectsInSet": 128,
         "collationAlgorithms": [
           "i;ascii-numeric",
           "i;ascii-casemap",
           "i;unicode-casemap"
         ]
       },
       "urn:ietf:params:jmap:mail": {}
       "urn:ietf:params:jmap:contacts": {},
       "https://example.com/apis/foobar": {
         "maxFoosFinangled": 42
       }
     },
     "accounts": {
       "A13824": {
         "name": "john@example.com",
         "isPersonal": true,
         "isReadOnly": false,
         "accountCapabilities": {
           "urn:ietf:params:jmap:mail": {
             "maxMailboxesPerEmail": null,
             "maxMailboxDepth": 10,
             ...
           },
           "urn:ietf:params:jmap:contacts": {
             ...
           }
         }
       },
        
       "A97813": {
         "name": "jane@example.com",
         "isPersonal": false,
         "isReadOnly": true,
         "accountCapabilities": {
           "urn:ietf:params:jmap:mail": {
             "maxMailboxesPerEmail": 1,
             "maxMailboxDepth": 10,
             ...
           }
         }
       }
     },
     "primaryAccounts": {
       "urn:ietf:params:jmap:mail": "A13824",
       "urn:ietf:params:jmap:contacts": "A13824"
     },
     "username": "john@example.com",
     "apiUrl": "https://jmap.example.com/api/",
     "downloadUrl": "https://jmap.example.com
       /download/{accountId}/{blobId}/{name}?accept={type}",
     "uploadUrl": "https://jmap.example.com/upload/{accountId}/",
     "eventSourceUrl": "https://jmap.example.com
       /eventsource/?types={types}&closeafter={closeafter}&ping={ping}",
     "state": "75128aab4b1b"
   }
        
       "A97813": {
         "name": "jane@example.com",
         "isPersonal": false,
         "isReadOnly": true,
         "accountCapabilities": {
           "urn:ietf:params:jmap:mail": {
             "maxMailboxesPerEmail": 1,
             "maxMailboxDepth": 10,
             ...
           }
         }
       }
     },
     "primaryAccounts": {
       "urn:ietf:params:jmap:mail": "A13824",
       "urn:ietf:params:jmap:contacts": "A13824"
     },
     "username": "john@example.com",
     "apiUrl": "https://jmap.example.com/api/",
     "downloadUrl": "https://jmap.example.com
       /download/{accountId}/{blobId}/{name}?accept={type}",
     "uploadUrl": "https://jmap.example.com/upload/{accountId}/",
     "eventSourceUrl": "https://jmap.example.com
       /eventsource/?types={types}&closeafter={closeafter}&ping={ping}",
     "state": "75128aab4b1b"
   }
        
2.2. Service Autodiscovery
2.2. 服务自动发现

There are two standardised autodiscovery methods in use for Internet protocols:

有两种用于Internet协议的标准化自动发现方法:

o DNS SRV (see [RFC2782], [RFC6186], and [RFC6764])

o DNS SRV(参见[RFC2782]、[RFC6186]和[RFC6764])

o .well-known/servicename (see [RFC8615])

o .知名/服务名称(参见[RFC8615])

A JMAP-supporting host for the domain "example.com" SHOULD publish a SRV record "_jmap._tcp.example.com" that gives a hostname and port (usually port "443"). The JMAP Session resource is then "https://${hostname}[:${port}]/.well-known/jmap" (following any redirects).

域“example.com”的JMAP支持主机应该发布一个SRV记录“_JMAP._tcp.example.com”,该记录提供主机名和端口(通常为端口“443”)。然后,JMAP会话资源是“https://${hostname}[:${port}]/.well-known/JMAP”(遵循任何重定向)。

If the client has a username in the form of an email address, it MAY use the domain portion of this to attempt autodiscovery of the JMAP server.

如果客户端有一个电子邮件地址形式的用户名,它可能会使用其中的域部分来尝试自动发现JMAP服务器。

3. Structured Data Exchange
3. 结构化数据交换

The client may make an API request to the server to get or set structured data. This request consists of an ordered series of method calls. These are processed by the server, which then returns an ordered series of responses.

客户端可以向服务器发出API请求以获取或设置结构化数据。此请求由一系列有序的方法调用组成。这些由服务器处理,然后服务器返回一系列有序的响应。

3.1. Making an API Request
3.1. 发出API请求

To make an API request, the client makes an authenticated POST request to the API resource, which is defined by the "apiUrl" property in the Session object (see Section 2).

为了发出API请求,客户端向API资源发出经过身份验证的POST请求,该请求由会话对象中的“apiUrl”属性定义(参见第2节)。

The request MUST be of type "application/json" and consist of a single JSON-encoded "Request" object, as defined in Section 3.3. If successful, the response MUST also be of type "application/json" and consist of a single "Response" object, as defined in Section 3.4.

请求必须是“application/json”类型,并由单个json编码的“request”对象组成,如第3.3节所定义。如果成功,响应也必须是“application/json”类型,并由单个“response”对象组成,如第3.4节所定义。

3.2. The Invocation Data Type
3.2. 调用数据类型

Method calls and responses are represented by the *Invocation* data type. This is a tuple, represented as a JSON array containing three elements:

方法调用和响应由*Invocation*数据类型表示。这是一个元组,表示为包含三个元素的JSON数组:

1. A "String" *name* of the method to call or of the response.

1. 要调用的方法或响应的“字符串”*名称*。

2. A "String[*]" object containing named *arguments* for that method or response.

2. 包含该方法或响应的命名*参数*的“字符串[*]”对象。

3. A "String" *method call id*: an arbitrary string from the client to be echoed back with the responses emitted by that method call (a method may return 1 or more responses, as it may make implicit calls to other methods; all responses initiated by this method call get the same method call id in the response).

3. “字符串”*方法调用id*:来自客户端的任意字符串,将与该方法调用发出的响应一起回显(一个方法可能返回1个或多个响应,因为它可能对其他方法进行隐式调用;由该方法调用启动的所有响应在响应中获得相同的方法调用id)。

3.3. The Request Object
3.3. 请求对象

A *Request* object has the following properties:

*请求*对象具有以下属性:

o using: "String[]"

o 使用:“字符串[]”

The set of capabilities the client wishes to use. The client MAY include capability identifiers even if the method calls it makes do not utilise those capabilities. The server advertises the set of specifications it supports in the Session object (see Section 2), as keys on the "capabilities" property.

客户端希望使用的一组功能。客户机可能包括功能标识符,即使它所进行的方法调用没有使用这些功能。服务器将其在会话对象中支持的一组规范(参见第2节)作为“capabilities”属性上的键播发。

o methodCalls: "Invocation[]"

o methodCalls:“调用[]”

An array of method calls to process on the server. The method calls MUST be processed sequentially, in order.

要在服务器上处理的方法调用数组。方法调用必须按顺序依次处理。

o createdIds: "Id[Id]" (optional)

o createdIds:“Id[Id]”(可选)

A map of a (client-specified) creation id to the id the server assigned when a record was successfully created.

(客户端指定的)创建id到成功创建记录时服务器分配的id的映射。

As described later in this specification, some records may have a property that contains the id of another record. To allow more efficient network usage, you can set this property to reference a record created earlier in the same API request. Since the real id is unknown when the request is created, the client can instead specify the creation id it assigned, prefixed with a "#" (see Section 5.3 for more details).

如本规范后面所述,某些记录的属性可能包含另一个记录的id。为了更有效地使用网络,可以将此属性设置为引用先前在同一API请求中创建的记录。由于创建请求时实际id未知,因此客户端可以指定其分配的创建id,前缀为“#”(有关详细信息,请参阅第5.3节)。

As the server processes API requests, any time it successfully creates a new record, it adds the creation id to this map (see the "create" argument to /set in Section 5.3), with the server-assigned real id as the value. If it comes across a reference to a creation id in a create/update, it looks it up in the map and replaces the reference with the real id, if found.

当服务器处理API请求时,每当成功创建新记录时,它都会将创建id添加到此映射(请参阅第5.3节中的“create”参数to/set),并将服务器分配的真实id作为值。如果它在创建/更新中遇到对创建id的引用,它会在地图中查找该引用,并用实际id替换该引用(如果找到)。

The client can pass an initial value for this map as the "createdIds" property of the Request object. This may be an empty object. If given in the request, the response will also include a createdIds property. This allows proxy servers to easily split a JMAP request into multiple JMAP requests to send to different servers. For example, it could send the first two method calls to server A, then the third to server B, before sending the fourth to server A again. By passing the createdIds of the previous response to the next request, it can ensure all of these still resolve. See Section 5.8 for further discussion of proxy considerations.

客户端可以将此映射的初始值作为请求对象的“createdIds”属性传递。这可能是一个空对象。如果在请求中给出,响应还将包括createdIds属性。这允许代理服务器轻松地将一个JMAP请求拆分为多个JMAP请求,以发送到不同的服务器。例如,它可以将前两个方法调用发送到服务器A,然后将第三个方法调用发送到服务器B,然后再将第四个方法调用发送到服务器A。通过将前一个响应的createdIds传递给下一个请求,它可以确保所有这些仍然得到解决。有关代理事项的进一步讨论,请参见第5.8节。

Future specifications MAY add further properties to the Request object to extend the semantics. To ensure forwards compatibility, a server MUST ignore any other properties it does not understand on the JMAP Request object.

未来的规范可能会向请求对象添加更多属性以扩展语义。为了确保转发兼容性,服务器必须忽略它在JMAP请求对象上不理解的任何其他属性。

3.3.1. Example Request
3.3.1. 示例请求
{
  "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
  "methodCalls": [
    [ "method1", {
      "arg1": "arg1data",
      "arg2": "arg2data"
    }, "c1" ],
    [ "method2", {
      "arg1": "arg1data"
    }, "c2" ],
    [ "method3", {}, "c3" ]
  ]
}
        
{
  "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
  "methodCalls": [
    [ "method1", {
      "arg1": "arg1data",
      "arg2": "arg2data"
    }, "c1" ],
    [ "method2", {
      "arg1": "arg1data"
    }, "c2" ],
    [ "method3", {}, "c3" ]
  ]
}
        
3.4. The Response Object
3.4. 响应对象

A *Response* object has the following properties:

*响应*对象具有以下属性:

o methodResponses: "Invocation[]"

o methodResponses:“调用[]”

An array of responses, in the same format as the "methodCalls" on the Request object. The output of the methods MUST be added to the "methodResponses" array in the same order that the methods are processed.

响应数组,格式与请求对象上的“methodCalls”相同。方法的输出必须按照处理方法的相同顺序添加到“methodResponses”数组中。

o createdIds: "Id[Id]" (optional; only returned if given in the request)

o createdIds:“Id[Id]”(可选;仅在请求中给出时返回)

A map of a (client-specified) creation id to the id the server assigned when a record was successfully created. This MUST include all creation ids passed in the original createdIds parameter of the Request object, as well as any additional ones added for newly created records.

(客户端指定的)创建id到成功创建记录时服务器分配的id的映射。这必须包括在请求对象的原始createdIds参数中传递的所有创建ID,以及为新创建的记录添加的任何其他ID。

o sessionState: "String"

o 会话状态:“字符串”

The current value of the "state" string on the Session object, as described in Section 2. Clients may use this to detect if this object has changed and needs to be refetched.

会话对象上“状态”字符串的当前值,如第2节所述。客户机可以使用此选项来检测此对象是否已更改,是否需要重新蚀刻。

Unless otherwise specified, if the method call completed successfully, its response name is the same as the method name in the request.

除非另有规定,否则如果方法调用成功完成,则其响应名称与请求中的方法名称相同。

3.4.1. Example Response
3.4.1. 示例响应
                   {
                     "methodResponses": [
                       [ "method1", {
                         "arg1": 3,
                         "arg2": "foo"
                       }, "c1" ],
                       [ "method2", {
                         "isBlah": true
                       }, "c2" ],
                       [ "anotherResponseFromMethod2", {
                         "data": 10,
                         "yetmoredata": "Hello"
                       }, "c2"],
                       [ "error", {
                         "type":"unknownMethod"
                       }, "c3" ]
                     ],
                     "sessionState": "75128aab4b1b"
                   }
        
                   {
                     "methodResponses": [
                       [ "method1", {
                         "arg1": 3,
                         "arg2": "foo"
                       }, "c1" ],
                       [ "method2", {
                         "isBlah": true
                       }, "c2" ],
                       [ "anotherResponseFromMethod2", {
                         "data": 10,
                         "yetmoredata": "Hello"
                       }, "c2"],
                       [ "error", {
                         "type":"unknownMethod"
                       }, "c3" ]
                     ],
                     "sessionState": "75128aab4b1b"
                   }
        
3.5. Omitting Arguments
3.5. 省略论点

An argument to a method may be specified to have a default value. If omitted by the client, the server MUST treat the method call the same as if the default value had been specified. Similarly, the server MAY omit any argument in a response that has the default value.

方法的参数可以指定为具有默认值。如果客户机省略了,服务器必须将方法调用视为指定了默认值。类似地,服务器可以省略响应中具有默认值的任何参数。

Unless otherwise specified in a method description, null is the default value for any argument in a request or response where this is allowed by the type signature. Other arguments may only be omitted if an explicit default value is defined in the method description.

除非在方法描述中另有规定,否则null是类型签名允许的请求或响应中任何参数的默认值。只有在方法描述中定义了显式默认值时,才能忽略其他参数。

3.6. Errors
3.6. 错误

There are three different levels of granularity at which an error may be returned in JMAP.

JMAP中可能返回错误的粒度级别有三种。

When an API request is made, the request as a whole may be rejected due to rate limiting, malformed JSON, request for an unknown capability, etc. In this case, the entire request is rejected with an appropriate HTTP error response code and an additional JSON body with more detail for the client.

当发出API请求时,由于速率限制、JSON格式错误、未知功能请求等原因,整个请求可能会被拒绝。在这种情况下,整个请求会被拒绝,并带有适当的HTTP错误响应代码和附加的JSON正文,其中包含客户端的更多详细信息。

Provided the request itself is syntactically valid (the JSON is valid and when decoded, it matches the type signature of a Request object), the methods within it are executed sequentially by the server. Each

如果请求本身在语法上是有效的(JSON是有效的,并且在解码时,它与请求对象的类型签名相匹配),那么服务器将按顺序执行其中的方法。每个

method may individually fail, for example, if invalid arguments are given or an unknown method name is called.

方法可能会单独失败,例如,如果给定的参数无效或调用了未知的方法名称。

Finally, methods that make changes to the server state often act upon a number of different records within a single call. Each record change may be separately rejected with a SetError, as described in Section 5.3.

最后,对服务器状态进行更改的方法通常在单个调用中作用于多个不同的记录。如第5.3节所述,每项记录更改可能会被单独拒绝,并伴有SetError。

3.6.1. Request-Level Errors
3.6.1. 请求级错误

When an HTTP error response is returned to the client, the server SHOULD return a JSON "problem details" object as the response body, as per [RFC7807].

当HTTP错误响应返回到客户机时,服务器应该返回一个JSON“problem details”对象作为响应主体,如[RFC7807]所示。

The following problem types are defined:

定义了以下问题类型:

o "urn:ietf:params:jmap:error:unknownCapability" The client included a capability in the "using" property of the request that the server does not support.

o “urn:ietf:params:jmap:error:unknownCapability”客户端在请求的“using”属性中包含了服务器不支持的功能。

o "urn:ietf:params:jmap:error:notJSON" The content type of the request was not "application/json" or the request did not parse as I-JSON.

o “urn:ietf:params:jmap:error:notJSON”请求的内容类型不是“application/json”,或者请求没有解析为I-json。

o "urn:ietf:params:jmap:error:notRequest" The request parsed as JSON but did not match the type signature of the Request object.

o “urn:ietf:params:jmap:error:notRequest”请求被解析为JSON,但与请求对象的类型签名不匹配。

o "urn:ietf:params:jmap:error:limit" The request was not processed as it would have exceeded one of the request limits defined on the capability object, such as maxSizeRequest, maxCallsInRequest, or maxConcurrentRequests. A "limit" property MUST also be present on the "problem details" object, containing the name of the limit being applied.

o “urn:ietf:params:jmap:error:limit”请求未被处理,因为它将超过在功能对象上定义的一个请求限制,例如maxSizeRequest、maxCallsInRequest或maxConcurrentRequests。“问题详细信息”对象上还必须存在一个“限制”属性,该属性包含所应用限制的名称。

3.6.1.1. Example
3.6.1.1. 实例
       {
         "type": "urn:ietf:params:jmap:error:unknownCapability",
         "status": 400,
         "detail": "The Request object used capability
           'https://example.com/apis/foobar', which is not supported
           by this server."
       }
        
       {
         "type": "urn:ietf:params:jmap:error:unknownCapability",
         "status": 400,
         "detail": "The Request object used capability
           'https://example.com/apis/foobar', which is not supported
           by this server."
       }
        

Another example:

另一个例子:

     {
       "type": "urn:ietf:params:jmap:error:limit",
       "limit": "maxSizeRequest",
       "status": 400,
       "detail": "The request is larger than the server is willing to
                  process."
     }
        
     {
       "type": "urn:ietf:params:jmap:error:limit",
       "limit": "maxSizeRequest",
       "status": 400,
       "detail": "The request is larger than the server is willing to
                  process."
     }
        
3.6.2. Method-Level Errors
3.6.2. 方法级错误

If a method encounters an error, the appropriate "error" response MUST be inserted at the current point in the "methodResponses" array and, unless otherwise specified, further processing MUST NOT happen within that method call.

如果方法遇到错误,则必须在“methodResponses”数组的当前点插入相应的“error”响应,并且除非另有规定,否则在该方法调用中不得进行进一步处理。

Any further method calls in the request MUST then be processed as normal. Errors at the method level MUST NOT generate an HTTP-level error.

然后,请求中的任何其他方法调用都必须正常处理。方法级别的错误不能生成HTTP级别的错误。

An "error" response looks like this:

“错误”响应如下所示:

                         [ "error", {
                           "type": "unknownMethod"
                         }, "call-id" ]
        
                         [ "error", {
                           "type": "unknownMethod"
                         }, "call-id" ]
        

The response name is "error", and it MUST have a type property. Other properties may be present with further information; these are detailed in the error type descriptions where appropriate.

响应名称为“error”,并且必须具有type属性。其他财产可能会提供更多信息;在适当的情况下,这些在错误类型描述中有详细说明。

With the exception of when the "serverPartialFail" error is returned, the externally visible state of the server MUST NOT have changed if an error is returned at the method level.

除了返回“serverPartialFail”错误外,如果在方法级别返回错误,则服务器的外部可见状态不得更改。

The following error types are defined, which may be returned for any method call where appropriate:

定义了以下错误类型,在适当的情况下,可以为任何方法调用返回这些错误类型:

"serverUnavailable": Some internal server resource was temporarily unavailable. Attempting the same operation later (perhaps after a backoff with a random factor) may succeed.

“服务器不可用”:某些内部服务器资源暂时不可用。稍后尝试相同的操作(可能在使用随机因素进行退避之后)可能会成功。

"serverFail": An unexpected or unknown error occurred during the processing of the call. A "description" property should provide more details about the error. The method call made no changes to the server's state. Attempting the same operation again is expected to fail again. Contacting the service administrator is likely necessary to resolve this problem if it is persistent.

“serverFail”:处理调用期间发生意外或未知错误。“description”属性应提供有关错误的更多详细信息。方法调用没有更改服务器的状态。再次尝试相同的操作可能会再次失败。如果此问题持续存在,则可能需要联系服务管理员来解决此问题。

"serverPartialFail": Some, but not all, expected changes described by the method occurred. The client MUST resynchronise impacted data to determine server state. Use of this error is strongly discouraged.

“serverPartialFail”:发生了该方法所描述的部分(但不是全部)预期更改。客户端必须重新同步受影响的数据以确定服务器状态。强烈反对使用此错误。

"unknownMethod": The server does not recognise this method name.

“unknownMethod”:服务器无法识别此方法名称。

"invalidArguments": One of the arguments is of the wrong type or is otherwise invalid, or a required argument is missing. A "description" property MAY be present to help debug with an explanation of what the problem was. This is a non-localised string, and it is not intended to be shown directly to end users.

“invalidArguments”:其中一个参数的类型错误或无效,或者缺少必需的参数。可能会出现一个“description”属性来帮助调试,解释问题是什么。这是一个非本地化字符串,不打算直接向最终用户显示。

"invalidResultReference": The method used a result reference for one of its arguments (see Section 3.7), but this failed to resolve.

“invalidResultReference”:该方法对其参数之一使用了结果引用(请参见第3.7节),但未能解决此问题。

"forbidden": The method and arguments are valid, but executing the method would violate an Access Control List (ACL) or other permissions policy.

“禁止”:方法和参数有效,但执行该方法将违反访问控制列表(ACL)或其他权限策略。

"accountNotFound": The accountId does not correspond to a valid account.

“accountNotFound”:accountId与有效帐户不对应。

"accountNotSupportedByMethod": The accountId given corresponds to a valid account, but the account does not support this method or data type.

“accountNotSupportedByMethod”:给定的accountId对应于有效的帐户,但该帐户不支持此方法或数据类型。

"accountReadOnly": This method modifies state, but the account is read-only (as returned on the corresponding Account object in the JMAP Session resource).

“accountReadOnly”:此方法修改状态,但帐户是只读的(在JMAP会话资源中的相应帐户对象上返回)。

Further possible errors for a particular method are specified in the method descriptions.

特定方法的其他可能错误在方法说明中指定。

Further general errors MAY be defined in future RFCs. Should a client receive an error type it does not understand, it MUST treat it the same as the "serverFail" type.

进一步的一般性错误可能在未来的RFC中定义。如果客户端接收到它不理解的错误类型,它必须将其视为“serverFail”类型。

3.7. References to Previous Method Results
3.7. 参考以前的方法结果

To allow clients to make more efficient use of the network and avoid round trips, an argument to one method can be taken from the result of a previous method call in the same request.

为了让客户端更有效地利用网络并避免往返,可以从同一请求中的前一个方法调用的结果中获取一个方法的参数。

To do this, the client prefixes the argument name with "#" (an octothorpe). The value is a ResultReference object as described below. When processing a method call, the server MUST first check the arguments object for any names beginning with "#". If found, the result reference should be resolved and the value used as the "real"

为此,客户机在参数名称前面加上“#”(一个八进制字符)。该值是ResultReference对象,如下所述。处理方法调用时,服务器必须首先检查arguments对象中是否有以“#”开头的名称。如果找到,应解析结果引用,并将该值用作“实值”

argument. The method is then processed as normal. If any result reference fails to resolve, the whole method MUST be rejected with an "invalidResultReference" error. If an arguments object contains the same argument name in normal and referenced form (e.g., "foo" and "#foo"), the method MUST return an "invalidArguments" error.

论点然后,该方法将按正常方式进行处理。如果任何结果引用未能解析,则必须以“invalidResultReference”错误拒绝整个方法。如果arguments对象包含普通和引用形式的相同参数名称(例如,“foo”和“#foo”),则该方法必须返回“invalidArguments”错误。

A *ResultReference* object has the following properties:

*ResultReference*对象具有以下属性:

o resultOf: "String"

o resultOf:“字符串”

The method call id (see Section 3.2) of a previous method call in the current request.

当前请求中先前方法调用的方法调用id(参见第3.2节)。

o name: "String"

o 名称:“字符串”

The required name of a response to that method call.

对该方法调用的响应所需的名称。

o path: "String"

o 路径:“字符串”

A pointer into the arguments of the response selected via the name and resultOf properties. This is a JSON Pointer [RFC6901], except it also allows the use of "*" to map through an array (see the description below).

指向通过name和resultOf属性选择的响应参数的指针。这是一个JSON指针[RFC6901],但它也允许使用“*”通过数组进行映射(请参见下面的描述)。

To resolve:

解决:

1. Find the first response with a method call id identical to the "resultOf" property of the ResultReference in the "methodResponses" array from previously processed method calls in the same request. If none, evaluation fails.

1. 从同一请求中先前处理的方法调用中,在“methodResponses”数组中查找方法调用id与ResultReference的“ResultTof”属性相同的第一个响应。如果没有,则评估失败。

2. If the response name is not identical to the "name" property of the ResultReference, evaluation fails.

2. 如果响应名称与ResultReference的“name”属性不相同,则计算失败。

3. Apply the "path" to the arguments object of the response (the second item in the response array) following the JSON Pointer algorithm [RFC6901], except with the following addition in "Evaluation" (see Section 4):

3. 在JSON指针算法[RFC6901]之后,将“路径”应用于响应的arguments对象(响应数组中的第二项),除了在“求值”中添加以下内容外(参见第4节):

If the currently referenced value is a JSON array, the reference token may be exactly the single character "*", making the new referenced value the result of applying the rest of the JSON Pointer tokens to every item in the array and returning the results in the same order in a new array. If the result of applying the rest of the pointer tokens to each item was itself an array, the contents of this array are added to the output rather than the array itself (i.e., the result is flattened from an array of arrays to a single array). If the result of applying

如果当前引用的值是一个JSON数组,则引用标记可能正好是单个字符“*”,使新引用的值成为将其余JSON指针标记应用于数组中的每个项并在新数组中以相同顺序返回结果的结果。如果将其余指针标记应用于每个项的结果本身就是一个数组,则此数组的内容将添加到输出中,而不是数组本身(即,结果将从数组数组展平到单个数组)。如果申请的结果

the rest of the pointer tokens to a value was itself an array, its items should be included individually in the output rather than including the array itself (i.e., the result is flattened from an array of arrays to a single array).

指向某个值的其余指针标记本身就是一个数组,其项应单独包含在输出中,而不是包含数组本身(即,结果从数组数组变为单个数组)。

As a simple example, suppose we have the following API request "methodCalls":

作为一个简单的示例,假设我们有以下API请求“methodCalls”:

                      [[ "Foo/changes", {
                          "accountId": "A1",
                          "sinceState": "abcdef"
                      }, "t0" ],
                      [ "Foo/get", {
                          "accountId": "A1",
                          "#ids": {
                              "resultOf": "t0",
                              "name": "Foo/changes",
                              "path": "/created"
                          }
                      }, "t1" ]]
        
                      [[ "Foo/changes", {
                          "accountId": "A1",
                          "sinceState": "abcdef"
                      }, "t0" ],
                      [ "Foo/get", {
                          "accountId": "A1",
                          "#ids": {
                              "resultOf": "t0",
                              "name": "Foo/changes",
                              "path": "/created"
                          }
                      }, "t1" ]]
        

After executing the first method call, the "methodResponses" array is:

执行第一个方法调用后,“methodResponses”数组为:

                      [[ "Foo/changes", {
                          "accountId": "A1",
                          "oldState": "abcdef",
                          "newState": "123456",
                          "hasMoreChanges": false,
                          "created": [ "f1", "f4" ],
                          "updated": [],
                          "destroyed": []
                      }, "t0" ]]
        
                      [[ "Foo/changes", {
                          "accountId": "A1",
                          "oldState": "abcdef",
                          "newState": "123456",
                          "hasMoreChanges": false,
                          "created": [ "f1", "f4" ],
                          "updated": [],
                          "destroyed": []
                      }, "t0" ]]
        

To execute the "Foo/get" call, we look through the arguments and find there is one with a "#" prefix. To resolve this, we apply the algorithm above:

要执行“Foo/get”调用,我们查看参数,发现有一个参数的前缀是“#”。为解决此问题,我们采用上述算法:

1. Find the first response with method call id "t0". The "Foo/ changes" response fulfils this criterion.

1. 查找方法调用id为“t0”的第一个响应。“Foo/changes”响应满足此标准。

2. Check that the response name is the same as in the result reference. It is, so this is fine.

2. 检查响应名称是否与结果引用中的名称相同。是的,所以这很好。

3. Apply the "path" as a JSON Pointer to the arguments object. This simply selects the "created" property, so the result of evaluating is: [ "f1", "f4" ].

3. 将“路径”作为JSON指针应用于arguments对象。这只是选择“created”属性,因此计算结果为:[“f1”、“f4”]。

The JMAP server now continues to process the "Foo/get" call as though the arguments were:

JMAP服务器现在继续处理“Foo/get”调用,就好像参数是:

                         {
                             "accountId": "A1",
                             "ids": [ "f1", "f4" ]
                         }
        
                         {
                             "accountId": "A1",
                             "ids": [ "f1", "f4" ]
                         }
        

Now, a more complicated example using the JMAP Mail data model: fetch the "from"/"date"/"subject" for every Email in the first 10 Threads in the inbox (sorted newest first):

现在,使用JMAP邮件数据模型的一个更复杂的示例是:为收件箱中前10个线程中的每封电子邮件获取“发件人”/“日期”/“主题”(排序最新的优先):

      [[ "Email/query", {
        "accountId": "A1",
        "filter": { "inMailbox": "id_of_inbox" },
        "sort": [{ "property": "receivedAt", "isAscending": false }],
        "collapseThreads": true,
        "position": 0,
        "limit": 10,
        "calculateTotal": true
      }, "t0" ],
      [ "Email/get", {
        "accountId": "A1",
        "#ids": {
          "resultOf": "t0",
          "name": "Email/query",
          "path": "/ids"
        },
        "properties": [ "threadId" ]
      }, "t1" ],
      [ "Thread/get", {
        "accountId": "A1",
        "#ids": {
          "resultOf": "t1",
          "name": "Email/get",
          "path": "/list/*/threadId"
        }
      }, "t2" ],
      [ "Email/get", {
        "accountId": "A1",
        "#ids": {
          "resultOf": "t2",
          "name": "Thread/get",
          "path": "/list/*/emailIds"
        },
        "properties": [ "from", "receivedAt", "subject" ]
      }, "t3" ]]
        
      [[ "Email/query", {
        "accountId": "A1",
        "filter": { "inMailbox": "id_of_inbox" },
        "sort": [{ "property": "receivedAt", "isAscending": false }],
        "collapseThreads": true,
        "position": 0,
        "limit": 10,
        "calculateTotal": true
      }, "t0" ],
      [ "Email/get", {
        "accountId": "A1",
        "#ids": {
          "resultOf": "t0",
          "name": "Email/query",
          "path": "/ids"
        },
        "properties": [ "threadId" ]
      }, "t1" ],
      [ "Thread/get", {
        "accountId": "A1",
        "#ids": {
          "resultOf": "t1",
          "name": "Email/get",
          "path": "/list/*/threadId"
        }
      }, "t2" ],
      [ "Email/get", {
        "accountId": "A1",
        "#ids": {
          "resultOf": "t2",
          "name": "Thread/get",
          "path": "/list/*/emailIds"
        },
        "properties": [ "from", "receivedAt", "subject" ]
      }, "t3" ]]
        

After executing the first 3 method calls, the "methodResponses" array might be:

执行前3个方法调用后,“methodResponses”数组可能是:

       [[ "Email/query", {
           "accountId": "A1",
           "queryState": "abcdefg",
           "canCalculateChanges": true,
           "position": 0,
           "total": 101,
           "ids": [ "msg1023", "msg223", "msg110", "msg93", "msg91",
               "msg38", "msg36", "msg33", "msg11", "msg1" ]
       }, "t0" ],
       [ "Email/get", {
           "accountId": "A1",
           "state": "123456",
           "list": [{
               "id": "msg1023",
               "threadId": "trd194"
           }, {
               "id": "msg223",
               "threadId": "trd114"
           },
           ...
           ],
           "notFound": []
       }, "t1" ],
       [ "Thread/get", {
           "accountId": "A1",
           "state": "123456",
           "list": [{
               "id": "trd194",
               "emailIds": [ "msg1020", "msg1021", "msg1023" ]
           }, {
               "id": "trd114",
               "emailIds": [ "msg201", "msg223" ]
           },
           ...
           ],
           "notFound": []
       }, "t2" ]]
        
       [[ "Email/query", {
           "accountId": "A1",
           "queryState": "abcdefg",
           "canCalculateChanges": true,
           "position": 0,
           "total": 101,
           "ids": [ "msg1023", "msg223", "msg110", "msg93", "msg91",
               "msg38", "msg36", "msg33", "msg11", "msg1" ]
       }, "t0" ],
       [ "Email/get", {
           "accountId": "A1",
           "state": "123456",
           "list": [{
               "id": "msg1023",
               "threadId": "trd194"
           }, {
               "id": "msg223",
               "threadId": "trd114"
           },
           ...
           ],
           "notFound": []
       }, "t1" ],
       [ "Thread/get", {
           "accountId": "A1",
           "state": "123456",
           "list": [{
               "id": "trd194",
               "emailIds": [ "msg1020", "msg1021", "msg1023" ]
           }, {
               "id": "trd114",
               "emailIds": [ "msg201", "msg223" ]
           },
           ...
           ],
           "notFound": []
       }, "t2" ]]
        

To execute the final "Email/get" call, we look through the arguments and find there is one with a "#" prefix. To resolve this, we apply the algorithm:

要执行最后一个“Email/get”调用,我们查看参数,发现有一个参数的前缀是“#”。为解决此问题,我们采用以下算法:

1. Find the first response with method call id "t2". The "Thread/ get" response fulfils this criterion.

1. 查找方法调用id为“t2”的第一个响应。“Thread/get”响应满足这个标准。

2. "Thread/get" is the name specified in the result reference, so this is fine.

2. “Thread/get”是在result引用中指定的名称,因此这很好。

3. Apply the "path" as a JSON Pointer to the arguments object. Token by token:

3. 将“路径”作为JSON指针应用于arguments对象。令牌对令牌:

1. "list": get the array of thread objects

1. “列表”:获取线程对象的数组

2. "*": for each of the items in the array:

2. “*”:对于数组中的每个项:

a. "emailIds": get the array of Email ids

a. “EmailID”:获取电子邮件ID数组

b. Concatenate these into a single array of all the ids in the result.

b. 将它们连接到结果中所有ID的单个数组中。

The JMAP server now continues to process the "Email/get" call as though the arguments were:

JMAP服务器现在继续处理“Email/get”调用,就好像参数是:

{
    "accountId": "A1",
    "ids": [ "msg1020", "msg1021", "msg1023", "msg201", "msg223", ... ],
    "properties": [ "from", "receivedAt", "subject" ]
}
        
{
    "accountId": "A1",
    "ids": [ "msg1020", "msg1021", "msg1023", "msg201", "msg223", ... ],
    "properties": [ "from", "receivedAt", "subject" ]
}
        

The ResultReference performs a similar role to that of the creation id, in that it allows a chained method call to refer to information not available when the request is generated. However, they are different things and not interchangeable; the only commonality is the octothorpe used to indicate them.

ResultReference执行与创建id类似的角色,因为它允许链式方法调用引用在生成请求时不可用的信息。但是,它们是不同的东西,不能互换;唯一的共同点是用来表示它们的八头梭鱼。

3.8. Localisation of User-Visible Strings
3.8. 用户可见字符串的本地化

If returning a custom string to be displayed to the user, for example, an error message, the server SHOULD use information from the Accept-Language header of the request (as defined in Section 5.3.5 of [RFC7231]) to choose the best available localisation. The Content-Language header of the response (see Section 3.1.3.2 of [RFC7231]) SHOULD indicate the language being used for user-visible strings.

如果返回要显示给用户的自定义字符串,例如错误消息,服务器应使用请求的Accept Language标头中的信息(如[RFC7231]第5.3.5节中的定义)来选择最佳可用本地化。响应的内容语言标题(见[RFC7231]第3.1.3.2节)应指示用户可见字符串使用的语言。

For example, suppose a request was made with the following header:

例如,假设使用以下标头发出请求:

       Accept-Language: fr-CH, fr;q=0.9, de;q=0.8, en;q=0.7, *;q=0.5
        
       Accept-Language: fr-CH, fr;q=0.9, de;q=0.8, en;q=0.7, *;q=0.5
        

and a method generated an error to display to the user. The server has translations of the error message in English and German. Looking at the Accept-Language header, the user's preferred language is French. Since we don't have a translation for this, we look at the

以及生成错误以显示给用户的方法。服务器有英文和德文的错误消息翻译。查看Accept Language标头,用户首选的语言是法语。因为我们没有这方面的翻译,我们看

next most preferred, which is German. We have a German translation, so the server returns this and indicates the language chosen in a Content-Language header like so:

下一个最受欢迎的是德语。我们有一个德语翻译,因此服务器会返回此内容,并指示在内容语言标题中选择的语言,如下所示:

Content-Language: de

内容语言:de

3.9. Security
3.9. 安全

As always, the server must be strict about data received from the client. Arguments need to be checked for validity; a malicious user could attempt to find an exploit through the API. In case of invalid arguments (unknown/insufficient/wrong type for data, etc.), the method MUST return an "invalidArguments" error and terminate.

和往常一样,服务器必须严格控制从客户端接收的数据。需要检查参数的有效性;恶意用户可能试图通过API查找漏洞。如果参数无效(数据类型未知/不足/错误等),该方法必须返回“invalidArguments”错误并终止。

3.10. Concurrency
3.10. 并发性

Method calls within a single request MUST be executed in order. However, method calls from different concurrent API requests may be interleaved. This means that the data on the server may change between two method calls within a single API request.

单个请求中的方法调用必须按顺序执行。然而,来自不同并发API请求的方法调用可能是交错的。这意味着服务器上的数据可能在单个API请求中的两个方法调用之间发生更改。

4. The Core/echo Method
4. 岩心/回声法

The "Core/echo" method returns exactly the same arguments as it is given. It is useful for testing if you have a valid authenticated connection to a JMAP API endpoint.

“Core/echo”方法返回与给定参数完全相同的参数。如果您拥有到JMAPAPI端点的有效身份验证连接,那么它对于测试非常有用。

4.1. Example
4.1. 实例

Request:

请求:

                             [[ "Core/echo", {
                               "hello": true,
                               "high": 5
                             }, "b3ff" ]]
        
                             [[ "Core/echo", {
                               "hello": true,
                               "high": 5
                             }, "b3ff" ]]
        

Response:

答复:

                             [[ "Core/echo", {
                               "hello": true,
                               "high": 5
                             }, "b3ff" ]]
        
                             [[ "Core/echo", {
                               "hello": true,
                               "high": 5
                             }, "b3ff" ]]
        
5. Standard Methods and Naming Convention
5. 标准方法和命名约定

JMAP provides a uniform interface for creating, retrieving, updating, and deleting objects of a particular type. For a "Foo" data type, records of that type would be fetched via a "Foo/get" call and modified via a "Foo/set" call. Delta updates may be fetched via a "Foo/changes" call. These methods all follow a standard format as described below.

JMAP为创建、检索、更新和删除特定类型的对象提供了统一的接口。对于“Foo”数据类型,该类型的记录将通过“Foo/get”调用获取,并通过“Foo/set”调用进行修改。增量更新可以通过“Foo/changes”调用获取。这些方法都遵循如下所述的标准格式。

Some types may not have all these methods. Specifications defining types MUST specify which methods are available for the type.

某些类型可能没有所有这些方法。定义类型的规范必须指定哪些方法可用于该类型。

5.1. /get
5.1. /得到

Objects of type Foo are fetched via a call to "Foo/get".

Foo类型的对象通过调用“Foo/get”获取。

It takes the following arguments:

它采用以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account to use.

要使用的帐户的id。

o ids: "Id[]|null"

o Id:“Id[]|空”

The ids of the Foo objects to return. If null, then *all* records of the data type are returned, if this is supported for that data type and the number of records does not exceed the "maxObjectsInGet" limit.

要返回的Foo对象的id。如果为null,则返回该数据类型的*所有*记录,前提是该数据类型支持此操作且记录数不超过“maxObjectsInGet”限制。

o properties: "String[]|null"

o 属性:“字符串[]空”

If supplied, only the properties listed in the array are returned for each Foo object. If null, all properties of the object are returned. The id property of the object is *always* returned, even if not explicitly requested. If an invalid property is requested, the call MUST be rejected with an "invalidArguments" error.

如果提供,则仅为每个Foo对象返回数组中列出的属性。如果为null,则返回对象的所有属性。即使未明确请求,也会*始终*返回对象的id属性。如果请求的属性无效,则必须以“invalidArguments”错误拒绝调用。

The response has the following arguments:

响应具有以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account used for the call.

用于呼叫的帐户的id。

o state: "String"

o 状态:“字符串”

A (preferably short) string representing the state on the server for *all* the data of this type in the account (not just the objects returned in this call). If the data changes, this string MUST change. If the Foo data is unchanged, servers SHOULD return the same state string on subsequent requests for this data type. When a client receives a response with a different state string to a previous call, it MUST either throw away all currently cached objects for the type or call "Foo/changes" to get the exact changes.

一个(最好是短的)字符串,表示帐户中此类型的数据(而不仅仅是此调用中返回的对象)在服务器上的状态。如果数据更改,则此字符串必须更改。如果Foo数据未更改,服务器应在后续请求此数据类型时返回相同的状态字符串。当客户机接收到与前一个调用具有不同状态字符串的响应时,它必须丢弃该类型当前缓存的所有对象,或者调用“Foo/changes”以获得确切的更改。

o list: "Foo[]"

o 列表:“Foo[]”

An array of the Foo objects requested. This is the *empty array* if no objects were found or if the "ids" argument passed in was also an empty array. The results MAY be in a different order to the "ids" in the request arguments. If an identical id is included more than once in the request, the server MUST only include it once in either the "list" or the "notFound" argument of the response.

请求的Foo对象数组。如果未找到任何对象或传入的“ids”参数也是空数组,则这是*空数组*。结果的顺序可能与请求参数中的“id”不同。如果请求中多次包含相同的id,则服务器只能在响应的“list”或“notFound”参数中包含一次。

o notFound: "Id[]"

o 未找到:“Id[]”

This array contains the ids passed to the method for records that do not exist. The array is empty if all requested ids were found or if the "ids" argument passed in was either null or an empty array.

此数组包含传递给不存在记录的方法的ID。如果找到所有请求的ID,或者传入的“ids”参数为null或空数组,则该数组为空。

The following additional error may be returned instead of the "Foo/ get" response:

可能会返回以下附加错误,而不是“Foo/get”响应:

"requestTooLarge": The number of ids requested by the client exceeds the maximum number the server is willing to process in a single method call.

“requestTooLarge”:客户端请求的ID数量超过了服务器在单个方法调用中愿意处理的最大数量。

5.2. /changes
5.2. /变化

When the state of the set of Foo records in an account changes on the server (whether due to creation, updates, or deletion), the "state" property of the "Foo/get" response will change. The "Foo/changes" method allows a client to efficiently update the state of its Foo cache to match the new state on the server. It takes the following arguments:

当服务器上的帐户中的一组Foo记录的状态发生更改时(无论是由于创建、更新还是删除),响应“Foo/get”的“state”属性将发生更改。“Foo/changes”方法允许客户端高效地更新其Foo缓存的状态,以匹配服务器上的新状态。它采用以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account to use.

要使用的帐户的id。

o sinceState: "String"

o sinceState:“字符串”

The current state of the client. This is the string that was returned as the "state" argument in the "Foo/get" response. The server will return the changes that have occurred since this state.

客户端的当前状态。这是作为“Foo/get”响应中的“state”参数返回的字符串。服务器将返回自此状态以来发生的更改。

o maxChanges: "UnsignedInt|null"

o maxChanges:“UnsignedInt | null”

The maximum number of ids to return in the response. The server MAY choose to return fewer than this value but MUST NOT return more. If not given by the client, the server may choose how many to return. If supplied by the client, the value MUST be a positive integer greater than 0. If a value outside of this range is given, the server MUST reject the call with an "invalidArguments" error.

响应中要返回的最大ID数。服务器可以选择返回小于此值的值,但不能返回更多值。如果客户端没有给出,服务器可以选择返回多少。如果由客户端提供,则该值必须是大于0的正整数。如果给定的值超出此范围,服务器必须以“invalidArguments”错误拒绝调用。

The response has the following arguments:

响应具有以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account used for the call.

用于呼叫的帐户的id。

o oldState: "String"

o 旧状态:“字符串”

This is the "sinceState" argument echoed back; it's the state from which the server is returning changes.

这是回响的“新国家”论调;这是服务器返回更改的状态。

o newState: "String"

o 新闻状态:“字符串”

This is the state the client will be in after applying the set of changes to the old state.

这是将更改集应用到旧状态后客户端将处于的状态。

o hasMoreChanges: "Boolean"

o hasMoreChanges:“布尔”

If true, the client may call "Foo/changes" again with the "newState" returned to get further updates. If false, "newState" is the current server state.

如果为true,客户端可能会再次调用“Foo/changes”,并返回“newState”以获取进一步更新。如果为false,“newState”是当前服务器状态。

o created: "Id[]"

o 已创建:“Id[]”

An array of ids for records that have been created since the old state.

自旧状态以来创建的记录的ID数组。

o updated: "Id[]"

o 更新:“Id[]”

An array of ids for records that have been updated since the old state.

自旧状态以来已更新的记录的ID数组。

o destroyed: "Id[]"

o 销毁:“Id[]”

An array of ids for records that have been destroyed since the old state.

自旧状态以来已销毁的记录的ID数组。

If a record has been created AND updated since the old state, the server SHOULD just return the id in the "created" list but MAY return it in the "updated" list as well.

如果记录是在旧状态之后创建和更新的,则服务器只应在“已创建”列表中返回id,但也可以在“已更新”列表中返回id。

If a record has been updated AND destroyed since the old state, the server SHOULD just return the id in the "destroyed" list but MAY return it in the "updated" list as well.

如果记录在旧状态之后已经更新和销毁,服务器应该只在“已销毁”列表中返回id,但也可以在“已更新”列表中返回id。

If a record has been created AND destroyed since the old state, the server SHOULD remove the id from the response entirely. However, it MAY include it in just the "destroyed" list or in both the "destroyed" and "created" lists.

如果在旧状态之后创建并销毁了记录,则服务器应完全从响应中删除该id。但是,它可能只将其包括在“已销毁”列表中,或同时包括在“已销毁”和“已创建”列表中。

If a "maxChanges" is supplied, or set automatically by the server, the server MUST ensure the number of ids returned across "created", "updated", and "destroyed" does not exceed this limit. If there are more changes than this between the client's state and the current server state, the server SHOULD generate an update to take the client to an intermediate state, from which the client can continue to call "Foo/changes" until it is fully up to date. If it is unable to calculate an intermediate state, it MUST return a "cannotCalculateChanges" error response instead.

如果服务器提供或自动设置了“maxChanges”,则服务器必须确保在“已创建”、“已更新”和“已销毁”之间返回的ID数不超过此限制。如果客户机状态和当前服务器状态之间的更改超过此值,则服务器应生成更新以将客户机带到中间状态,客户机可以从中间状态继续调用“Foo/changes”,直到完全更新为止。如果无法计算中间状态,则必须返回“cannotCalculateChanges”错误响应。

When generating intermediate states, the server may choose how to divide up the changes. For many types, it will provide a better user experience to return the more recent changes first, as this is more likely to be what the user is most interested in. The client can then continue to page in the older changes while the user is viewing the newer data. For example, suppose a server went through the following states:

在生成中间状态时,服务器可以选择如何划分更改。对于许多类型,它将提供更好的用户体验,首先返回最新的更改,因为这更可能是用户最感兴趣的。然后,当用户查看较新的数据时,客户端可以继续在较旧的更改中分页。例如,假设服务器经历以下状态:

                           A -> B -> C -> D -> E
        
                           A -> B -> C -> D -> E
        

And a client asks for changes from state "B". The server might first get the ids of records created, updated, or destroyed between states D and E, returning them with:

客户端请求更改状态“B”。服务器可能首先获取在状态D和E之间创建、更新或销毁的记录的ID,并返回它们:

state: "B-D-E" hasMoreChanges: true

状态:“B-D-E”有更多变化:正确

The client will then ask for the change from state "B-D-E", and the server can return the changes between states C and D, returning:

然后,客户端将要求更改状态“B-D-E”,服务器可以返回状态C和D之间的更改,返回:

state: "B-C-E" hasMoreChanges: true

状态:“B-C-E”有更多变化:正确

Finally, the client will request the changes from "B-C-E", and the server can return the changes between states B and C, returning:

最后,客户端将从“B-C-E”请求更改,服务器可以返回状态B和C之间的更改,返回:

state: "E" hasMoreChanges: false

状态:“E”有更多更改:false

Should the state on the server be modified in the middle of all this (to "F"), the server still does the same, but now when the update to state "E" is returned, it would indicate that it still has more changes for the client to fetch.

如果服务器上的状态在所有这些(到“f”)的中间被修改,服务器仍然是相同的,但是现在当更新到状态“E”时,它将指示它仍然有更多的改变给客户端来获取。

Where multiple changes to a record are split across different intermediate states, the server MUST NOT return a record as created after a response that deems it as updated or destroyed, and it MUST NOT return a record as destroyed before a response that deems it as created or updated. The server may have to coalesce multiple changes to a record to satisfy this requirement.

如果一个记录的多个更改被分割到不同的中间状态,则服务器不得返回在认为已更新或已销毁的响应之后创建的记录,也不得返回在认为已创建或已更新的响应之前已销毁的记录。服务器可能必须合并对记录的多个更改以满足此要求。

The following additional errors may be returned instead of the "Foo/ changes" response:

可能会返回以下附加错误,而不是“Foo/changes”响应:

"cannotCalculateChanges": The server cannot calculate the changes from the state string given by the client. Usually, this is due to the client's state being too old or the server being unable to produce an update to an intermediate state when there are too many updates. The client MUST invalidate its Foo cache.

“cannotCalculateChanges”:服务器无法根据客户端提供的状态字符串计算更改。通常,这是由于客户端的状态太旧,或者当更新太多时,服务器无法生成到中间状态的更新。客户端必须使其Foo缓存无效。

Maintaining state to allow calculation of "Foo/changes" can be expensive for the server, but always returning "cannotCalculateChanges" severely increases network traffic and resource usage for the client. To allow efficient sync, servers SHOULD be able to calculate changes from any state string that was given to a client within the last 30 days (but of course may support calculating updates from states older than this).

维护状态以允许计算“Foo/changes”对服务器来说可能代价高昂,但始终返回“cannotCalculateChanges”会严重增加客户端的网络流量和资源使用。为了实现高效的同步,服务器应该能够计算在过去30天内提供给客户端的任何状态字符串的更改(当然,可能支持计算比这更早的状态的更新)。

5.3. /set
5.3. /设置

Modifying the state of Foo objects on the server is done via the "Foo/set" method. This encompasses creating, updating, and destroying Foo records. This allows the server to sort out ordering and dependencies that may exist if doing multiple operations at once (for example, to ensure there is always a minimum number of a certain record type).

通过“Foo/set”方法修改服务器上Foo对象的状态。这包括创建、更新和销毁Foo记录。这允许服务器对同时执行多个操作时可能存在的顺序和依赖项进行排序(例如,确保某个记录类型的数量始终为最小)。

The "Foo/set" method takes the following arguments:

“Foo/set”方法采用以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account to use.

要使用的帐户的id。

o ifInState: "String|null"

o i说明:“字符串|空”

This is a state string as returned by the "Foo/get" method (representing the state of all objects of this type in the account). If supplied, the string must match the current state; otherwise, the method will be aborted and a "stateMismatch" error returned. If null, any changes will be applied to the current state.

这是由“Foo/get”方法返回的状态字符串(表示帐户中此类型的所有对象的状态)。如果提供,字符串必须与当前状态匹配;否则,该方法将被中止,并返回“StateMatch”错误。如果为null,则任何更改都将应用于当前状态。

o create: "Id[Foo]|null"

o 创建:“Id[Foo]| null”

A map of a *creation id* (a temporary id set by the client) to Foo objects, or null if no objects are to be created.

*创建id*(客户端设置的临时id)到Foo对象的映射,如果不创建对象,则为null。

The Foo object type definition may define default values for properties. Any such property may be omitted by the client.

Foo对象类型定义可以定义属性的默认值。客户可省略任何此类财产。

The client MUST omit any properties that may only be set by the server (for example, the "id" property on most object types).

客户端必须省略任何只能由服务器设置的属性(例如,大多数对象类型上的“id”属性)。

o update: "Id[PatchObject]|null"

o 更新:“Id[PatchObject]| null”

A map of an id to a Patch object to apply to the current Foo object with that id, or null if no objects are to be updated.

id到修补程序对象的映射,以应用于具有该id的当前Foo对象,如果没有要更新的对象,则为null。

A *PatchObject* is of type "String[*]" and represents an unordered set of patches. The keys are a path in JSON Pointer format [RFC6901], with an implicit leading "/" (i.e., prefix each key with "/" before applying the JSON Pointer evaluation algorithm).

*PatchObject*的类型为“String[*]”,表示无序的补丁集。这些键是JSON指针格式[RFC6901]的路径,带有一个隐式的前导“/”(即,在应用JSON指针计算算法之前,在每个键前面加“/”)。

All paths MUST also conform to the following restrictions; if there is any violation, the update MUST be rejected with an "invalidPatch" error:

所有路径还必须符合以下限制:;如果存在任何冲突,则必须拒绝更新,并出现“invalidPatch”错误:

* The pointer MUST NOT reference inside an array (i.e., you MUST NOT insert/delete from an array; the array MUST be replaced in its entirety instead).

* 指针不能在数组中引用(即,不能从数组中插入/删除;必须替换整个数组)。

* All parts prior to the last (i.e., the value after the final slash) MUST already exist on the object being patched.

* 最后一个之前的所有零件(即,最后斜杠后的值)必须已经存在于正在修补的对象上。

* There MUST NOT be two patches in the PatchObject where the pointer of one is the prefix of the pointer of the other, e.g., "alerts/1/offset" and "alerts".

* PatchObject中不得有两个补丁,其中一个的指针是另一个指针的前缀,例如“警报/1/偏移”和“警报”。

The value associated with each pointer determines how to apply that patch:

与每个指针关联的值确定如何应用该修补程序:

* If null, set to the default value if specified for this property; otherwise, remove the property from the patched object. If the key is not present in the parent, this a no-op.

* 如果为空,则设置为默认值(如果为此属性指定);否则,请从修补对象中删除该属性。如果父项中不存在密钥,则表示不操作。

* Anything else: The value to set for this property (this may be a replacement or addition to the object being patched).

* 其他任何内容:为此属性设置的值(这可能是对正在修补的对象的替换或添加)。

Any server-set properties MAY be included in the patch if their value is identical to the current server value (before applying the patches to the object). Otherwise, the update MUST be rejected with an "invalidProperties" SetError.

如果任何服务器集属性的值与当前服务器值相同(在将修补程序应用于对象之前),则修补程序中可能包含这些属性。否则,必须以“invalidProperties”设置错误拒绝更新。

This patch definition is designed such that an entire Foo object is also a valid PatchObject. The client may choose to optimise network usage by just sending the diff or may send the whole object; the server processes it the same either way.

此修补程序定义的设计使整个Foo对象也是有效的修补程序对象。客户端可以选择通过发送差异来优化网络使用,也可以发送整个对象;服务器以同样的方式处理它。

o destroy: "Id[]|null"

o 销毁:“Id[]|空”

A list of ids for Foo objects to permanently delete, or null if no objects are to be destroyed.

要永久删除的Foo对象的ID列表,如果没有要销毁的对象,则为null。

Each creation, modification, or destruction of an object is considered an atomic unit. It is permissible for the server to commit changes to some objects but not others; however, it MUST NOT only commit part of an update to a single record (e.g., update a "name" property but not a "count" property, if both are supplied in the update object).

对象的每次创建、修改或破坏都被视为一个原子单位。允许服务器提交对某些对象的更改,但不能提交其他对象的更改;但是,它必须不仅将更新的一部分提交给单个记录(例如,更新“name”属性,而不是“count”属性,如果更新对象中同时提供了这两个属性)。

The final state MUST be valid after the "Foo/set" is finished; however, the server may have to transition through invalid intermediate states (not exposed to the client) while processing the individual create/update/destroy requests. For example, suppose there is a "name" property that must be unique. A single method call

“Foo/set”完成后,最终状态必须有效;但是,在处理单个创建/更新/销毁请求时,服务器可能必须通过无效的中间状态(不向客户端公开)进行转换。例如,假设有一个“name”属性必须是唯一的。单个方法调用

could rename an object A => B and simultaneously rename another object B => A. If the final state is valid, this is allowed. Otherwise, each creation, modification, or destruction of an object should be processed sequentially and accepted/rejected based on the current server state.

可以重命名一个对象A=>B,同时重命名另一个对象B=>A。如果最终状态有效,这是允许的。否则,对象的每次创建、修改或销毁都应按顺序处理,并根据当前服务器状态接受/拒绝。

If a create, update, or destroy is rejected, the appropriate error MUST be added to the notCreated/notUpdated/notDestroyed property of the response, and the server MUST continue to the next create/update/ destroy. It does not terminate the method.

如果拒绝创建、更新或销毁,则必须将相应的错误添加到响应的notCreated/NotUpdate/notDestroyed属性中,并且服务器必须继续执行下一次创建/更新/销毁。它不会终止该方法。

If an id given cannot be found, the update or destroy MUST be rejected with a "notFound" set error.

如果找不到给定的id,则必须以“notFound”设置错误拒绝更新或销毁。

The server MAY skip an update (rejecting it with a "willDestroy" SetError) if that object is destroyed in the same /set request.

如果对象在同一个/set请求中被销毁,服务器可能会跳过更新(以“willDestroy”SetError拒绝更新)。

Some records may hold references to other records (foreign keys). That reference may be set (via create or update) in the same request as the referenced record is created. To do this, the client refers to the new record using its creation id prefixed with a "#". The order of the method calls in the request by the client MUST be such that the record being referenced is created in the same or an earlier call. Thus, the server never has to look ahead. Instead, while processing a request, the server MUST keep a simple map for the duration of the request of creation id to record id for each newly created record, so it can substitute in the correct value if necessary in later method calls. In the case of records with references to the same type, the server MUST order the creates and updates within a single method call so that creates happen before their creation ids are referenced by another create/update/destroy in the same call.

某些记录可能包含对其他记录的引用(外键)。可在创建引用记录的同一请求中设置该引用(通过创建或更新)。为此,客户机使用前缀为“#”的创建id引用新记录。客户端请求中方法调用的顺序必须确保在同一调用或更早的调用中创建所引用的记录。因此,服务器永远不必向前看。相反,在处理请求时,服务器必须在每个新创建的记录的创建id到记录id的请求期间保持一个简单的映射,以便在以后的方法调用中必要时可以替换为正确的值。对于引用相同类型的记录,服务器必须在单个方法调用中对创建和更新进行排序,以便在创建ID被同一调用中的另一个创建/更新/销毁引用之前进行创建。

Creation ids are not scoped by type but are a single map for all types. A client SHOULD NOT reuse a creation id anywhere in the same API request. If a creation id is reused, the server MUST map the creation id to the most recently created item with that id. To allow easy proxying of API requests, an initial set of creation id to real id values may be passed with a request (see "The Request Object", Section 3.3) and the final state of the map passed out with the response (see "The Response Object", Section 3.4).

创建ID不按类型划分范围,而是所有类型的单一映射。客户端不应在同一API请求中的任何位置重用创建id。如果重复使用创建id,服务器必须将创建id映射到具有该id的最近创建的项。为了方便代理API请求,可以通过请求(请参阅“请求对象”,第3.3节)将创建id的初始集传递到真实id值,并随响应传递映射的最终状态(请参阅“响应对象”,第3.4节)。

The response has the following arguments:

响应具有以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account used for the call.

用于呼叫的帐户的id。

o oldState: "String|null"

o oldState:“字符串|空”

The state string that would have been returned by "Foo/get" before making the requested changes, or null if the server doesn't know what the previous state string was.

在进行请求的更改之前,“Foo/get”将返回的状态字符串,如果服务器不知道以前的状态字符串是什么,则返回null。

o newState: "String"

o 新闻状态:“字符串”

The state string that will now be returned by "Foo/get".

现在将由“Foo/get”返回的状态字符串。

o created: "Id[Foo]|null"

o 创建:“Id[Foo]| null”

A map of the creation id to an object containing any properties of the created Foo object that were not sent by the client. This includes all server-set properties (such as the "id" in most object types) and any properties that were omitted by the client and thus set to a default by the server.

创建id到对象的映射,该对象包含客户端未发送的已创建Foo对象的任何属性。这包括所有服务器集属性(如大多数对象类型中的“id”)以及客户端忽略的任何属性,因此服务器将这些属性设置为默认值。

This argument is null if no Foo objects were successfully created.

如果没有成功创建Foo对象,则此参数为null。

o updated: "Id[Foo|null]|null"

o 更新:“Id[Foo | null]| null”

The keys in this map are the ids of all Foos that were successfully updated.

此映射中的键是已成功更新的所有FOO的ID。

The value for each id is a Foo object containing any property that changed in a way *not* explicitly requested by the PatchObject sent to the server, or null if none. This lets the client know of any changes to server-set or computed properties.

每个id的值都是一个Foo对象,其中包含以发送到服务器的PatchObject明确请求的方式*未*更改的任何属性,如果没有,则为null。这使客户机知道服务器集或计算属性的任何更改。

This argument is null if no Foo objects were successfully updated.

如果没有成功更新Foo对象,则此参数为null。

o destroyed: "Id[]|null"

o 已销毁:“Id[]|空”

A list of Foo ids for records that were successfully destroyed, or null if none.

已成功销毁记录的Foo ID列表,如果没有,则为null。

o notCreated: "Id[SetError]|null"

o notCreated:“Id[SetError]| null”

A map of the creation id to a SetError object for each record that failed to be created, or null if all successful.

对于每个未能创建的记录,创建id映射到SetError对象,如果所有记录都成功,则映射为null。

o notUpdated: "Id[SetError]|null"

o NotUpdate:“Id[SetError]| null”

A map of the Foo id to a SetError object for each record that failed to be updated, or null if all successful.

每个未能更新的记录的Foo id到SetError对象的映射,如果所有记录都成功,则为null。

o notDestroyed: "Id[SetError]|null"

o notDestroyed:“Id[SetError]| null”

A map of the Foo id to a SetError object for each record that failed to be destroyed, or null if all successful.

对于每个未能销毁的记录,Foo id到SetError对象的映射,如果全部成功,则为null。

A *SetError* object has the following properties:

*SetError*对象具有以下属性:

o type: "String"

o 类型:“字符串”

The type of error.

错误的类型。

o description: "String|null"

o 描述:“字符串|空”

A description of the error to help with debugging that includes an explanation of what the problem was. This is a non-localised string and is not intended to be shown directly to end users.

对错误的描述,以帮助调试,其中包括对问题原因的解释。这是一个非本地化字符串,不打算直接向最终用户显示。

The following SetError types are defined and may be returned for set operations on any record type where appropriate:

定义了以下SetError类型,并可在适当情况下为任何记录类型上的set操作返回这些类型:

o "forbidden": (create; update; destroy). The create/update/destroy would violate an ACL or other permissions policy.

o “禁止”:(创建;更新;销毁)。创建/更新/销毁将违反ACL或其他权限策略。

o "overQuota": (create; update). The create would exceed a server-defined limit on the number or total size of objects of this type.

o “超额”:(创建;更新)。创建将超过服务器定义的对此类型对象的数量或总大小的限制。

o "tooLarge": (create; update). The create/update would result in an object that exceeds a server-defined limit for the maximum size of a single object of this type.

o “tooLarge”:(创建;更新)。创建/更新将导致对象超出服务器定义的此类型单个对象的最大大小限制。

o "rateLimit": (create). Too many objects of this type have been created recently, and a server-defined rate limit has been reached. It may work if tried again later.

o “费率限制”:(创建)。最近创建的此类对象太多,并且已达到服务器定义的速率限制。如果以后再试,它可能会起作用。

o "notFound": (update; destroy). The id given to update/destroy cannot be found.

o “未找到”:(更新;销毁)。找不到指定用于更新/销毁的id。

o "invalidPatch": (update). The PatchObject given to update the record was not a valid patch (see the patch description).

o “invalidPatch”:(更新)。用于更新记录的PatchObject不是有效的修补程序(请参阅修补程序说明)。

o "willDestroy": (update). The client requested that an object be both updated and destroyed in the same /set request, and the server has decided to therefore ignore the update.

o “willDestroy”:(更新)。客户端请求在同一个/set请求中更新和销毁对象,服务器因此决定忽略更新。

o "invalidProperties": (create; update). The record given is invalid in some way. For example:

o “无效属性”:(创建;更新)。给出的记录在某种程度上是无效的。例如:

* It contains properties that are invalid according to the type specification of this record type.

* 根据此记录类型的类型规范,它包含无效的属性。

* It contains a property that may only be set by the server (e.g., "id") and is different to the current value. Note, to allow clients to pass whole objects back, it is not an error to include a server-set property in an update as long as the value is identical to the current value on the server.

* 它包含一个只能由服务器设置的属性(例如,“id”),该属性与当前值不同。请注意,为了允许客户端传回整个对象,在更新中包含服务器集属性不是错误,只要该值与服务器上的当前值相同。

* There is a reference to another record (foreign key), and the given id does not correspond to a valid record.

* 存在对另一条记录(外键)的引用,且给定id与有效记录不对应。

The SetError object SHOULD also have a property called "properties" of type "String[]" that lists *all* the properties that were invalid.

SetError对象还应该有一个名为“properties”的属性,类型为“String[]”,它列出了*所有*无效的属性。

Individual methods MAY specify more specific errors for certain conditions that would otherwise result in an invalidProperties error. If the condition of one of these is met, it MUST be returned instead of the invalidProperties error.

个别方法可能会针对某些情况指定更具体的错误,否则会导致invalidProperties错误。如果满足其中一个条件,则必须返回该条件,而不是返回invalidProperties错误。

o "singleton": (create; destroy). This is a singleton type, so you cannot create another one or destroy the existing one.

o “单例”:(创建;销毁)。这是一个单例类型,因此您无法创建另一个单例类型或销毁现有单例类型。

Other possible SetError types MAY be given in specific method descriptions. Other properties MAY also be present on the SetError object, as described in the relevant methods.

其他可能的SetError类型可在具体方法说明中给出。如相关方法中所述,SetError对象上还可能存在其他属性。

The following additional errors may be returned instead of the "Foo/ set" response:

可能会返回以下附加错误,而不是“Foo/set”响应:

"requestTooLarge": The total number of objects to create, update, or destroy exceeds the maximum number the server is willing to process in a single method call.

“requestTooLarge”:要创建、更新或销毁的对象总数超过了服务器在单个方法调用中愿意处理的最大数量。

"stateMismatch": An "ifInState" argument was supplied, and it does not match the current state.

“StateMatch”:提供了一个“ifInState”参数,但该参数与当前状态不匹配。

5.4. /copy
5.4. /抄袭

The only way to move Foo records *between* two different accounts is to copy them using the "Foo/copy" method; once the copy has succeeded, delete the original. The "onSuccessDestroyOriginal" argument allows you to try to do this in one method call; however, note that the two different actions are not atomic, so it is possible for the copy to succeed but the original not to be destroyed for some reason.

在两个不同账户之间移动Foo记录*的唯一方法是使用“Foo/copy”方法复制它们;复制成功后,删除原件。“onSuccessDestroyorial”参数允许您尝试在一个方法调用中执行此操作;但是,请注意,这两个不同的操作不是原子操作,因此复制成功是可能的,但原始操作不会因为某种原因而被销毁。

The copy is conceptually in three phases:

副本在概念上分为三个阶段:

1. Reading the current values from the "from" account.

1. 从“发件人”帐户读取当前值。

2. Writing the new copies to the other account.

2. 将新副本写入另一个帐户。

3. Destroying the originals in the "from" account, if requested.

3. 如有要求,销毁“发件人”账户中的原件。

Data may change in between phases due to concurrent requests.

由于并发请求,数据可能在两个阶段之间发生变化。

The "Foo/copy" method takes the following arguments:

“Foo/copy”方法采用以下参数:

o fromAccountId: "Id"

o fromAccountId:“Id”

The id of the account to copy records from.

要从中复制记录的帐户的id。

o ifFromInState: "String|null"

o ifFromInState:“字符串|空”

This is a state string as returned by the "Foo/get" method. If supplied, the string must match the current state of the account referenced by the fromAccountId when reading the data to be copied; otherwise, the method will be aborted and a "stateMismatch" error returned. If null, the data will be read from the current state.

这是“Foo/get”方法返回的状态字符串。如果提供,则在读取要复制的数据时,字符串必须与fromAccountId引用的帐户的当前状态匹配;否则,该方法将被中止,并返回“StateMatch”错误。如果为null,则将从当前状态读取数据。

o accountId: "Id"

o 帐户Id:“Id”

The id of the account to copy records to. This MUST be different to the "fromAccountId".

要将记录复制到的帐户的id。这必须与“fromAccountId”不同。

o ifInState: "String|null"

o i说明:“字符串|空”

This is a state string as returned by the "Foo/get" method. If supplied, the string must match the current state of the account referenced by the accountId; otherwise, the method will be aborted and a "stateMismatch" error returned. If null, any changes will be applied to the current state.

这是“Foo/get”方法返回的状态字符串。如果提供,字符串必须与accountId引用的帐户的当前状态匹配;否则,该方法将被中止,并返回“StateMatch”错误。如果为null,则任何更改都将应用于当前状态。

o create: "Id[Foo]"

o 创建:“Id[Foo]”

A map of the *creation id* to a Foo object. The Foo object MUST contain an "id" property, which is the id (in the fromAccount) of the record to be copied. When creating the copy, any other properties included are used instead of the current value for that property on the original.

*创建id*到Foo对象的映射。Foo对象必须包含一个“id”属性,该属性是要复制的记录的id(在fromcount中)。创建副本时,将使用包含的任何其他特性,而不是原始副本上该特性的当前值。

o onSuccessDestroyOriginal: "Boolean" (default: false)

o OnSuccessDestroyorial:“布尔”(默认值:false)

If true, an attempt will be made to destroy the original records that were successfully copied: after emitting the "Foo/copy" response, but before processing the next method, the server MUST make a single call to "Foo/set" to destroy the original of each successfully copied record; the output of this is added to the responses as normal, to be returned to the client.

如果为true,将尝试销毁成功复制的原始记录:在发出“Foo/copy”响应后,但在处理下一个方法之前,服务器必须对“Foo/set”进行一次调用,以销毁每个成功复制记录的原始记录;此操作的输出将正常添加到响应中,并返回给客户端。

o destroyFromIfInState: "String|null"

o DestroyFromiInstate:“字符串| null”

This argument is passed on as the "ifInState" argument to the implicit "Foo/set" call, if made at the end of this request to destroy the originals that were successfully copied.

如果在此请求结束时发出销毁成功复制的原件的请求,则此参数将作为“ifInState”参数传递给隐式“Foo/set”调用。

Each record copy is considered an atomic unit that may succeed or fail individually.

每个记录副本都被视为一个原子单元,可能成功,也可能失败。

The response has the following arguments:

响应具有以下参数:

o fromAccountId: "Id"

o fromAccountId:“Id”

The id of the account records were copied from.

帐户记录的id是从中复制的。

o accountId: "Id"

o 帐户Id:“Id”

The id of the account records were copied to.

帐户记录的id已复制到。

o oldState: "String|null"

o oldState:“字符串|空”

The state string that would have been returned by "Foo/get" on the account records that were copied to before making the requested changes, or null if the server doesn't know what the previous state string was.

在进行请求的更改之前,复制到的帐户记录上的“Foo/get”将返回的状态字符串,如果服务器不知道以前的状态字符串是什么,则返回null。

o newState: "String"

o 新闻状态:“字符串”

The state string that will now be returned by "Foo/get" on the account records were copied to.

帐户记录上的“Foo/get”现在将返回的状态字符串已复制到。

o created: "Id[Foo]|null"

o 创建:“Id[Foo]| null”

A map of the creation id to an object containing any properties of the copied Foo object that are set by the server (such as the "id" in most object types; note, the id is likely to be different to the id of the object in the account it was copied from).

创建id到对象的映射,该对象包含由服务器设置的复制的Foo对象的任何属性(例如大多数对象类型中的“id”;注意,该id可能与复制该对象的帐户中的对象id不同)。

This argument is null if no Foo objects were successfully copied.

如果未成功复制任何Foo对象,则此参数为null。

o notCreated: "Id[SetError]|null"

o notCreated:“Id[SetError]| null”

A map of the creation id to a SetError object for each record that failed to be copied, or null if none.

对于未能复制的每条记录,创建id到SetError对象的映射,如果没有,则为null。

The SetError may be any of the standard set errors returned for a create or update. In addition, the following SetError is defined:

SetError可以是为创建或更新返回的任何标准集合错误。此外,还定义了以下SetError:

"alreadyExists": The server forbids duplicates, and the record already exists in the target account. An "existingId" property of type "Id" MUST be included on the SetError object with the id of the existing record.

“alreadyExists”:服务器禁止重复,并且记录已存在于目标帐户中。SetError对象上必须包含“Id”类型的“existingId”属性,该属性的Id为现有记录的Id。

The following additional errors may be returned instead of the "Foo/ copy" response:

可能会返回以下附加错误,而不是“Foo/copy”响应:

"fromAccountNotFound": The "fromAccountId" does not correspond to a valid account.

“fromAccountNotFound:“fromAccountId”与有效帐户不对应。

"fromAccountNotSupportedByMethod": The "fromAccountId" given corresponds to a valid account, but the account does not support this data type.

“fromAccountNotSupportedByMethod”:给定的“fromAccountId”对应于有效的帐户,但该帐户不支持此数据类型。

"stateMismatch": An "ifInState" argument was supplied and it does not match the current state, or an "ifFromInState" argument was supplied and it does not match the current state in the from account.

“StateMatch”:提供了“ifInState”参数但与当前状态不匹配,或者提供了“ifFromInState”参数但与from帐户中的当前状态不匹配。

5.5. /query
5.5. /质疑

For data sets where the total amount of data is expected to be very small, clients can just fetch the complete set of data and then do any sorting/filtering locally. However, for large data sets (e.g., multi-gigabyte mailboxes), the client needs to be able to search/sort/window the data type on the server.

对于数据总量预计很小的数据集,客户端只需获取完整的数据集,然后在本地执行任何排序/筛选。但是,对于大型数据集(例如,千兆字节邮箱),客户端需要能够在服务器上搜索/排序/窗口数据类型。

A query on the set of Foos in an account is made by calling "Foo/ query". This takes a number of arguments to determine which records to include, how they should be sorted, and which part of the result

通过调用“Foo/query”对帐户中的Foo集进行查询。这需要许多参数来确定要包括哪些记录、它们应该如何排序以及结果的哪一部分

should be returned (the full list may be *very* long). The result is returned as a list of Foo ids.

应返回(完整列表可能*非常*长)。结果作为Foo id列表返回。

A call to "Foo/query" takes the following arguments:

对“Foo/query”的调用采用以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account to use.

要使用的帐户的id。

o filter: "FilterOperator|FilterCondition|null"

o 筛选器:“FilterOperator | FilterCondition | null”

Determines the set of Foos returned in the results. If null, all objects in the account of this type are included in the results. A *FilterOperator* object has the following properties:

确定结果中返回的一组foo。如果为null,则结果中包含此类型帐户中的所有对象。*FilterOperator*对象具有以下属性:

* operator: "String"

* 运算符:“字符串”

This MUST be one of the following strings:

这必须是以下字符串之一:

+ "AND": All of the conditions must match for the filter to match.

+ “和”:所有条件必须匹配,过滤器才能匹配。

+ "OR": At least one of the conditions must match for the filter to match.

+ “或”:至少一个条件必须匹配,过滤器才能匹配。

+ "NOT": None of the conditions must match for the filter to match.

+ “非”:任何条件都不必匹配,过滤器才能匹配。

* conditions: "(FilterOperator|FilterCondition)[]"

* 条件:“(FilterOperator | FilterCondition)[]”

The conditions to evaluate against each record.

根据每个记录评估的条件。

A *FilterCondition* is an "object" whose allowed properties and semantics depend on the data type and is defined in the /query method specification for that type. It MUST NOT have an "operator" property.

*FilterCondition*是一个“对象”,其允许的属性和语义取决于数据类型,并在该类型的/query方法规范中定义。它不能有“运算符”属性。

o sort: "Comparator[]|null"

o 排序:“比较器[]|空”

Lists the names of properties to compare between two Foo records, and how to compare them, to determine which comes first in the sort. If two Foo records have an identical value for the first comparator, the next comparator will be considered, and so on. If all comparators are the same (this includes the case where an empty array or null is given as the "sort" argument), the sort order is server dependent, but it MUST be stable between calls to "Foo/query". A *Comparator* has the following properties:

列出要在两个Foo记录之间进行比较的属性的名称,以及如何比较它们,以确定排序中的第一个属性。如果两个Foo记录对于第一个比较器具有相同的值,则将考虑下一个比较器,依此类推。如果所有比较器都相同(这包括将空数组或null作为“sort”参数给出的情况),则排序顺序依赖于服务器,但在调用“Foo/query”之间必须保持稳定。*比较器*具有以下属性:

* property: "String"

* 属性:“字符串”

The name of the property on the Foo objects to compare.

要比较的Foo对象上的属性的名称。

* isAscending: "Boolean" (optional; default: true)

* isAscending:“布尔”(可选;默认值:true)

If true, sort in ascending order. If false, reverse the comparator's results to sort in descending order.

如果为true,则按升序排序。如果为false,则反转比较器的结果以按降序排序。

* collation: "String" (optional; default is server-dependent)

* 排序规则:“字符串”(可选;默认值取决于服务器)

The identifier, as registered in the collation registry defined in [RFC4790], for the algorithm to use when comparing the order of strings. The algorithms the server supports are advertised in the capabilities object returned with the Session object (see Section 2).

在[RFC4790]中定义的排序规则注册表中注册的标识符,用于比较字符串顺序时使用的算法。服务器支持的算法在会话对象返回的capabilities对象中公布(参见第2节)。

If omitted, the default algorithm is server dependent, but:

如果省略,默认算法依赖于服务器,但是:

1. It MUST be unicode-aware.

1. 它必须知道unicode。

2. It MAY be selected based on an Accept-Language header in the request (as defined in [RFC7231], Section 5.3.5) or out-of-band information about the user's language/locale.

2. 可以根据请求中的Accept Language标头(如[RFC7231]第5.3.5节中的定义)或有关用户语言/区域设置的带外信息来选择。

3. It SHOULD be case insensitive where such a concept makes sense for a language/locale. Where the user's language is unknown, it is RECOMMENDED to follow the advice in Section 5.2.3 of [RFC8264].

3. 如果这样的概念对语言/区域设置有意义,那么它应该不区分大小写。如果用户语言未知,建议遵循[RFC8264]第5.2.3节中的建议。

The "i;unicode-casemap" collation [RFC5051] and the Unicode Collation Algorithm (<http://www.unicode.org/reports/tr10/>) are two examples that fulfil these criterion and provide reasonable behaviour for a large number of languages.

“i;unicode casemap”排序规则[RFC5051]和unicode排序算法(<http://www.unicode.org/reports/tr10/>)有两个例子可以满足这些标准,并为大量语言提供合理的行为。

When the property being compared is not a string, the "collation" property is ignored, and the following comparison rules apply based on the type. In ascending order:

当要比较的属性不是字符串时,将忽略“collation”属性,并根据类型应用以下比较规则。按升序:

+ "Boolean": false comes before true.

+ “布尔”:假在真之前。

+ "Number": A lower number comes before a higher number.

+ “数字”:较低的数字在较高的数字之前。

+ "Date"/"UTCDate": The earlier date comes first.

+ “日期”/“UTCDate”:日期越早越好。

The Comparator object may also have additional properties as required for specific sort operations defined in a type's /query method.

Comparator对象还可能具有在类型的/query方法中定义的特定排序操作所需的其他属性。

o position: "Int" (default: 0)

o 位置:“Int”(默认值:0)

The zero-based index of the first id in the full list of results to return.

要返回的结果完整列表中第一个id的从零开始的索引。

If a negative value is given, it is an offset from the end of the list. Specifically, the negative value MUST be added to the total number of results given the filter, and if still negative, it's clamped to "0". This is now the zero-based index of the first id to return.

如果给定负值,则为距列表末尾的偏移量。具体来说,负值必须添加到给定过滤器的结果总数中,如果仍然为负值,则将其钳制为“0”。现在,这是要返回的第一个id的从零开始的索引。

If the index is greater than or equal to the total number of objects in the results list, then the "ids" array in the response will be empty, but this is not an error.

如果索引大于或等于结果列表中的对象总数,则响应中的“ids”数组将为空,但这不是错误。

o anchor: "Id|null"

o 锚定:“Id | null”

A Foo id. If supplied, the "position" argument is ignored. The index of this id in the results will be used in combination with the "anchorOffset" argument to determine the index of the first result to return (see below for more details).

Foo id。如果提供,则忽略“position”参数。结果中此id的索引将与“AnchoroOffset”参数结合使用,以确定要返回的第一个结果的索引(有关更多详细信息,请参见下文)。

o anchorOffset: "Int" (default: 0)

o 主持人偏移:“Int”(默认值:0)

The index of the first result to return relative to the index of the anchor, if an anchor is given. This MAY be negative. For example, "-1" means the Foo immediately preceding the anchor is the first result in the list returned (see below for more details).

如果给定了锚,则相对于锚的索引返回的第一个结果的索引。这可能是负面的。例如,“-1”表示锚之前的Foo是返回列表中的第一个结果(有关更多详细信息,请参见下文)。

o limit: "UnsignedInt|null"

o 限制:“UnsignedInt | null”

The maximum number of results to return. If null, no limit presumed. The server MAY choose to enforce a maximum "limit" argument. In this case, if a greater value is given (or if it is null), the limit is clamped to the maximum; the new limit is returned with the response so the client is aware. If a negative value is given, the call MUST be rejected with an "invalidArguments" error.

要返回的最大结果数。如果为空,则不假定限制。服务器可以选择强制执行最大“限制”参数。在这种情况下,如果给定了更大的值(或如果为空),则限制被钳制到最大值;新的限制将随响应一起返回,以便客户机知道。如果给定负值,则必须以“invalidArguments”错误拒绝调用。

o calculateTotal: "Boolean" (default: false)

o calculateTotal:“布尔”(默认值:false)

Does the client wish to know the total number of results in the query? This may be slow and expensive for servers to calculate, particularly with complex filters, so clients should take care to only request the total when needed.

客户是否希望知道查询中的结果总数?对于服务器来说,这可能会很慢,计算成本也很高,特别是对于复杂的过滤器,因此客户端应该注意只在需要时请求总数。

If an "anchor" argument is given, the anchor is looked for in the results after filtering and sorting. If found, the "anchorOffset" is then added to its index. If the resulting index is now negative, it is clamped to 0. This index is now used exactly as though it were supplied as the "position" argument. If the anchor is not found, the call is rejected with an "anchorNotFound" error.

如果给出了“锚定”参数,则在过滤和排序后在结果中查找锚定。如果找到,则将“锚定偏移”添加到其索引中。如果结果索引现在为负数,则将其钳制为0。该索引现在的使用方式与作为“position”参数提供的完全相同。如果找不到锚,调用将被拒绝,并出现“anchorNotFound”错误。

If an "anchor" is specified, any position argument supplied by the client MUST be ignored. If no "anchor" is supplied, any "anchorOffset" argument MUST be ignored.

如果指定了“锚定”,则必须忽略客户端提供的任何位置参数。如果未提供“锚定”,则必须忽略任何“锚定偏移”参数。

A client can use "anchor" instead of "position" to find the index of an id within a large set of results.

客户机可以使用“锚定”而不是“位置”来查找大量结果中id的索引。

The response has the following arguments:

响应具有以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account used for the call.

用于呼叫的帐户的id。

o queryState: "String"

o queryState:“字符串”

A string encoding the current state of the query on the server. This string MUST change if the results of the query (i.e., the matching ids and their sort order) have changed. The queryState string MAY change if something has changed on the server, which means the results may have changed but the server doesn't know for sure.

对服务器上查询的当前状态进行编码的字符串。如果查询结果(即匹配ID及其排序顺序)已更改,则此字符串必须更改。如果服务器上发生了更改,queryState字符串可能会更改,这意味着结果可能已更改,但服务器无法确定。

The queryState string only represents the ordered list of ids that match the particular query (including its sort/filter). There is no requirement for it to change if a property on an object matching the query changes but the query results are unaffected (indeed, it is more efficient if the queryState string does not change in this case). The queryState string only has meaning when compared to future responses to a query with the same type/sort/ filter or when used with /queryChanges to fetch changes.

queryState字符串仅表示与特定查询(包括其排序/筛选器)匹配的ID的有序列表。如果与查询匹配的对象上的属性发生更改,但查询结果不受影响,则不需要对其进行更改(实际上,在这种情况下,如果queryState字符串不更改,则效率更高)。queryState字符串仅在与将来对具有相同类型/排序/筛选器的查询的响应进行比较时,或与/queryChanges一起使用以获取更改时才有意义。

Should a client receive back a response with a different queryState string to a previous call, it MUST either throw away the currently cached query and fetch it again (note, this does not require fetching the records again, just the list of ids) or call "Foo/queryChanges" to get the difference.

如果客户机接收到对上一次调用具有不同queryState字符串的响应,它必须扔掉当前缓存的查询并再次获取它(注意,这不需要再次获取记录,只需要获取ID列表),或者调用“Foo/queryChanges”以获得差异。

o canCalculateChanges: "Boolean"

o canCalculateChanges:“布尔”

This is true if the server supports calling "Foo/queryChanges" with these "filter"/"sort" parameters. Note, this does not guarantee that the "Foo/queryChanges" call will succeed, as it may only be possible for a limited time afterwards due to server internal implementation details.

如果服务器支持使用这些“过滤器”/“排序”参数调用“Foo/queryChanges”,则这是正确的。注意,这并不能保证“Foo/queryChanges”调用会成功,因为由于服务器内部实现细节的原因,可能只能在有限的时间内调用。

o position: "UnsignedInt"

o 位置:“未签名”

The zero-based index of the first result in the "ids" array within the complete list of query results.

查询结果完整列表中“ids”数组中第一个结果的从零开始的索引。

o ids: "Id[]"

o Id:“Id[]”

The list of ids for each Foo in the query results, starting at the index given by the "position" argument of this response and continuing until it hits the end of the results or reaches the "limit" number of ids. If "position" is >= "total", this MUST be the empty list.

查询结果中每个Foo的id列表,从该响应的“position”参数给出的索引开始,一直到到达结果末尾或达到id的“limit”数量。如果“位置”大于等于“总计”,则该列表必须为空。

o total: "UnsignedInt" (only if requested)

o 总计:“未签名”(仅在要求时)

The total number of Foos in the results (given the "filter"). This argument MUST be omitted if the "calculateTotal" request argument is not true.

结果中的FOO总数(给定“过滤器”)。如果“calculateTotal”请求参数不为true,则必须省略此参数。

o limit: "UnsignedInt" (if set by the server)

o 限制:“UnsignedInt”(如果由服务器设置)

The limit enforced by the server on the maximum number of results to return. This is only returned if the server set a limit or used a different limit than that given in the request.

服务器对要返回的最大结果数施加的限制。仅当服务器设置了限制或使用了与请求中给定的限制不同的限制时,才会返回此消息。

The following additional errors may be returned instead of the "Foo/ query" response:

可能会返回以下附加错误,而不是“Foo/query”响应:

"anchorNotFound": An anchor argument was supplied, but it cannot be found in the results of the query.

“anchorNotFound”:提供了锚参数,但在查询结果中找不到该参数。

"unsupportedSort": The "sort" is syntactically valid, but it includes a property the server does not support sorting on or a collation method it does not recognise.

“unsupportedSort:“sort”在语法上是有效的,但它包含服务器不支持排序的属性或它无法识别的排序方法。

"unsupportedFilter": The "filter" is syntactically valid, but the server cannot process it. If the filter was the result of a user's search input, the client SHOULD suggest that the user simplify their search.

“unsupportedFilter”:该“筛选器”在语法上有效,但服务器无法处理它。如果过滤器是用户搜索输入的结果,客户端应建议用户简化搜索。

5.6. /queryChanges
5.6. /查询更改

The "Foo/queryChanges" method allows a client to efficiently update the state of a cached query to match the new state on the server. It takes the following arguments:

“Foo/queryChanges”方法允许客户端高效地更新缓存查询的状态,以匹配服务器上的新状态。它采用以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account to use.

要使用的帐户的id。

o filter: "FilterOperator|FilterCondition|null"

o 筛选器:“FilterOperator | FilterCondition | null”

The filter argument that was used with "Foo/query".

与“Foo/query”一起使用的筛选器参数。

o sort: "Comparator[]|null"

o 排序:“比较器[]|空”

The sort argument that was used with "Foo/query".

与“Foo/query”一起使用的排序参数。

o sinceQueryState: "String"

o 从QueryState开始:“字符串”

The current state of the query in the client. This is the string that was returned as the "queryState" argument in the "Foo/query" response with the same sort/filter. The server will return the changes made to the query since this state.

客户端中查询的当前状态。这是作为“Foo/query”响应中的“queryState”参数返回的字符串,具有相同的排序/筛选器。服务器将返回自此状态以来对查询所做的更改。

o maxChanges: "UnsignedInt|null"

o maxChanges:“UnsignedInt | null”

The maximum number of changes to return in the response. See error descriptions below for more details.

响应中要返回的最大更改数。有关更多详细信息,请参阅下面的错误描述。

o upToId: "Id|null"

o uptid:“Id | null”

The last (highest-index) id the client currently has cached from the query results. When there are a large number of results, in a common case, the client may have only downloaded and cached a small subset from the beginning of the results. If the sort and filter are both only on immutable properties, this allows the server to omit changes after this point in the results, which can significantly increase efficiency. If they are not immutable, this argument is ignored.

客户端当前从查询结果缓存的最后一个(最高索引)id。当有大量结果时,在常见情况下,客户端可能只下载并缓存了结果开头的一小部分。如果sort和filter都只针对不可变属性,这允许服务器忽略结果中这一点之后的更改,这可以显著提高效率。如果它们不是不可变的,则忽略此参数。

o calculateTotal: "Boolean" (default: false)

o calculateTotal:“布尔”(默认值:false)

Does the client wish to know the total number of results now in the query? This may be slow and expensive for servers to calculate, particularly with complex filters, so clients should take care to only request the total when needed.

客户是否希望知道查询中现在的结果总数?对于服务器来说,这可能会很慢,计算成本也很高,特别是对于复杂的过滤器,因此客户端应该注意只在需要时请求总数。

The response has the following arguments:

响应具有以下参数:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account used for the call.

用于呼叫的帐户的id。

o oldQueryState: "String"

o oldQueryState:“字符串”

This is the "sinceQueryState" argument echoed back; that is, the state from which the server is returning changes.

这是“自QueryState”论点的回响;也就是说,服务器返回更改的状态。

o newQueryState: "String"

o newQueryState:“字符串”

This is the state the query will be in after applying the set of changes to the old state.

这是将更改集应用于旧状态后查询将处于的状态。

o total: "UnsignedInt" (only if requested)

o 总计:“未签名”(仅在要求时)

The total number of Foos in the results (given the "filter"). This argument MUST be omitted if the "calculateTotal" request argument is not true.

结果中的FOO总数(给定“过滤器”)。如果“calculateTotal”请求参数不为true,则必须省略此参数。

o removed: "Id[]"

o 删除:“Id[]”

The "id" for every Foo that was in the query results in the old state and that is not in the results in the new state.

查询中的每个Foo的“id”将导致旧状态的结果,而不是新状态的结果。

If the server cannot calculate this exactly, the server MAY return the ids of extra Foos in addition that may have been in the old results but are not in the new results.

如果服务器无法准确计算,则服务器可能会返回额外的foo的id,这些foo可能已在旧结果中,但不在新结果中。

If the sort and filter are both only on immutable properties and an "upToId" is supplied and exists in the results, any ids that were removed but have a higher index than "upToId" SHOULD be omitted.

如果sort和filter都只在不可变属性上,并且提供了一个“uptid”并存在于结果中,则应忽略任何已删除但索引高于“uptid”的id。

If the "filter" or "sort" includes a mutable property, the server MUST include all Foos in the current results for which this property may have changed. The position of these may have moved in the results, so they must be reinserted by the client to ensure its query cache is correct.

如果“筛选器”或“排序”包含可变属性,则服务器必须在当前结果中包含此属性可能已更改的所有FOO。它们的位置可能已在结果中移动,因此客户端必须重新插入它们,以确保其查询缓存正确。

o added: "AddedItem[]"

o 增加:“增编[]”

The id and index in the query results (in the new state) for every Foo that has been added to the results since the old state AND every Foo in the current results that was included in the "removed" array (due to a filter or sort based upon a mutable property).

自旧状态以来添加到结果中的每个Foo的查询结果(处于新状态)中的id和索引,以及当前结果中包含在“已删除”数组中的每个Foo的id和索引(由于基于可变属性的筛选或排序)。

If the sort and filter are both only on immutable properties and an "upToId" is supplied and exists in the results, any ids that were added but have a higher index than "upToId" SHOULD be omitted.

如果sort和filter都只在不可变属性上,并且提供了一个“uptid”并存在于结果中,则应忽略添加的但索引高于“uptid”的任何ID。

The array MUST be sorted in order of index, with the lowest index first.

数组必须按索引顺序排序,首先是最低的索引。

An *AddedItem* object has the following properties:

*AddedItem*对象具有以下属性:

* id: "Id"

* id:“id”

* index: "UnsignedInt"

* 索引:“UnsignedInt”

The result of this is that if the client has a cached sparse array of Foo ids corresponding to the results in the old state, then:

这样做的结果是,如果客户端有一个缓存的Foo id稀疏数组,该数组对应于旧状态下的结果,那么:

   fooIds = [ "id1", "id2", null, null, "id3", "id4", null, null, null ]
        
   fooIds = [ "id1", "id2", null, null, "id3", "id4", null, null, null ]
        

If it *splices out* all ids in the removed array that it has in its cached results, then:

如果它*拼接出*已删除数组中缓存结果中的所有ID,则:

      removed = [ "id2", "id31", ... ];
      fooIds => [ "id1", null, null, "id3", "id4", null, null, null ]
        
      removed = [ "id2", "id31", ... ];
      fooIds => [ "id1", null, null, "id3", "id4", null, null, null ]
        

and *splices in* (one by one in order, starting with the lowest index) all of the ids in the added array:

和*拼接*(按顺序逐个拼接,从最低索引开始)添加阵列中的所有ID:

  added = [{ id: "id5", index: 0, ... }];
  fooIds => [ "id5", "id1", null, null, "id3", "id4", null, null, null ]
        
  added = [{ id: "id5", index: 0, ... }];
  fooIds => [ "id5", "id1", null, null, "id3", "id4", null, null, null ]
        

and *truncates* or *extends* to the new total length, then the results will now be in the new state.

并*截断*或*延伸*到新的总长度,则结果现在将处于新状态。

Note: splicing in adds the item at the given index, incrementing the index of all items previously at that or a higher index. Splicing out is the inverse, removing the item and decrementing the index of every item after it in the array.

注意:在给定索引处添加项目,增加之前在该索引或更高索引处的所有项目的索引。剪接是相反的,删除该项并减少数组中每个项后面的索引。

The following additional errors may be returned instead of the "Foo/ queryChanges" response:

可能会返回以下附加错误,而不是“Foo/queryChanges”响应:

"tooManyChanges": There are more changes than the client's "maxChanges" argument. Each item in the removed or added array is considered to be one change. The client may retry with higher max changes or invalidate its cache of the query results.

“tooManyChanges”:比客户端的“maxChanges”参数有更多的更改。删除或添加的数组中的每个项都被视为一个更改。客户端可能会重试更高的最大更改,或使其查询结果缓存无效。

"cannotCalculateChanges": The server cannot calculate the changes from the queryState string given by the client, usually due to the client's state being too old. The client MUST invalidate its cache of the query results.

“cannotCalculateChanges”:服务器无法从客户端提供的queryState字符串计算更改,通常是由于客户端的状态太旧。客户端必须使其查询结果缓存无效。

5.7. Examples
5.7. 例子

Suppose we have a type *Todo* with the following properties:

假设我们有一个具有以下属性的类型*Todo*:

o id: "Id" (immutable; server-set)

o id:“id”(不可变;服务器集)

The id of the object.

对象的id。

o title: "String"

o 标题:“字符串”

A brief summary of what is to be done.

将要做的事情的简要总结。

o keywords: "String[Boolean]" (default: {})

o 关键词:“字符串[布尔]”(默认值:{})

A set of keywords that apply to the Todo. The set is represented as an object, with the keys being the "keywords". The value for each key in the object MUST be true. (This format allows you to update an individual key using patch syntax rather than having to update the whole set of keywords as one, which a "String[]" representation would require.)

应用于待办事项的一组关键字。集合被表示为一个对象,键是“关键字”。对象中每个键的值必须为true。(此格式允许您使用修补程序语法更新单个关键字,而不必将整个关键字集更新为一个关键字集,这是“字符串[]”表示法所需要的。)

o neuralNetworkTimeEstimation: "Number" (server-set)

o neuralNetworkTimeEstimation:“编号”(服务器集)

The title and keywords are fed into the server's state-of-the-art neural network to get an estimation of how long this Todo will take, in seconds.

标题和关键字被输入服务器的最先进的神经网络,以估计这项任务需要多长时间,以秒为单位。

o subTodoIds: "Id[]|null"

o SubtoDoId:“Id[]| null”

The ids of a list of other Todos to complete as part of this Todo.

作为此Todo的一部分要完成的其他Todo列表的ID。

Suppose also that all the standard methods are defined for this type and the FilterCondition object supports a "hasKeyword" property to match Todos with the given keyword.

还假设所有标准方法都是为此类型定义的,并且FilterCondition对象支持一个“hasKeyword”属性,以将TODO与给定的关键字匹配。

A client might want to display the list of Todos with either a "music" keyword or a "video" keyword, so it makes the following method call:

客户端可能希望显示带有“音乐”关键字或“视频”关键字的TODO列表,因此它会进行以下方法调用:

                   [[ "Todo/query", {
                     "accountId": "x",
                     "filter": {
                       "operator": "OR",
                       "conditions": [
                         { "hasKeyword": "music" },
                         { "hasKeyword": "video" }
                       ]
                     },
                     "sort": [{ "property": "title" }],
                     "position": 0,
                     "limit": 10
                   }, "0" ],
                   [ "Todo/get", {
                     "accountId": "x",
                     "#ids": {
                       "resultOf": "0",
                       "name": "Todo/query",
                       "path": "/ids"
                     }
                   }, "1" ]]
        
                   [[ "Todo/query", {
                     "accountId": "x",
                     "filter": {
                       "operator": "OR",
                       "conditions": [
                         { "hasKeyword": "music" },
                         { "hasKeyword": "video" }
                       ]
                     },
                     "sort": [{ "property": "title" }],
                     "position": 0,
                     "limit": 10
                   }, "0" ],
                   [ "Todo/get", {
                     "accountId": "x",
                     "#ids": {
                       "resultOf": "0",
                       "name": "Todo/query",
                       "path": "/ids"
                     }
                   }, "1" ]]
        

This would query the server for the set of Todos with a keyword of either "music" or "video", sorted by title, and limited to the first 10 results. It fetches the full object for each of these Todos using back-references to reference the result of the query. The response might look something like:

这将向服务器查询包含关键字“音乐”或“视频”的TODO集合,按标题排序,并限制为前10个结果。它使用反向引用获取每个TODO的完整对象,以引用查询结果。响应可能类似于:

       [[ "Todo/query", {
         "accountId": "x",
         "queryState": "y13213",
         "canCalculateChanges": true,
         "position": 0,
         "ids": [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" ]
       }, "0" ],
       [ "Todo/get", {
         "accountId": "x",
         "state": "10324",
         "list": [{
           "id": "a",
           "title": "Practise Piano",
           "keywords": {
             "music": true,
             "beethoven": true,
             "mozart": true,
             "liszt": true,
             "rachmaninov": true
           },
           "neuralNetworkTimeEstimation": 3600
         }, {
           "id": "b",
           "title": "Watch Daft Punk music video",
           "keywords": {
             "music": true,
             "video": true,
             "trance": true
           },
           "neuralNetworkTimeEstimation": 18000
         },
         ...
         ]
       }, "1" ]]
        
       [[ "Todo/query", {
         "accountId": "x",
         "queryState": "y13213",
         "canCalculateChanges": true,
         "position": 0,
         "ids": [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" ]
       }, "0" ],
       [ "Todo/get", {
         "accountId": "x",
         "state": "10324",
         "list": [{
           "id": "a",
           "title": "Practise Piano",
           "keywords": {
             "music": true,
             "beethoven": true,
             "mozart": true,
             "liszt": true,
             "rachmaninov": true
           },
           "neuralNetworkTimeEstimation": 3600
         }, {
           "id": "b",
           "title": "Watch Daft Punk music video",
           "keywords": {
             "music": true,
             "video": true,
             "trance": true
           },
           "neuralNetworkTimeEstimation": 18000
         },
         ...
         ]
       }, "1" ]]
        

Now, suppose the user adds a keyword "chopin" and removes the keyword "mozart" from the "Practise Piano" task. The client may send the whole object to the server, as this is a valid PatchObject:

现在,假设用户添加了一个关键字“肖邦”,并从“练习钢琴”任务中删除了关键字“莫扎特”。客户端可能会将整个对象发送到服务器,因为这是一个有效的补丁对象:

                 [[ "Todo/set", {
                   "accountId": "x",
                   "ifInState": "10324",
                   "update": {
                     "a": {
                       "id": "a",
                       "title": "Practise Piano",
                       "keywords": {
                         "music": true,
                         "beethoven": true,
                         "chopin": true,
                         "liszt": true,
                         "rachmaninov": true
                       },
                       "neuralNetworkTimeEstimation": 360
                     }
                   }
                 }, "0" ]]
        
                 [[ "Todo/set", {
                   "accountId": "x",
                   "ifInState": "10324",
                   "update": {
                     "a": {
                       "id": "a",
                       "title": "Practise Piano",
                       "keywords": {
                         "music": true,
                         "beethoven": true,
                         "chopin": true,
                         "liszt": true,
                         "rachmaninov": true
                       },
                       "neuralNetworkTimeEstimation": 360
                     }
                   }
                 }, "0" ]]
        

or it may send a minimal patch:

或者它可以发送一个最小的补丁:

                      [[ "Todo/set", {
                        "accountId": "x",
                        "ifInState": "10324",
                        "update": {
                          "a": {
                            "keywords/chopin": true,
                            "keywords/mozart": null
                          }
                        }
                      }, "0" ]]
        
                      [[ "Todo/set", {
                        "accountId": "x",
                        "ifInState": "10324",
                        "update": {
                          "a": {
                            "keywords/chopin": true,
                            "keywords/mozart": null
                          }
                        }
                      }, "0" ]]
        

The effect is exactly the same on the server in either case, and presuming the server is still in state "10324", it will probably return success:

在这两种情况下,服务器上的效果完全相同,并且假定服务器仍处于状态“10324”,它可能会返回成功:

                 [[ "Todo/set", {
                   "accountId": "x",
                   "oldState": "10324",
                   "newState": "10329",
                   "updated": {
                     "a": {
                       "neuralNetworkTimeEstimation": 5400
                     }
                   }
                 }, "0" ]]
        
                 [[ "Todo/set", {
                   "accountId": "x",
                   "oldState": "10324",
                   "newState": "10329",
                   "updated": {
                     "a": {
                       "neuralNetworkTimeEstimation": 5400
                     }
                   }
                 }, "0" ]]
        

The server changed the "neuralNetworkTimeEstimation" property on the object as part of this change; as this changed in a way *not* explicitly requested by the PatchObject sent to the server, it is returned with the "updated" confirmation.

作为此更改的一部分,服务器更改了对象的“neuralNetworkTimeEstimation”属性;由于发送到服务器的PatchObject以*非*明确请求的方式更改了此项,因此将返回“更新”确认。

Let us now add a sub-Todo to our new "Practise Piano" Todo. In this example, we can see the use of a reference to a creation id to allow us to set a foreign key reference to a record created in the same request:

现在让我们为新的“练习钢琴”Todo添加一个子Todo。在本例中,我们可以看到使用对创建id的引用来允许我们设置对在同一请求中创建的记录的外键引用:

                   [[ "Todo/set", {
                     "accountId": "x",
                     "create": {
                       "k15": {
                         "title": "Warm up with scales"
                       }
                     },
                     "update": {
                       "a": {
                         "subTodoIds": [ "#k15" ]
                       }
                     }
                   }, "0" ]]
        
                   [[ "Todo/set", {
                     "accountId": "x",
                     "create": {
                       "k15": {
                         "title": "Warm up with scales"
                       }
                     },
                     "update": {
                       "a": {
                         "subTodoIds": [ "#k15" ]
                       }
                     }
                   }, "0" ]]
        

Now, suppose another user deleted the "Listen to Daft Punk" Todo. The first user will receive a push notification (see Section 7) with the changed state string for the "Todo" type. Since the new string does not match its current state, it knows it needs to check for updates. It may make a request like:

现在,假设另一个用户删除了“听愚蠢的朋克”Todo。第一个用户将收到一个推送通知(参见第7节),其中包含“Todo”类型的已更改状态字符串。由于新字符串与其当前状态不匹配,它知道需要检查更新。它可能会提出如下请求:

                   [[ "Todo/changes", {
                     "accountId": "x",
                     "sinceState": "10324",
                     "maxChanges": 50
                   }, "0" ],
                   [ "Todo/queryChanges", {
                     "accountId": "x",
                     "filter": {
                       "operator": "OR",
                       "conditions": [
                         { "hasKeyword": "music" },
                         { "hasKeyword": "video" }
                       ]
                     },
                     "sort": [{ "property": "title" }],
                     "sinceQueryState": "y13213",
                     "maxChanges": 50
                   }, "1" ]]
        
                   [[ "Todo/changes", {
                     "accountId": "x",
                     "sinceState": "10324",
                     "maxChanges": 50
                   }, "0" ],
                   [ "Todo/queryChanges", {
                     "accountId": "x",
                     "filter": {
                       "operator": "OR",
                       "conditions": [
                         { "hasKeyword": "music" },
                         { "hasKeyword": "video" }
                       ]
                     },
                     "sort": [{ "property": "title" }],
                     "sinceQueryState": "y13213",
                     "maxChanges": 50
                   }, "1" ]]
        

and receive in response:

并收到以下回复:

                       [[ "Todo/changes", {
                         "accountId": "x",
                         "oldState": "10324",
                         "newState": "871903",
                         "hasMoreChanges": false,
                         "created": [],
                         "updated": [],
                         "destroyed": ["b"]
                       }, "0" ],
                       [ "Todo/queryChanges", {
                         "accountId": "x",
                         "oldQueryState": "y13213",
                         "newQueryState": "y13218",
                         "removed": ["b"],
                         "added": null
                       }, "1" ]]
        
                       [[ "Todo/changes", {
                         "accountId": "x",
                         "oldState": "10324",
                         "newState": "871903",
                         "hasMoreChanges": false,
                         "created": [],
                         "updated": [],
                         "destroyed": ["b"]
                       }, "0" ],
                       [ "Todo/queryChanges", {
                         "accountId": "x",
                         "oldQueryState": "y13213",
                         "newQueryState": "y13218",
                         "removed": ["b"],
                         "added": null
                       }, "1" ]]
        

Suppose the user has access to another account "y", for example, a team account shared between multiple users. To move an existing Todo from account "x", the client would call:

假设用户可以访问另一个帐户“y”,例如,多个用户共享的团队帐户。要从帐户“x”移动现有Todo,客户端将调用:

                    [[ "Todo/copy", {
                      "fromAccountId": "x",
                      "accountId": "y",
                      "create": {
                        "k5122": {
                          "id": "a"
                        }
                      },
                      "onSuccessDestroyOriginal": true
                    }, "0" ]]
        
                    [[ "Todo/copy", {
                      "fromAccountId": "x",
                      "accountId": "y",
                      "create": {
                        "k5122": {
                          "id": "a"
                        }
                      },
                      "onSuccessDestroyOriginal": true
                    }, "0" ]]
        

The server successfully copies the Todo to a new account (where it receives a new id) and deletes the original. Due to the implicit call to "Todo/set", there are two responses to the single method call, both with the same method call id:

服务器成功地将Todo复制到一个新帐户(在那里它接收到一个新id)并删除原始帐户。由于对“Todo/set”的隐式调用,对单个方法调用有两个响应,都具有相同的方法调用id:

                       [[ "Todo/copy", {
                         "fromAccountId": "x",
                         "accountId": "y",
                         "created": {
                           "k5122": {
                             "id": "DAf97"
                           }
                         },
                         "oldState": "c1d64ecb038c",
                         "newState": "33844835152b"
                       }, "0" ],
                       [ "Todo/set", {
                         "accountId": "x",
                         "oldState": "871903",
                         "newState": "871909",
                         "destroyed": [ "a" ],
                         ...
                       }, "0" ]]
        
                       [[ "Todo/copy", {
                         "fromAccountId": "x",
                         "accountId": "y",
                         "created": {
                           "k5122": {
                             "id": "DAf97"
                           }
                         },
                         "oldState": "c1d64ecb038c",
                         "newState": "33844835152b"
                       }, "0" ],
                       [ "Todo/set", {
                         "accountId": "x",
                         "oldState": "871903",
                         "newState": "871909",
                         "destroyed": [ "a" ],
                         ...
                       }, "0" ]]
        
5.8. Proxy Considerations
5.8. 代理考虑

JMAP has been designed to allow an API endpoint to easily proxy through to one or more JMAP servers. This may be useful for load balancing, augmenting capabilities, or presenting a single endpoint to accounts hosted on different JMAP servers (splitting the request based on each method's "accountId" argument). The proxy need only understand the general structure of a JMAP Request object; it does not need to know anything specifically about the methods and arguments it will pass through to other servers.

JMAP的设计允许API端点轻松地代理到一个或多个JMAP服务器。这对于负载平衡、增强功能或向托管在不同JMAP服务器上的帐户显示单个端点(基于每个方法的“accountId”参数拆分请求)可能很有用。代理只需要了解JMAP请求对象的一般结构;它不需要特别了解它将传递给其他服务器的方法和参数。

If splitting up the methods in a request to call them on different backend servers, the proxy must do two things to ensure back-references and creation-id references resolve the same as if the entire request were processed on a single server:

如果将请求中的方法拆分以在不同的后端服务器上调用它们,则代理必须执行两项操作,以确保后台引用和创建id引用的解析方式与在单个服务器上处理整个请求的解析方式相同:

1. It must pass a "createdIds" property with each subrequest. If this is not given by the client, an empty object should be used for the first subrequest. The "createdIds" property of each subresponse should be passed on in the next subrequest.

1. 它必须在每个子请求中传递一个“createdIds”属性。如果客户端没有给出,那么第一个子请求应该使用空对象。每个子响应的“createdIds”属性应在下一个子请求中传递。

2. It must resolve back-references to previous method results that were processed on a different server. This is a relatively simple syntactic substitution, described in Section 3.7.

2. 它必须解析对以前在不同服务器上处理的方法结果的反向引用。这是一个相对简单的句法替换,如第3.7节所述。

When splitting a request based on accountId, proxy implementors do need to be aware of "/copy" methods that copy between accounts. If the accounts are on different servers, the proxy will have to implement this functionality directly.

当基于accountId拆分请求时,代理实现者确实需要知道在帐户之间进行复制的“/copy”方法。如果帐户位于不同的服务器上,则代理必须直接实现此功能。

6. Binary Data
6. 二进制数据

Binary data is referenced by a *blobId* in JMAP and uploaded/ downloaded separately to the core API. The blobId solely represents the raw bytes of data, not any associated metadata such as a file name or content type. Such metadata is stored alongside the blobId in the object referencing it. The data represented by a blobId is immutable.

二进制数据由JMAP中的*blobId*引用,并单独上传/下载到核心API。blobId只表示数据的原始字节,而不是任何相关的元数据,如文件名或内容类型。此类元数据与引用它的对象中的blobId一起存储。blobId表示的数据是不可变的。

Any blobId that exists within an account may be used when creating/ updating another object in that account. For example, an Email type may have a blobId that represents the object in Internet Message Format [RFC5322]. A client could create a new Email object with an attachment and use this blobId, in effect attaching the old message to the new one. Similarly, it could attach any existing attachment of an old message without having to download and upload it again.

在帐户中创建/更新另一个对象时,可以使用帐户中存在的任何blobId。例如,电子邮件类型可能具有以Internet消息格式[RFC5322]表示对象的blobId。客户端可以创建带有附件的新电子邮件对象并使用此blobId,实际上是将旧邮件附加到新邮件。类似地,它可以附加旧邮件的任何现有附件,而无需再次下载和上传。

When the client uses a blobId in a create/update, the server MAY assign a new blobId to refer to the same binary data within the new/ updated object. If it does so, it MUST return any properties that contain a changed blobId in the created/updated response, so the client gets the new ids.

当客户端在创建/更新中使用blobId时,服务器可能会分配一个新的blobId来引用新/更新对象中的相同二进制数据。如果这样做,它必须在创建/更新的响应中返回任何包含已更改blobId的属性,以便客户端获得新ID。

A blob that is not referenced by a JMAP object (e.g., as a message attachment) MAY be deleted by the server to free up resources. Uploads (see below) are initially unreferenced blobs. To ensure interoperability:

未被JMAP对象引用的blob(例如,作为消息附件)可能会被服务器删除以释放资源。上传(见下文)最初是未引用的blob。为确保互操作性:

o The server SHOULD use a separate quota for unreferenced blobs to the account's usual quota. In the case of shared accounts, this quota SHOULD be separate per user.

o 服务器应该为未引用的blob使用单独的配额,而不是帐户的常规配额。在共享帐户的情况下,此配额应按用户分开。

o This quota SHOULD be at least the maximum total size that a single object can reference on this server. For example, if supporting JMAP Mail, this should be at least the maximum total attachments size for a message.

o 此配额至少应为此服务器上单个对象可以引用的最大总大小。例如,如果支持JMAP邮件,则至少应为邮件的最大总附件大小。

o When an upload would take the user over quota, the server MUST delete unreferenced blobs in date order, oldest first, until there is room for the new blob.

o 当上传会让用户超过配额时,服务器必须按日期顺序删除未引用的blob,最早的优先,直到有空间容纳新blob。

o Except where quota restrictions force early deletion, an unreferenced blob MUST NOT be deleted for at least 1 hour from the time of upload; if reuploaded, the same blobId MAY be returned, but this SHOULD reset the expiry time.

o 除非配额限制强制提前删除,否则在上传后至少1小时内不得删除未引用的blob;如果重新加载,可能会返回相同的blobId,但这将重置到期时间。

o A blob MUST NOT be deleted during the method call that removed the last reference, so that a client can issue a create and a destroy that both reference the blob within the same method call.

o 在删除最后一个引用的方法调用期间,不能删除blob,这样客户端就可以发出create和destroy命令,这两个命令都在同一个方法调用中引用blob。

6.1. Uploading Binary Data
6.1. 上传二进制数据

There is a single endpoint that handles all file uploads for an account, regardless of what they are to be used for. The Session object (see Section 2) has an "uploadUrl" property in URI Template (level 1) format [RFC6570], which MUST contain a variable called "accountId". The client may use this template in combination with an "accountId" to get the URL of the file upload resource.

有一个端点可以处理帐户的所有文件上载,而不管它们用于什么目的。会话对象(参见第2节)具有URI模板(级别1)格式[RFC6570]的“uploadUrl”属性,该属性必须包含一个名为“accountId”的变量。客户端可以将此模板与“accountId”结合使用,以获取文件上载资源的URL。

To upload a file, the client submits an authenticated POST request to the file upload resource.

要上载文件,客户端向文件上载资源提交经过身份验证的POST请求。

A successful request MUST return a single JSON object with the following properties as the response:

成功的请求必须返回具有以下属性的单个JSON对象作为响应:

o accountId: "Id"

o 帐户Id:“Id”

The id of the account used for the call.

用于呼叫的帐户的id。

o blobId: "Id"

o blobId:“Id”

The id representing the binary data uploaded. The data for this id is immutable. The id *only* refers to the binary data, not any metadata.

表示上载的二进制数据的id。此id的数据是不可变的。id*only*表示二进制数据,而不是任何元数据。

o type: "String"

o 类型:“字符串”

The media type of the file (as specified in [RFC6838], Section 4.2) as set in the Content-Type header of the upload HTTP request.

上传HTTP请求的内容类型标头中设置的文件的媒体类型(如[RFC6838]第4.2节所述)。

o size: "UnsignedInt"

o 大小:“未签名”

The size of the file in octets.

文件的大小(以八位字节为单位)。

If identical binary content to an existing blob in the account is uploaded, the existing blobId MAY be returned.

如果上载了与帐户中现有blob相同的二进制内容,则可能会返回现有blob。

Clients should use the blobId returned in a timely manner. Under rare circumstances, the server may have deleted the blob before the client uses it; the client should keep a reference to the local file so it can upload it again in such a situation.

客户应及时使用退回的blobId。在极少数情况下,服务器可能在客户端使用blob之前删除了blob;客户端应该保留对本地文件的引用,以便在这种情况下可以再次上传。

When an HTTP error response is returned to the client, the server SHOULD return a JSON "problem details" object as the response body, as per [RFC7807].

当HTTP错误响应返回到客户机时,服务器应该返回一个JSON“problem details”对象作为响应主体,如[RFC7807]所示。

As access controls are often determined by the object holding the reference to a blob, unreferenced blobs MUST only be accessible to the uploader, even in shared accounts.

由于访问控制通常由持有对blob的引用的对象确定,因此未引用的blob只能由上传者访问,即使是在共享帐户中。

6.2. Downloading Binary Data
6.2. 下载二进制数据

The Session object (see Section 2) has a "downloadUrl" property, which is in URI Template (level 1) format [RFC6570]. The URL MUST contain variables called "accountId", "blobId", "type", and "name".

会话对象(参见第2节)有一个“downloadUrl”属性,它是URI模板(级别1)格式[RFC6570]。URL必须包含名为“accountId”、“blobId”、“type”和“name”的变量。

To download a file, the client makes an authenticated GET request to the download URL with the appropriate variables substituted in:

要下载文件,客户端向下载URL发出经过身份验证的GET请求,并在以下内容中替换相应的变量:

o "accountId": The id of the account to which the record with the blobId belongs.

o “accountId”:包含blobId的记录所属的帐户的id。

o "blobId": The blobId representing the data of the file to download.

o “blobId”:表示要下载的文件数据的blobId。

o "type": The type for the server to set in the "Content-Type" header of the response; the blobId only represents the binary data and does not have a content-type innately associated with it.

o “类型”:服务器在响应的“内容类型”标题中设置的类型;blobId只表示二进制数据,没有与之固有关联的内容类型。

o "name": The name for the file; the server MUST return this as the filename if it sets a "Content-Disposition" header.

o “名称”:文件的名称;如果服务器设置了“内容处置”标题,则必须将其作为文件名返回。

As the data for a particular blobId is immutable, and thus the response in the generated download URL is too, implementors are recommended to set long cache times and use the "immutable" Cache-Control extension [RFC8246] for successful responses, for example, "Cache-Control: private, immutable, max-age=31536000".

由于特定blobId的数据是不可变的,因此生成的下载URL中的响应也是不可变的,因此建议实现者设置较长的缓存时间,并使用“不可变”缓存控制扩展[RFC8246]来成功响应,例如,“缓存控制:private,immutable,max age=31536000”。

When an HTTP error response is returned to the client, the server SHOULD return a JSON "problem details" object as the response body, as per [RFC7807].

当HTTP错误响应返回到客户机时,服务器应该返回一个JSON“problem details”对象作为响应主体,如[RFC7807]所示。

6.3. Blob/copy
6.3. Blob/副本

Binary data may be copied *between* two different accounts using the "Blob/copy" method rather than having to download and then reupload on the client.

二进制数据可以使用“Blob/copy”方法在两个不同的帐户之间*复制,而不必下载然后在客户端上重新加载。

The "Blob/copy" method takes the following arguments:

“Blob/copy”方法采用以下参数:

o fromAccountId: "Id"

o fromAccountId:“Id”

The id of the account to copy blobs from.

要从中复制Blob的帐户的id。

o accountId: "Id"

o 帐户Id:“Id”

The id of the account to copy blobs to.

要将Blob复制到的帐户的id。

o blobIds: "Id[]"

o blobIds:“Id[]”

A list of ids of blobs to copy to the other account.

要复制到其他帐户的Blob ID的列表。

The response has the following arguments:

响应具有以下参数:

o fromAccountId: "Id"

o fromAccountId:“Id”

The id of the account blobs were copied from.

已从中复制帐户Blob的id。

o accountId: "Id"

o 帐户Id:“Id”

The id of the account blobs were copied to.

已将帐户Blob的id复制到。

o copied: "Id[Id]|null"

o 复制:“Id[Id]|空”

A map of the blobId in the fromAccount to the id for the blob in the account it was copied to, or null if none were successfully copied.

fromAccount中的blobId映射到其复制到的帐户中blob的id,如果没有成功复制,则映射为null。

o notCopied: "Id[SetError]|null"

o NotCopy:“Id[SetError]| null”

A map of blobId to a SetError object for each blob that failed to be copied, or null if none.

无法复制的每个blob的blobId到SetError对象的映射,如果没有,则为null。

The SetError may be any of the standard set errors that may be returned for a create, as defined in Section 5.3. In addition, the "notFound" SetError error may be returned if the blobId to be copied cannot be found.

SetError可以是第5.3节中定义的创建返回的任何标准集合错误。此外,如果找不到要复制的blobId,则可能会返回“notFound”SetError错误。

The following additional method-level error may be returned instead of the "Blob/copy" response:

可能会返回以下附加的方法级错误,而不是“Blob/copy”响应:

"fromAccountNotFound": The "fromAccountId" included with the request does not correspond to a valid account.

“fromAccountNotFound”:请求中包含的“fromAccountId”与有效帐户不对应。

7. Push
7. 推

Push notifications allow clients to efficiently update (almost) instantly to stay in sync with data changes on the server. The general model for push is simple and sends minimal data over the push channel: just enough for the client to know whether it needs to resync. The format allows multiple changes to be coalesced into a single push update and the frequency of pushes to be rate limited by the server. It doesn't matter if some push events are dropped before they reach the client; the next time it gets/sets any records of a changed type, it will discover the data has changed and still sync all changes.

推送通知允许客户端高效地(几乎)立即更新,以与服务器上的数据更改保持同步。推送的一般模型很简单,通过推送通道发送的数据最少:只足以让客户端知道是否需要重新同步。该格式允许将多个更改合并到一个推送更新中,推送的频率由服务器限制。一些推送事件是否在到达客户端之前被丢弃并不重要;下次获取/设置任何更改类型的记录时,它将发现数据已更改,并且仍然同步所有更改。

There are two different mechanisms by which a client can receive push notifications, to allow for the different environments in which a client may exist. An event source resource (see Section 7.3) allows clients that can hold transport connections open to receive push notifications directly from the JMAP server. This is simple and avoids third parties, but it is often not feasible on constrained platforms such as mobile devices. Alternatively, clients can make use of any push service supported by their environment. A URL for the push service is registered with the JMAP server (see Section 7.2); the server then POSTs each notification to that URL. The push service is then responsible for routing these to the client.

客户端可以通过两种不同的机制接收推送通知,以适应客户端可能存在的不同环境。事件源资源(参见第7.3节)允许可以保持传输连接打开的客户端直接从JMAP服务器接收推送通知。这很简单,避免了第三方,但在移动设备等受限平台上通常不可行。或者,客户端可以使用其环境支持的任何推送服务。推送服务的URL在JMAP服务器上注册(参见第7.2节);然后服务器将每个通知发布到该URL。推送服务然后负责将这些路由到客户端。

7.1. The StateChange Object
7.1. StateChange对象

When something changes on the server, the server pushes a StateChange object to the client. A *StateChange* object has the following properties:

当服务器上发生更改时,服务器会将StateChange对象推送到客户端。*StateChange*对象具有以下属性:

o @type: "String"

o @类型:“字符串”

This MUST be the string "StateChange".

这必须是字符串“StateChange”。

o changed: "Id[TypeState]"

o 更改:“Id[TypeState]”

A map of an "account id" to an object encoding the state of data types that have changed for that account since the last StateChange object was pushed, for each of the accounts to which the user has access and for which something has changed.

一个“帐户id”到一个对象的映射,该对象编码自上一个StateChange对象被推送以来该帐户的数据类型的状态,对于用户有权访问的每个帐户以及某些更改的帐户。

A *TypeState* object is a map. The keys are the type name "Foo" (e.g., "Mailbox" or "Email"), and the value is the "state" property that would currently be returned by a call to "Foo/get".

*TypeState*对象是一个映射。键是类型名“Foo”(例如,“Mailbox”或“Email”),值是当前通过调用“Foo/get”返回的“state”属性。

The client can compare the new state strings with its current values to see whether it has the current data for these types. If not, the changes can then be efficiently fetched in a single standard API request (using the /changes type methods).

客户端可以将新状态字符串与其当前值进行比较,以查看是否具有这些类型的当前数据。如果没有,则可以在单个标准API请求中高效地获取更改(使用/changes类型方法)。

7.1.1. Example
7.1.1. 实例

In this example, the server has amalgamated a few changes together across two different accounts the user has access to, before pushing the following StateChange object to the client:

在此示例中,在将以下StateChange对象推送到客户端之前,服务器已将用户有权访问的两个不同帐户中的一些更改合并在一起:

                  {
                    "@type": "StateChange",
                    "changed": {
                      "a3123": {
                        "Email": "d35ecb040aab",
                        "EmailDelivery": "428d565f2440",
                        "CalendarEvent": "87accfac587a"
                      },
                      "a43461d": {
                        "Mailbox": "0af7a512ce70",
                        "CalendarEvent": "7a4297cecd76"
                      }
                    }
                  }
        
                  {
                    "@type": "StateChange",
                    "changed": {
                      "a3123": {
                        "Email": "d35ecb040aab",
                        "EmailDelivery": "428d565f2440",
                        "CalendarEvent": "87accfac587a"
                      },
                      "a43461d": {
                        "Mailbox": "0af7a512ce70",
                        "CalendarEvent": "7a4297cecd76"
                      }
                    }
                  }
        

The client can compare the state strings with its current state for the Email, CalendarEvent, etc., object types in the appropriate accounts to see if it needs to fetch changes.

客户端可以将状态字符串与相应帐户中的电子邮件、CalendarEvent等对象类型的当前状态进行比较,以查看是否需要获取更改。

If the client is itself making changes, it may receive a StateChange object while the /set API call is in flight. It can wait until the call completes and then compare if the new state string after the /set is the same as was pushed in the StateChange object; if so, and the old state of the /set response matches the client's previous state, it does not need to waste a request asking for changes it already knows.

如果客户机本身正在进行更改,则在/set API调用进行时,它可能会收到一个StateChange对象。它可以等到调用完成,然后比较/set之后的新状态字符串是否与StateChange对象中推送的相同;如果是这样,并且/set响应的旧状态与客户机以前的状态匹配,那么它就不需要浪费一个请求来请求它已经知道的更改。

7.2. PushSubscription
7.2. 推送订阅

Clients may create a PushSubscription to register a URL with the JMAP server. The JMAP server will then make an HTTP POST request to this URL for each push notification it wishes to send to the client.

客户端可以创建PushSubscription以向JMAP服务器注册URL。然后,JMAP服务器将针对希望发送给客户端的每个推送通知向该URL发出HTTP POST请求。

As a push subscription causes the JMAP server to make a number of requests to a previously unknown endpoint, it can be used as a vector for launching a denial-of-service attack. To prevent this, when a subscription is created, the JMAP server immediately sends a PushVerification object to that URL (see Section 7.2.2). The JMAP server MUST NOT make any further requests to the URL until the client receives the push and updates the subscription with the correct verification code.

由于推送订阅导致JMAP服务器向以前未知的端点发出大量请求,因此它可以用作发起拒绝服务攻击的载体。为了防止出现这种情况,当创建订阅时,JMAP服务器会立即向该URL发送一个PushVerification对象(参见第7.2.2节)。在客户端收到推送并使用正确的验证代码更新订阅之前,JMAP服务器不得对URL发出任何进一步的请求。

A *PushSubscription* object has the following properties:

*PushSubscription*对象具有以下属性:

o id: "Id" (immutable; server-set)

o id:“id”(不可变;服务器集)

The id of the push subscription.

推送订阅的id。

o deviceClientId: "String" (immutable)

o deviceClientId:“字符串”(不可变)

An id that uniquely identifies the client + device it is running on. The purpose of this is to allow clients to identify which PushSubscription objects they created even if they lose their local state, so they can revoke or update them. This string MUST be different on different devices and be different from apps from other vendors. It SHOULD be easy to regenerate and not depend on persisted state. It is RECOMMENDED to use a secure hash of a string that contains:

唯一标识正在运行的客户端+设备的id。这样做的目的是允许客户端识别他们创建的PushSubscription对象,即使它们丢失了本地状态,也可以撤销或更新它们。此字符串在不同的设备上必须不同,并且与其他供应商的应用程序不同。它应该易于重新生成,并且不依赖于持久化状态。建议使用包含以下内容的字符串的安全哈希:

1. A unique identifier associated with the device where the JMAP client is running, normally supplied by the device's operating system.

1. 与运行JMAP客户端的设备相关联的唯一标识符,通常由设备的操作系统提供。

2. A custom vendor/app id, including a domain controlled by the vendor of the JMAP client.

2. 自定义供应商/应用程序id,包括由JMAP客户端供应商控制的域。

To protect the privacy of the user, the deviceClientId id MUST NOT contain an unobfuscated device id.

为保护用户隐私,deviceClientId不得包含未混淆的设备id。

o url: "String" (immutable)

o url:“字符串”(不可变)

An absolute URL where the JMAP server will POST the data for the push message. This MUST begin with "https://".

JMAP服务器将在其中发布推送消息数据的绝对URL。这必须以“https://”开头。

o keys: "Object|null" (immutable)

o 键:“对象| null”(不可变)

Client-generated encryption keys. If supplied, the server MUST use them as specified in [RFC8291] to encrypt all data sent to the push subscription. The object MUST have the following properties:

客户端生成的加密密钥。如果提供,服务器必须按照[RFC8291]中的规定使用它们来加密发送到推送订阅的所有数据。对象必须具有以下属性:

* p256dh: "String"

* p256dh:“字符串”

The P-256 Elliptic Curve Diffie-Hellman (ECDH) public key as described in [RFC8291], encoded in URL-safe base64 representation as defined in [RFC4648].

[RFC8291]中描述的P-256椭圆曲线Diffie-Hellman(ECDH)公钥,按照[RFC4648]中定义的URL安全base64表示进行编码。

* auth: "String"

* auth:“字符串”

The authentication secret as described in [RFC8291], encoded in URL-safe base64 representation as defined in [RFC4648].

[RFC8291]中描述的身份验证秘密,以[RFC4648]中定义的URL安全base64表示形式编码。

o verificationCode: "String|null"

o 验证代码:“字符串|空”

This MUST be null (or omitted) when the subscription is created. The JMAP server then generates a verification code and sends it in a push message, and the client updates the PushSubscription object with the code; see Section 7.2.2 for details.

创建订阅时,该值必须为null(或省略)。然后,JMAP服务器生成一个验证代码并以推送消息的形式发送,客户端用该代码更新PushSubscription对象;详见第7.2.2节。

o expires: "UTCDate|null"

o 过期:“UTCDate |空”

The time this push subscription expires. If specified, the JMAP server MUST NOT make further requests to this resource after this time. It MAY automatically destroy the push subscription at or after this time.

此推送订阅过期的时间。如果指定,JMAP服务器在此时间之后不得对该资源发出进一步的请求。此时或之后,它可能会自动销毁推送订阅。

The server MAY choose to set an expiry if none is given by the client or modify the expiry time given by the client to a shorter duration.

如果客户机没有给出到期时间,服务器可以选择设置到期时间,或者将客户机给出的到期时间修改为更短的持续时间。

o types: "String[]|null"

o 类型:“字符串[]空”

A list of types the client is interested in (using the same names as the keys in the TypeState object defined in the previous section). A StateChange notification will only be sent if the data for one of these types changes. Other types are omitted from the TypeState object. If null, changes will be pushed for all types.

客户机感兴趣的类型列表(使用与上一节中定义的TypeState对象中的键相同的名称)。仅当其中一种类型的数据发生更改时,才会发送状态更改通知。TypeState对象中省略了其他类型。如果为null,则将为所有类型推送更改。

The POST request MUST have a content type of "application/json" and contain the UTF-8 JSON-encoded object as the body. The request MUST have a "TTL" header and MAY have "Urgency" and/or "Topic" headers, as specified in Section 5 of [RFC8030]. The JMAP server is expected to understand and handle HTTP status responses in a reasonable manner. A "429" (Too Many Requests) response MUST cause the JMAP server to reduce the frequency of pushes; the JMAP push structure allows multiple changes to be coalesced into a single minimal StateChange object. See the security considerations in Section 8.6 for a discussion of the risks in connecting to unknown servers.

POST请求的内容类型必须为“application/json”,并包含UTF-8 json编码的对象作为主体。如[RFC8030]第5节所述,请求必须具有“TTL”标题,并且可能具有“紧迫性”和/或“主题”标题。JMAP服务器应该以合理的方式理解和处理HTTP状态响应。“429”(太多请求)响应必须导致JMAP服务器减少推送的频率;JMAP推送结构允许将多个更改合并到单个最小状态更改对象中。有关连接未知服务器的风险讨论,请参阅第8.6节中的安全注意事项。

The JMAP server acts as an application server as defined in [RFC8030]. A client MAY use the rest of [RFC8030] in combination with its own push service to form a complete end-to-end solution, or it MAY rely on alternative mechanisms to ensure the delivery of the pushed data after it leaves the JMAP server.

JMAP服务器充当[RFC8030]中定义的应用程序服务器。客户机可以将[RFC8030]的其余部分与自己的推送服务结合使用,形成一个完整的端到端解决方案,也可以依赖其他机制确保推送数据在离开JMAP服务器后的交付。

The push subscription is tied to the credentials used to authenticate the API request that created it. Should these credentials expire or be revoked, the push subscription MUST be destroyed by the JMAP

推送订阅绑定到用于验证创建它的API请求的凭据。如果这些凭据过期或被吊销,推送订阅必须由JMAP销毁

server. Only subscriptions created by these credentials are returned when the client fetches existing subscriptions.

服务器当客户端获取现有订阅时,仅返回由这些凭据创建的订阅。

When these credentials have their own expiry (i.e., it is a session with a timeout), the server SHOULD NOT set or bound the expiry time for the push subscription given by the client but MUST expire it when the session expires.

当这些凭据有自己的到期日(即,这是一个有超时的会话)时,服务器不应设置或绑定客户端提供的推送订阅的到期日,但必须在会话到期时使其到期。

When these credentials are not time bounded (e.g., Basic authentication [RFC7617]), the server SHOULD set an expiry time for the push subscription if none is given and limit the expiry time if set too far in the future. This maximum expiry time MUST be at least 48 hours in the future and SHOULD be at least 7 days in the future. An app running on a mobile device may only be able to refresh the push subscription lifetime when it is in the foreground, so this gives a reasonable time frame to allow this to happen.

如果这些凭据没有时间限制(例如,基本身份验证[RFC7617]),则服务器应设置推送订阅的到期时间(如果未提供到期时间),并限制到期时间(如果在将来设置得太远)。该最长到期时间必须至少为48小时,且应至少为7天。在移动设备上运行的应用程序可能只有在前台时才能刷新推送订阅生存期,因此这提供了一个合理的时间框架来允许这种情况发生。

In the case of separate access and refresh credentials, as in Oauth 2.0 [RFC6749], the server SHOULD tie the push subscription to the validity of the refresh token rather than the access token and behave according to whether this is time-limited or not.

在单独访问和刷新凭据的情况下,如Oauth 2.0[RFC6749]中所述,服务器应将推送订阅与刷新令牌(而不是访问令牌)的有效性联系起来,并根据是否有时间限制进行操作。

When a push subscription is destroyed, the server MUST securely erase the URL and encryption keys from memory and storage as soon as possible.

当推送订阅被销毁时,服务器必须尽快从内存和存储中安全地擦除URL和加密密钥。

7.2.1. PushSubscription/get
7.2.1. 推送订阅/获取

Standard /get method as described in Section 5.1, except it does *not* take or return an "accountId" argument, as push subscriptions are not tied to specific accounts. It also does *not* return a "state" argument. The "ids" argument may be null to fetch all at once.

标准/get方法,如第5.1节所述,但它不*接受或返回“accountId”参数,因为推送订阅不绑定到特定帐户。它也*不*返回“state”参数。“ids”参数可以为null,以便一次获取所有内容。

The server MUST only return push subscriptions that were created using the same authentication credentials as for this "PushSubscription/get" request.

服务器必须仅返回使用与此“PushSubscription/get”请求相同的身份验证凭据创建的推送订阅。

As the "url" and "keys" properties may contain data that is private to a particular device, the values for these properties MUST NOT be returned. If the "properties" argument is null or omitted, the server MUST default to all properties excluding these two. If one of them is explicitly requested, the method call MUST be rejected with a "forbidden" error.

由于“url”和“密钥”属性可能包含特定设备专用的数据,因此不得返回这些属性的值。如果“properties”参数为null或省略,则服务器必须默认为除这两个属性之外的所有属性。如果显式请求其中一个,则必须拒绝方法调用,并出现“禁止”错误。

7.2.2. PushSubscription/set
7.2.2. 推送订阅/套

Standard /set method as described in Section 5.3, except it does *not* take or return an "accountId" argument, as push subscriptions are not tied to specific accounts. It also does *not* take an "ifInState" argument or return "oldState" or "newState" arguments.

标准/设置方法,如第5.3节所述,但它不*接受或返回“accountId”参数,因为推送订阅不绑定到特定帐户。它也不*接受“ifInState”参数或返回“oldState”或“newState”参数。

The "url" and "keys" properties are immutable; if the client wishes to change these, it must destroy the current push subscription and create a new one.

“url”和“keys”属性是不可变的;如果客户端希望更改这些内容,则必须销毁当前推送订阅并创建新订阅。

When a PushSubscription is created, the server MUST immediately push a *PushVerification* object to the URL. It has the following properties:

创建PushSubscription时,服务器必须立即将*PushVerification*对象推送到URL。它具有以下属性:

o @type: "String"

o @类型:“字符串”

This MUST be the string "PushVerification".

这必须是字符串“PushVerification”。

o pushSubscriptionId: "String"

o pushSubscriptionId:“字符串”

The id of the push subscription that was created.

已创建的推送订阅的id。

o verificationCode: "String"

o 验证代码:“字符串”

The verification code to add to the push subscription. This MUST contain sufficient entropy to avoid the client being able to guess the code via brute force.

要添加到推送订阅的验证代码。这必须包含足够的熵,以避免客户端能够通过暴力猜测代码。

The client MUST update the push subscription with the correct verification code before the server makes any further requests to the subscription's URL. Attempts to update the subscription with an invalid verification code MUST be rejected by the server with an "invalidProperties" SetError.

在服务器进一步请求订阅的URL之前,客户端必须使用正确的验证代码更新推送订阅。服务器必须以“invalidProperties”设置错误拒绝使用无效验证码更新订阅的尝试。

The client may update the "expires" property to extend (or, less commonly, shorten) the lifetime of a push subscription. The server MAY modify the proposed new expiry time to enforce server-defined limits. Extending the lifetime does not require the subscription to be verified again.

客户端可以更新“expires”属性以延长(或者,不太常见的情况是缩短)推送订阅的生存期。服务器可以修改建议的新到期时间,以强制执行服务器定义的限制。延长生存期不需要再次验证订阅。

Clients SHOULD NOT update or destroy a push subscription that they did not create (i.e., has a "deviceClientId" that they do not recognise).

客户端不应更新或销毁他们未创建的推送订阅(即,拥有他们无法识别的“deviceClientId”)。

7.2.3. Example
7.2.3. 实例

At "2018-07-06T02:14:29Z", a client with deviceClientId "a889-ffea-910" fetches the set of push subscriptions currently on the server, making an API request with:

在“2018-07-06T02:14:29Z”,具有deviceClientId“a889-ffea-910”的客户端获取服务器上当前的推送订阅集,并通过以下方式发出API请求:

[[ "PushSubscription/get", { "ids": null }, "0" ]]

[[“PushSubscription/get”,{“ids”:null},“0”]]

Which returns:

返回:

       [[ "PushSubscription/get", {
         "list": [{
             "id": "e50b2c1d-9553-41a3-b0a7-a7d26b599ee1",
             "deviceClientId": "b37ff8001ca0",
             "verificationCode": "b210ef734fe5f439c1ca386421359f7b",
             "expires": "2018-07-31T00:13:21Z",
             "types": [ "Todo" ]
         }, {
             "id": "f2d0aab5-e976-4e8b-ad4b-b380a5b987e4",
             "deviceClientId": "X8980fc",
             "verificationCode": "f3d4618a9ae15c8b7f5582533786d531",
             "expires": "2018-07-12T05:55:00Z",
             "types": [ "Mailbox", "Email", "EmailDelivery" ]
         }],
         "notFound": []
       }, "0" ]]
        
       [[ "PushSubscription/get", {
         "list": [{
             "id": "e50b2c1d-9553-41a3-b0a7-a7d26b599ee1",
             "deviceClientId": "b37ff8001ca0",
             "verificationCode": "b210ef734fe5f439c1ca386421359f7b",
             "expires": "2018-07-31T00:13:21Z",
             "types": [ "Todo" ]
         }, {
             "id": "f2d0aab5-e976-4e8b-ad4b-b380a5b987e4",
             "deviceClientId": "X8980fc",
             "verificationCode": "f3d4618a9ae15c8b7f5582533786d531",
             "expires": "2018-07-12T05:55:00Z",
             "types": [ "Mailbox", "Email", "EmailDelivery" ]
         }],
         "notFound": []
       }, "0" ]]
        

Since neither of the returned push subscription objects have the client's deviceClientId, it knows it does not have a current push subscription active on the server. So it creates one, sending this request:

由于返回的推送订阅对象都没有客户端的deviceClientId,因此它知道服务器上没有活动的当前推送订阅。所以它创建了一个,发送这个请求:

[[ "PushSubscription/set", {
  "create": {
    "4f29": {
      "deviceClientId": "a889-ffea-910",
      "url": "https://example.com/push/?device=X8980fc&client=12c6d086",
      "types": null
    }
  }
}, "0" ]]
        
[[ "PushSubscription/set", {
  "create": {
    "4f29": {
      "deviceClientId": "a889-ffea-910",
      "url": "https://example.com/push/?device=X8980fc&client=12c6d086",
      "types": null
    }
  }
}, "0" ]]
        

The server creates the push subscription but limits the expiry time to 7 days in the future, returning this response:

服务器创建推送订阅,但将到期时间限制为7天,并返回以下响应:

            [[ "PushSubscription/set", {
              "created": {
                "4f29": {
                  "id": "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60",
                  "keys": null,
                  "expires": "2018-07-13T02:14:29Z"
                }
              }
            }, "0" ]]
        
            [[ "PushSubscription/set", {
              "created": {
                "4f29": {
                  "id": "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60",
                  "keys": null,
                  "expires": "2018-07-13T02:14:29Z"
                }
              }
            }, "0" ]]
        

The server also immediately makes a POST request to "https://example.com/push/?device=X8980fc&client=12c6d086" with the data:

服务器还立即向“发送POST请求”https://example.com/push/?device=X8980fc&client=12c6d086“有了这些数据:

      {
        "@type": "PushVerification",
        "pushSubscriptionId": "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60",
        "verificationCode": "da1f097b11ca17f06424e30bf02bfa67"
      }
        
      {
        "@type": "PushVerification",
        "pushSubscriptionId": "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60",
        "verificationCode": "da1f097b11ca17f06424e30bf02bfa67"
      }
        

The client receives this and updates the subscription with the verification code (note there is a potential race condition here; the client MUST be able to handle receiving the push while the request creating the subscription is still in progress):

客户端收到此消息并使用验证码更新订阅(注意,此处存在潜在的争用条件;客户端必须能够在创建订阅的请求仍在进行时处理接收推送):

       [[ "PushSubscription/set", {
         "update": {
           "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
             "verificationCode": "da1f097b11ca17f06424e30bf02bfa67"
           }
         }
       }, "0" ]]
        
       [[ "PushSubscription/set", {
         "update": {
           "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
             "verificationCode": "da1f097b11ca17f06424e30bf02bfa67"
           }
         }
       }, "0" ]]
        

The server confirms the update was successful and will now make requests to the registered URL when the state changes.

服务器确认更新成功,并在状态更改时向注册的URL发出请求。

Two days later, the client updates the subscription to extend its lifetime, sending this request:

两天后,客户端更新订阅以延长其生存期,并发送以下请求:

               [[ "PushSubscription/set", {
                 "update": {
                   "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
                     "expires": "2018-08-13T00:00:00Z"
                   }
                 }
               }, "0" ]]
        
               [[ "PushSubscription/set", {
                 "update": {
                   "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
                     "expires": "2018-08-13T00:00:00Z"
                   }
                 }
               }, "0" ]]
        

The server extends the expiry time, but only again to its maximum limit of 7 days in the future, returning this response:

服务器会延长到期时间,但在将来只会再次延长到其最大限制7天,并返回以下响应:

               [[ "PushSubscription/set", {
                 "updated": {
                   "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
                     "expires": "2018-07-15T02:22:50Z"
                   }
                 }
               }, "0" ]]
        
               [[ "PushSubscription/set", {
                 "updated": {
                   "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
                     "expires": "2018-07-15T02:22:50Z"
                   }
                 }
               }, "0" ]]
        
7.3. Event Source
7.3. 事件源

Clients that can hold transport connections open can connect directly to the JMAP server to receive push notifications via a "text/event-stream" resource, as described in [EventSource]. This is a long running HTTP request, where the server can push data to the client by appending data without ending the response.

可以保持传输连接打开的客户端可以直接连接到JMAP服务器,通过“文本/事件流”资源接收推送通知,如[EventSource]中所述。这是一个长时间运行的HTTP请求,服务器可以通过追加数据将数据推送到客户端,而无需结束响应。

When a change occurs in the data on the server, it pushes an event called "state" to any connected clients, with the StateChange object as the data.

当服务器上的数据发生更改时,它会将名为“state”的事件推送到任何连接的客户端,并将StateChange对象作为数据。

The server SHOULD also send a new event id that encodes the entire server state visible to the user immediately after sending a "state" event. When a new connection is made to the event-source endpoint, a client following the server-sent events specification will send a Last-Event-ID HTTP header field with the last id it saw, which the server can use to work out whether the client has missed some changes. If so, it SHOULD send these changes immediately on connection.

服务器还应发送一个新的事件id,该id在发送“状态”事件后立即对用户可见的整个服务器状态进行编码。当与事件源端点建立新连接时,遵循server sent events规范的客户端将发送最后一个事件ID HTTP头字段,其中包含它看到的最后一个ID,服务器可以使用该字段确定客户端是否遗漏了某些更改。如果是这样,它应该在连接时立即发送这些更改。

The Session object (see Section 2) has an "eventSourceUrl" property, which is in URI Template (level 1) format [RFC6570]. The URL MUST contain variables called "types", "closeafter", and "ping".

会话对象(参见第2节)有一个“eventSourceUrl”属性,该属性采用URI模板(级别1)格式[RFC6570]。URL必须包含名为“type”、“closeafter”和“ping”的变量。

To connect to the resource, the client makes an authenticated GET request to the event-source URL with the appropriate variables substituted in:

要连接到资源,客户端向事件源URL发出经过身份验证的GET请求,并在以下内容中替换相应的变量:

o "types": This MUST be either:

o “类型”:必须是:

* A comma-separated list of type names, e.g., "Email,CalendarEvent". The server MUST only push changes for the types in this list.

* 以逗号分隔的类型名称列表,例如“电子邮件、日历事件”。服务器只能推送此列表中类型的更改。

* The single character: "*". Changes to all types are pushed.

* 单个字符:“*”。对所有类型的更改都被推送。

o "closeafter": This MUST be one of the following values:

o “closeafter”:这必须是以下值之一:

* "state": The server MUST end the HTTP response after pushing a state event. This can be used by clients in environments where buffering proxies prevent the pushed data from arriving immediately, or indeed at all, when operating in the usual mode.

* “状态”:服务器必须在推送状态事件后结束HTTP响应。在缓冲代理阻止推送数据立即到达的环境中,客户机可以使用此功能,或者在正常模式下操作时,完全可以使用此功能。

* "no": The connection is persisted by the server as a standard event-source resource.

* “否”:服务器将连接作为标准事件源资源进行持久化。

o "ping": A positive integer value representing a length of time in seconds, e.g., "300". If non-zero, the server MUST send an event called "ping" whenever this time elapses since the previous event was sent. This MUST NOT set a new event id. If the value is "0", the server MUST NOT send ping events.

o “ping”:表示时间长度(以秒为单位)的正整数值,例如“300”。如果非零,则服务器必须在自上次事件发送以来的任何时间内发送一个名为“ping”的事件。这不得设置新的事件id。如果值为“0”,则服务器不得发送ping事件。

The server MAY modify a requested ping interval to be subject to a minimum and/or maximum value. For interoperability, servers MUST NOT have a minimum allowed value higher than 30 or a maximum allowed value less than 300.

服务器可以将请求的ping间隔修改为最小和/或最大值。对于互操作性,服务器的最小允许值不得大于30,最大允许值不得小于300。

The data for the ping event MUST be a JSON object containing an "interval" property, the value (type "UnsignedInt") being the interval in seconds the server is using to send pings (this may be different to the requested value if the server clamped it to be within a min/max value).

ping事件的数据必须是包含“interval”属性的JSON对象,值(类型“UnsignedInt”)是服务器用于发送ping的时间间隔(如果服务器将其限制在最小/最大值内,则该值可能不同于请求的值)。

Clients can monitor for the ping event to help determine when the closeafter mode may be required.

客户端可以监视ping事件,以帮助确定何时需要closeafter模式。

A client MAY hold open multiple connections to the event-source resource, although it SHOULD try to use a single connection for efficiency.

客户机可能会打开到事件源资源的多个连接,尽管为了提高效率,它应该尝试使用单个连接。

8. Security Considerations
8. 安全考虑
8.1. Transport Confidentiality
8.1. 传输机密性

To ensure the confidentiality and integrity of data sent and received via JMAP, all requests MUST use TLS 1.2 [RFC5246] [RFC8446] or later, following the recommendations in [RFC7525]. Servers SHOULD support TLS 1.3 [RFC8446] or later.

为确保通过JMAP发送和接收的数据的机密性和完整性,所有请求必须按照[RFC7525]中的建议使用TLS 1.2[RFC5246][RFC8446]或更高版本。服务器应支持TLS 1.3[RFC8446]或更高版本。

Clients MUST validate TLS certificate chains to protect against man-in-the-middle attacks [RFC5280].

客户端必须验证TLS证书链以防止中间人攻击[RFC5280]。

8.2. Authentication Scheme
8.2. 认证方案

A number of HTTP authentication schemes have been standardised (see <https://www.iana.org/assignments/http-authschemes/>). Servers should take care to assess the security characteristics of different schemes in relation to their needs when deciding what to implement.

许多HTTP认证方案已经标准化(参见<https://www.iana.org/assignments/http-authschemes/>). 在决定实施什么时,服务器应注意根据不同方案的需要评估其安全特性。

Use of the Basic authentication scheme is NOT RECOMMENDED. Services that choose to use it are strongly recommended to require generation of a unique "app password" via some external mechanism for each client they wish to connect. This allows connections from different devices to be differentiated by the server and access to be individually revoked.

不建议使用基本身份验证方案。强烈建议选择使用它的服务要求通过某些外部机制为其希望连接的每个客户端生成唯一的“应用程序密码”。这允许服务器区分来自不同设备的连接,并分别撤销访问。

8.3. Service Autodiscovery
8.3. 服务自动发现

Unless secured by something like DNSSEC, autodiscovery of server details using SRV DNS records is vulnerable to a DNS poisoning attack, which can lead to the client talking to an attacker's server instead of the real JMAP server. The attacker may then intercept requests to execute man-in-the-middle attacks and, depending on the authentication scheme, steal credentials to generate its own requests.

除非受到DNSSEC之类的保护,否则使用SRV DNS记录自动发现服务器详细信息容易受到DNS中毒攻击,这可能导致客户端与攻击者的服务器而不是真正的JMAP服务器进行对话。然后,攻击者可能会拦截执行中间人攻击的请求,并根据身份验证方案窃取凭据以生成自己的请求。

Clients that do not support SRV lookups are likely to try just using the "/.well-known/jmap" path directly against the domain of the username over HTTPS. Servers SHOULD ensure this path resolves or redirects to the correct JMAP Session resource to allow this to work. If this is not feasible, servers MUST ensure this path cannot be controlled by an attacker, as again it may be used to steal credentials.

不支持SRV查找的客户端可能会尝试通过HTTPS直接对用户名域使用“/.well-known/jmap”路径。服务器应确保此路径解析或重定向到正确的JMAP会话资源,以使其正常工作。如果这不可行,服务器必须确保攻击者无法控制此路径,因为它可能再次被用来窃取凭据。

8.4. JSON Parsing
8.4. JSON解析

The Security Considerations of [RFC8259] apply to the use of JSON as the data interchange format.

[RFC8259]的安全注意事项适用于使用JSON作为数据交换格式。

As for any serialization format, parsers need to thoroughly check the syntax of the supplied data. JSON uses opening and closing tags for several types and structures, and it is possible that the end of the supplied data will be reached when scanning for a matching closing tag; this is an error condition, and implementations need to stop scanning at the end of the supplied data.

对于任何序列化格式,解析器都需要彻底检查所提供数据的语法。JSON对几种类型和结构使用开始和结束标记,在扫描匹配的结束标记时,可能会到达所提供数据的末尾;这是一种错误情况,实现需要在提供的数据结束时停止扫描。

JSON also uses a string encoding with some escape sequences to encode special characters within a string. Care is needed when processing these escape sequences to ensure that they are fully formed before the special processing is triggered, with special care taken when the escape sequences appear adjacent to other (non-escaped) special characters or adjacent to the end of data (as in the previous paragraph).

JSON还使用带有转义序列的字符串编码对字符串中的特殊字符进行编码。在处理这些转义序列时需要小心,以确保它们在触发特殊处理之前完全成形,当转义序列与其他(非转义)特殊字符相邻或与数据结尾相邻时(如前一段所述),需要特别小心。

If parsing JSON into a non-textual structured data format, implementations may need to allocate storage to hold JSON string elements. Since JSON does not use explicit string lengths, the risk of denial of service due to resource exhaustion is small, but implementations may still wish to place limits on the size of allocations they are willing to make in any given context, to avoid untrusted data causing excessive memory allocation.

如果将JSON解析为非文本结构化数据格式,实现可能需要分配存储来保存JSON字符串元素。由于JSON不使用显式字符串长度,因此由于资源耗尽而导致拒绝服务的风险很小,但实现可能仍希望限制它们在任何给定上下文中愿意进行的分配的大小,以避免不受信任的数据导致过多的内存分配。

8.5. Denial of Service
8.5. 拒绝服务

A small request may result in a very large response and require considerable work on the server if resource limits are not enforced. JMAP provides mechanisms for advertising and enforcing a wide variety of limits for mitigating this threat, including limits on the number of objects fetched in a single method call, number of methods in a single request, number of concurrent requests, etc.

如果不强制执行资源限制,一个小请求可能会导致非常大的响应,并且需要在服务器上进行大量工作。JMAP提供了各种机制,用于宣传和强制实施各种限制以缓解这种威胁,包括对单个方法调用中获取的对象数量、单个请求中的方法数量、并发请求数量等的限制。

JMAP servers MUST implement sensible limits to mitigate against resource exhaustion attacks.

JMAP服务器必须实现合理的限制,以抵御资源耗尽攻击。

8.6. Connection to Unknown Push Server
8.6. 连接到未知推送服务器

When a push subscription is registered, the application server will make POST requests to the given URL. There are a number of security considerations that MUST be considered when implementing this.

注册推送订阅后,应用程序服务器将向给定URL发出POST请求。在实现此功能时,必须考虑许多安全因素。

The server MUST ensure the URL is externally resolvable to avoid server-side request forgery, where the server makes a request to a resource on its internal network.

服务器必须确保URL是外部可解析的,以避免服务器端请求伪造,即服务器向其内部网络上的资源发出请求。

A malicious client may use the push subscription to attempt to flood a third party server with requests, creating a denial-of-service attack and masking the attacker's true identity. There is no guarantee that the URL given to the JMAP server is actually a valid push server. Upon creation of a push subscription, the JMAP server sends a PushVerification object to the URL and MUST NOT send any further requests until the client verifies it has received the initial push. The verification code MUST contain sufficient entropy to prevent the client from being able to verify the subscription via brute force.

恶意客户端可能使用推送订阅试图向第三方服务器发送大量请求,从而造成拒绝服务攻击并掩盖攻击者的真实身份。无法保证提供给JMAP服务器的URL实际上是有效的推送服务器。在创建推送订阅时,JMAP服务器向URL发送一个PushVerification对象,并且在客户端验证其已收到初始推送之前,不得发送任何进一步的请求。验证代码必须包含足够的熵,以防止客户端能够通过暴力验证订阅。

The verification code does not guarantee the URL is a valid push server, only that the client is able to access the data submitted to it. While the verification step significantly reduces the set of potential targets, there is still a risk that the server is unrelated to the client and being targeted for a denial-of-service attack.

验证代码并不保证URL是有效的推送服务器,只保证客户端能够访问提交给它的数据。虽然验证步骤大大减少了潜在目标集,但仍然存在服务器与客户端无关并成为拒绝服务攻击目标的风险。

The server MUST limit the number of push subscriptions any one user may have to ensure the user cannot cause the server to send a large number of push notifications at once, which could again be used as part of a denial-of-service attack. The rate of creation MUST also be limited to minimise the ability to abuse the verification request as an attack vector.

服务器必须限制任何用户可能拥有的推送订阅数,以确保该用户不能导致服务器同时发送大量推送通知,这可能再次被用作拒绝服务攻击的一部分。创建的速率也必须限制以最小化验证请求作为攻击向量的滥用能力。

8.7. Push Encryption
8.7. 推送加密

When data changes, a small object is pushed with the new state strings for the types that have changed. While the data here is minimal, a passive man-in-the-middle attacker may be able to gain useful information. To ensure confidentiality and integrity, if the push is sent via a third party outside of the control of the client and JMAP server, the client MUST specify encryption keys when establishing the PushSubscription and ignore any push notification received that is not encrypted with those keys.

当数据更改时,将使用已更改类型的新状态字符串推送一个小对象。虽然这里的数据很少,但被动中间人攻击者可能能够获得有用的信息。为确保机密性和完整性,如果推送是通过客户端和JMAP服务器控制范围之外的第三方发送的,则客户端在建立推送订阅时必须指定加密密钥,并忽略接收到的未使用这些密钥加密的推送通知。

The privacy and security considerations of [RFC8030] and [RFC8291] also apply to the use of the PushSubscription mechanism.

[RFC8030]和[RFC8291]的隐私和安全注意事项也适用于PushSubscription机制的使用。

As there is no crypto algorithm agility in Web Push Encryption [RFC8291], a new specification will be needed to provide this if new algorithms are required in the future.

由于Web推送加密[RFC8291]中没有加密算法的灵活性,因此如果将来需要新的算法,则需要一个新的规范来提供这一点。

8.8. Traffic Analysis
8.8. 流量分析

While the data is encrypted, a passive observer with the ability to monitor network traffic may be able to glean information from the timing of API requests and push notifications. For example, suppose an email or calendar invitation is sent from User A (hosted on Server X) to User B (hosted on Server Y). If Server X hosts data for many users, a passive observer can see that the two servers connected but does not know who the data was for. However, if a push notification is immediately sent to User B and the attacker can observe this as well, they may reasonably conclude that someone on Server X is connecting to User B.

当数据被加密时,能够监控网络流量的被动观察者可以从API请求和推送通知的定时中收集信息。例如,假设从用户A(托管在服务器X上)向用户B(托管在服务器Y上)发送电子邮件或日历邀请。如果ServerX为许多用户托管数据,则被动观察者可以看到两台服务器已连接,但不知道数据是为谁提供的。但是,如果推送通知立即发送给用户B,并且攻击者也可以观察到这一点,则他们可以合理地推断服务器X上有人正在连接用户B。

9. IANA Considerations
9. IANA考虑
9.1. Assignment of jmap Service Name
9.1. jmap服务名称的分配

IANA has assigned the 'jmap' service name in the "Service Name and Transport Protocol Port Number Registry" [RFC6335].

IANA已在“服务名称和传输协议端口号注册表”[RFC6335]中分配了“jmap”服务名称。

Service Name: jmap

服务名称:jmap

Transport Protocol(s): tcp

传输协议:tcp

Assignee: IESG

受让人:IESG

Contact: IETF Chair

联系人:IETF主席

Description: JSON Meta Application Protocol

描述:JSON元应用程序协议

Reference: RFC 8620

参考:RFC 8620

Assignment Notes: This service name was previously assigned under the name "JSON Mail Access Protocol". This has been de-assigned and re-assigned with the approval of the previous assignee.

分配说明:此服务名称以前是以“JSON邮件访问协议”的名称分配的。经前一受让人批准,已取消和重新转让。

9.2. Registration of Well-Known URI Suffix for JMAP
9.2. 为JMAP注册众所周知的URI后缀

IANA has registered the following suffix in the "Well-Known URIs" registry for JMAP, as described in [RFC8615]:

IANA已在JMAP的“众所周知的URI”注册表中注册了以下后缀,如[RFC8615]所述:

URI Suffix: jmap

URI后缀:jmap

Change Controller: IETF

更改控制器:IETF

Specification Document: RFC 8620, Section 2.2.

规范文件:RFC 8620,第2.2节。

9.3. Registration of the jmap URN Sub-namespace
9.3. 注册jmap URN子命名空间

IANA has registered the following URN sub-namespace in the "IETF URN Sub-namespace for Registered Protocol Parameter Identifiers" registry within the "Uniform Resource Name (URN) Namespace for IETF Use" registry as described in [RFC3553].

IANA已在“IETF使用的统一资源名称(URN)命名空间”注册表中的“注册协议参数标识符的IETF URN子命名空间”注册表中注册了以下URN子命名空间,如[RFC3553]所述。

Registered Parameter Identifier: jmap

注册参数标识符:jmap

Reference: RFC 8620, Section 9.4

参考:RFC 8620,第9.4节

   IANA Registry Reference: http://www.iana.org/assignments/jmap
        
   IANA Registry Reference: http://www.iana.org/assignments/jmap
        
9.4. Creation of "JMAP Capabilities" Registry
9.4. 创建“JMAP功能”注册表

IANA has created the "JMAP Capabilities" registry as described in Section 2. JMAP capabilities are advertised in the "capabilities" property of the JMAP Session resource. They are used to extend the functionality of a JMAP server. A capability is referenced by a URI. The JMAP capability URI can be a URN starting with "urn:ietf:params:jmap:" plus a unique suffix that is the index value in the jmap URN sub-namespace. Registration of a JMAP capability with another form of URI has no impact on the jmap URN sub-namespace.

IANA已经创建了“JMAP能力”注册表,如第2节所述。JMAP功能在JMAP会话资源的“capabilities”属性中公布。它们用于扩展JMAP服务器的功能。功能由URI引用。JMAP功能URI可以是一个以“URN:ietf:params:JMAP:”开头的URN,加上一个唯一的后缀,该后缀是JMAP URN子命名空间中的索引值。用另一种形式的URI注册JMAP功能对JMAP URN子命名空间没有影响。

This registry follows the expert review process unless the "intended use" field is "common" or "placeholder", in which case registration follows the specification required process.

除非“预期用途”字段为“通用”或“占位符”,否则该登记遵循专家审查流程,在这种情况下,登记遵循规范要求的流程。

A JMAP capability registration can have an intended use of "common", "placeholder", "limited", or "obsolete". IANA will list common-use registrations prominently and separately from those with other intended use values.

JMAP功能注册可以有“通用”、“占位符”、“有限”或“过时”的预期用途。IANA将突出列出常见用途登记,并将其与具有其他预期用途价值的登记分开。

The JMAP capability registration procedure is not a formal standards process but rather an administrative procedure intended to allow community comment and sanity checking without excessive time delay.

JMAP能力注册程序不是一个正式的标准程序,而是一个行政程序,旨在允许社区评论和健全性检查,而不存在过度的时间延迟。

A "placeholder" registration reserves part of the jmap URN namespace for another purpose but is typically not included in the "capabilities" property of the JMAP Session resource.

“占位符”注册保留jmap URN命名空间的一部分用于其他目的,但通常不包括在jmap会话资源的“功能”属性中。

9.4.1. Preliminary Community Review
9.4.1. 社区初步审查

Notice of a potential JMAP common-use registration SHOULD be sent to the JMAP mailing list <jmap@ietf.org> for review. This mailing list is appropriate to solicit community feedback on a proposed JMAP

潜在JMAP通用注册通知应发送至JMAP邮件列表<jmap@ietf.org>供审查。此邮件列表适用于征求社区对拟议JMAP的反馈

capability. Registrations that are not intended for common use MAY be sent to the list for review as well; doing so is entirely OPTIONAL, but is encouraged.

能力。非通用注册也可发送至列表进行审查;这样做是完全可以选择的,但受到鼓励。

The intent of the public posting to this list is to solicit comments and feedback on the choice of the capability name, the unambiguity of the specification document, and a review of any interoperability or security considerations. The submitter may submit a revised registration proposal or abandon the registration completely at any time.

公开发布到此列表的目的是征求关于能力名称的选择、规范文档的明确性以及对任何互操作性或安全考虑的审查的意见和反馈。提交人可随时提交修改后的注册建议书或完全放弃注册。

9.4.2. Submit Request to IANA
9.4.2. 向IANA提交申请

Registration requests can be sent to <iana@iana.org>.

注册申请可发送至<iana@iana.org>.

9.4.3. Designated Expert Review
9.4.3. 指定专家审评

For a limited-use registration, the primary concern of the designated expert (DE) is preventing name collisions and encouraging the submitter to document security and privacy considerations; a published specification is not required. For a common-use registration, the DE is expected to confirm that suitable documentation, as described in Section 4.6 of [RFC8126], is available. The DE should also verify that the capability does not conflict with work that is active or already published within the IETF.

对于有限使用注册,指定专家(DE)的主要关注点是防止名称冲突,并鼓励提交人记录安全和隐私注意事项;不需要发布的规范。对于通用注册,DE应确认[RFC8126]第4.6节所述的适当文件可用。DE还应验证该能力是否与IETF中正在进行或已经发布的工作相冲突。

Before a period of 30 days has passed, the DE will either approve or deny the registration request and publish a notice of the decision to the JMAP WG mailing list or its successor, as well as inform IANA. A denial notice must be justified by an explanation, and, in the cases where it is possible, concrete suggestions on how the request can be modified so as to become acceptable should be provided.

在30天内,DE将批准或拒绝注册请求,并向JMAP工作组邮件列表或其继任者发布决定通知,并通知IANA。拒绝通知必须以解释为理由,在可能的情况下,应就如何修改请求以使其成为可接受的提出具体建议。

If the DE does not respond within 30 days, the registrant may request the IESG take action to process the request in a timely manner.

如果DE在30天内未作出回应,注册人可要求IESG采取行动及时处理该请求。

9.4.4. Change Procedures
9.4.4. 变更程序

Once a JMAP capability has been published by the IANA, the change controller may request a change to its definition. The same procedure that would be appropriate for the original registration request is used to process a change request.

一旦IANA发布了JMAP功能,变更控制器可能会请求对其定义进行变更。适用于原始注册申请的相同程序用于处理变更申请。

JMAP capability registrations may not be deleted; capabilities that are no longer believed appropriate for use can be declared obsolete by a change to their "intended use" field; such capabilities will be clearly marked in the lists published by the IANA.

不得删除JMAP能力注册;不再被认为适合使用的能力可以通过更改其“预期用途”字段宣布为过时;IANA发布的清单中将明确标明此类能力。

Significant changes to a capability's definition should be requested only when there are serious omissions or errors in the published specification. When review is required, a change request may be denied if it renders entities that were valid under the previous definition invalid under the new definition.

只有在发布的规范中存在严重遗漏或错误时,才应要求对能力定义进行重大更改。当需要审查时,如果变更请求使根据先前定义有效的实体在新定义下无效,则变更请求可能会被拒绝。

The owner of a JMAP capability may pass responsibility to another person or agency by informing the IANA; this can be done without discussion or review.

JMAP能力的所有者可以通过通知IANA将责任转移给另一个人或机构;这可以在不进行讨论或审查的情况下完成。

The IESG may reassign responsibility for a JMAP capability. The most common case of this will be to enable changes to be made to capabilities where the author of the registration has died, moved out of contact, or is otherwise unable to make changes that are important to the community.

IESG可以重新分配JMAP能力的责任。最常见的情况是,当注册作者去世、失去联系或无法进行对社区重要的更改时,可以对功能进行更改。

9.4.5. JMAP Capabilities Registry Template
9.4.5. JMAP功能注册表模板

Capability name: (see capability property in Section 2)

能力名称:(参见第2节中的能力属性)

Specification document:

规范文件:

Intended use: (one of common, limited, placeholder, or obsolete)

预期用途:(通用、有限、占位符或过时)

Change controller: ("IETF" for Standards Track / BCP RFCs)

变更控制员:(“IETF”用于标准跟踪/BCP RFC)

Security and privacy considerations:

安全和隐私注意事项:

9.4.6. Initial Registration for JMAP Core
9.4.6. JMAP核心的初始注册
   Capability Name: "urn:ietf:params:jmap:core"
        
   Capability Name: "urn:ietf:params:jmap:core"
        

Specification document: RFC 8620, Section 2

规范文件:RFC 8620,第2节

Intended use: common

预期用途:普通

Change Controller: IETF

更改控制器:IETF

Security and privacy considerations: RFC 8620, Section 8.

安全和隐私注意事项:RFC 8620,第8节。

9.4.7. Registration for JMAP Error Placeholder in JMAP Capabilities Registry

9.4.7. 在JMAP功能注册表中注册JMAP错误占位符

   Capability Name: "urn:ietf:params:jmap:error:"
        
   Capability Name: "urn:ietf:params:jmap:error:"
        

Specification document: RFC 8620, Section 9.5

规范文件:RFC 8620,第9.5节

Intended use: placeholder

预期用途:占位符

Change Controller: IETF

更改控制器:IETF

Security and privacy considerations: RFC 8620, Section 8.

安全和隐私注意事项:RFC 8620,第8节。

9.5. Creation of "JMAP Error Codes" Registry
9.5. 创建“JMAP错误代码”注册表

IANA has created the "JMAP Error Codes" registry. JMAP error codes appear in the "type" member of a JSON problem details object (as described in Section 3.6.1), the "type" member in a JMAP error object (as described in Section 3.6.2), or the "type" member of a JMAP method-specific error object (such as SetError in Section 5.3). When used in a problem details object, the prefix "urn:ietf:params:jmap:error:" is always included; when used in JMAP objects, the prefix is always omitted.

IANA已经创建了“JMAP错误代码”注册表。JMAP错误代码出现在JSON问题详细信息对象的“类型”成员(如第3.6.1节所述)、JMAP错误对象的“类型”成员(如第3.6.2节所述)或JMAP方法特定错误对象的“类型”成员(如第5.3节中的SetError)中。在问题详细信息对象中使用时,始终包含前缀“urn:ietf:params:jmap:error:”;在JMAP对象中使用时,前缀总是被省略。

This registry follows the expert review process. Preliminary community review for this registry follows the same procedures as the "JMAP Capabilities" registry, but it is optional. The change procedures for this registry are the same as the change procedures for the "JMAP Capabilities" registry.

该登记册遵循专家审查程序。此注册表的初步社区审查遵循与“JMAP功能”注册表相同的过程,但它是可选的。此注册表的更改过程与“JMAP功能”注册表的更改过程相同。

9.5.1. Expert Review
9.5.1. 专家审评

The designated expert should review the following aspects of the registration:

指定专家应审查登记的以下方面:

1. Verify the error code does not conflict with existing names.

1. 验证错误代码与现有名称不冲突。

2. Verify the error code follows the syntax limitations (does not require URI encoding).

2. 验证错误代码是否符合语法限制(不需要URI编码)。

3. Encourage the submitter to follow the naming convention of previously registered errors.

3. 鼓励提交者遵循先前注册错误的命名约定。

4. Encourage the submitter to describe client behaviours that are recommended in response to the error code. These may distinguish the error code from other error codes.

4. 鼓励提交人描述针对错误代码建议的客户行为。这些可能会将错误代码与其他错误代码区分开来。

5. Encourage the submitter to describe when the server should issue the error as opposed to some other error code.

5. 鼓励提交者描述服务器何时应该发出错误,而不是其他错误代码。

6. Encourage the submitter to note any security considerations associated with the error, if any (e.g., an error code that might disclose existence of data the authenticated user does not have permission to know about).

6. 鼓励提交人注意与错误相关的任何安全注意事项(如果有)(例如,可能会泄露已验证用户无权知道的数据存在的错误代码)。

Steps 3-6 are meant to promote a higher-quality registry. However, the expert is encouraged to approve any registration that would not actively harm JMAP interoperability to make this a relatively lightweight process.

步骤3-6旨在促进更高质量的注册。但是,鼓励专家批准任何不会严重损害JMAP互操作性的注册,使其成为一个相对轻量级的过程。

9.5.2. JMAP Error Codes Registry Template
9.5.2. JMAP错误代码注册表模板

JMAP Error Code:

JMAP错误代码:

Intended use: (one of "common", "limited", "obsolete")

预期用途:(通用、有限、过时)

Change Controller: ("IETF" for Standards Track / BCP RFCs)

变更控制员:(“IETF”用于标准跟踪/BCP RFC)

Reference: (Optional. Only required if defined in an RFC.)

参考:(可选。只有在RFC中定义时才需要。)

Description:

说明:

9.5.3. Initial Contents for the JMAP Error Codes Registry
9.5.3. JMAP错误代码注册表的初始内容

o JMAP Error Code: accountNotFound Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: The accountId does not correspond to a valid account.

o JMAP错误代码:accountNotFound预期用途:通用更改控制器:IETF参考:RFC 8620,第3.6.2节说明:accountId与有效帐户不对应。

o JMAP Error Code: accountNotSupportedByMethod Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: The accountId given corresponds to a valid account, but the account does not support this method or data type.

o JMAP错误代码:accountNotSupportedByMethod预期用途:通用更改控制器:IETF参考:RFC 8620,第3.6.2节说明:给定的accountId对应于有效帐户,但该帐户不支持此方法或数据类型。

o JMAP Error Code: accountReadOnly Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: This method modifies state, but the account is read-only (as returned on the corresponding Account object in the JMAP Session resource).

o JMAP错误代码:accountReadOnly预期用途:通用更改控制器:IETF参考:RFC 8620,第3.6.2节描述:此方法修改状态,但帐户是只读的(在JMAP会话资源中的相应帐户对象上返回)。

o JMAP Error Code: anchorNotFound Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.5 Description: An anchor argument was supplied, but it cannot be found in the results of the query.

o JMAP错误代码:anchorNotFound预期用途:通用变更控制器:IETF参考:RFC 8620,第5.5节说明:提供了锚参数,但在查询结果中找不到。

o JMAP Error Code: alreadyExists Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.4 Description: The server forbids duplicates, and the record already exists in the target account. An existingId property of type Id MUST be included on the SetError object with the id of the existing record.

o JMAP错误代码:alreadyExists预期用途:通用更改控制器:IETF参考:RFC 8620,第5.4节说明:服务器禁止重复,并且记录已存在于目标帐户中。类型为Id的existingId属性必须包含在具有现有记录Id的SetError对象上。

o JMAP Error Code: cannotCalculateChanges Intended Use: Common Change Controller: IETF Reference: RFC 8620, Sections 5.2 and 5.6 Description: The server cannot calculate the changes from the state string given by the client.

o JMAP错误代码:cannotCalculateChanges预期用途:通用更改控制器:IETF参考:RFC 8620,第5.2和5.6节说明:服务器无法根据客户端提供的状态字符串计算更改。

o JMAP Error Code: forbidden Intended Use: Common Change Controller: IETF Reference: RFC 8620, Sections 3.6.2, 5.3, and 7.2.1 Description: The action would violate an ACL or other permissions policy.

o JMAP错误代码:禁止的预期用途:通用更改控制器:IETF参考:RFC 8620,第3.6.2、5.3和7.2.1节说明:该操作将违反ACL或其他权限策略。

o JMAP Error Code: fromAccountNotFound Intended Use: Common Change Controller: IETF Reference: RFC 8620, Sections 5.4 and 6.3 Description: The fromAccountId does not correspond to a valid account.

o JMAP错误代码:fromAccountNotFound预期用途:通用更改控制器:IETF参考:RFC 8620,第5.4和6.3节说明:fromAccountId与有效帐户不对应。

o JMAP Error Code: fromAccountNotSupportedByMethod Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.4 Description: The fromAccountId given corresponds to a valid account, but the account does not support this data type.

o JMAP错误代码:fromAccountNotSupportedByMethod预期用途:通用更改控制器:IETF参考:RFC 8620,第5.4节说明:给定的fromAccountId对应于有效帐户,但该帐户不支持此数据类型。

o JMAP Error Code: invalidArguments Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: One of the arguments is of the wrong type or otherwise invalid, or a required argument is missing.

o JMAP错误代码:invalidArguments预期用途:通用变更控制器:IETF参考:RFC 8620,第3.6.2节说明:其中一个参数类型错误或无效,或者缺少必需的参数。

o JMAP Error Code: invalidPatch Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: The PatchObject given to update the record was not a valid patch.

o JMAP错误代码:invalidPatch预期用途:通用更改控制器:IETF参考:RFC 8620,第5.3节说明:用于更新记录的PatchObject不是有效的补丁。

o JMAP Error Code: invalidProperties Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: The record given is invalid.

o JMAP错误代码:invalidProperties预期用途:通用变更控制器:IETF参考:RFC 8620,第5.3节说明:给出的记录无效。

o JMAP Error Code: notFound Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: The id given cannot be found.

o JMAP错误代码:未找到预期用途:通用变更控制器:IETF参考:RFC 8620,第5.3节说明:找不到给定的id。

o JMAP Error Code: notJSON Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.1 Description: The content type of the request was not application/ json, or the request did not parse as I-JSON.

o JMAP错误代码:notJSON预期用途:公共变更控制器:IETF参考:RFC 8620,第3.6.1节描述:请求的内容类型不是application/json,或者请求没有解析为I-json。

o JMAP Error Code: notRequest Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.1 Description: The request parsed as JSON but did not match the type signature of the Request object.

o JMAP错误代码:notRequest预期用途:公共变更控制器:IETF参考:RFC 8620,第3.6.1节描述:请求解析为JSON,但与请求对象的类型签名不匹配。

o JMAP Error Code: overQuota Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: The create would exceed a server-defined limit on the number or total size of objects of this type.

o JMAP错误代码:超额预期用途:通用更改控制器:IETF参考:RFC 8620,第5.3节说明:创建将超过服务器定义的对此类型对象的数量或总大小的限制。

o JMAP Error Code: rateLimit Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: Too many objects of this type have been created recently, and a server-defined rate limit has been reached. It may work if tried again later.

o JMAP错误代码:rateLimit预期用途:通用更改控制器:IETF参考:RFC 8620,第5.3节说明:最近创建了太多此类对象,并且达到了服务器定义的速率限制。如果以后再试,它可能会起作用。

o JMAP Error Code: requestTooLarge Intended Use: Common Change Controller: IETF Reference: RFC 8620, Sections 5.1 and 5.3 Description: The total number of actions exceeds the maximum number the server is willing to process in a single method call.

o JMAP错误代码:requestTooLarge预期用途:通用更改控制器:IETF参考:RFC 8620,第5.1和5.3节说明:操作总数超过了服务器在单个方法调用中愿意处理的最大数量。

o JMAP Error Code: invalidResultReference Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: The method used a result reference for one of its arguments, but this failed to resolve.

o JMAP错误代码:invalidResultReference预期用途:通用更改控制器:IETF参考:RFC 8620,第3.6.2节说明:该方法对其参数之一使用了结果参考,但未能解决此问题。

o JMAP Error Code: serverFail Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: An unexpected or unknown error occurred during the processing of the call. The method call made no changes to the server's state.

o JMAP错误代码:serverFail预期用途:通用变更控制器:IETF参考:RFC 8620,第3.6.2节描述:在呼叫处理过程中发生意外或未知错误。方法调用没有更改服务器的状态。

o JMAP Error Code: serverPartialFail Intended Use: Limited Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: Some, but not all, expected changes described by the method occurred. The client MUST resynchronise impacted data to determine the server state. Use of this error is strongly discouraged.

o JMAP错误代码:serverPartialFail预期用途:有限更改控制器:IETF参考:RFC 8620,第3.6.2节描述:发生了方法描述的部分(但不是全部)预期更改。客户端必须重新同步受影响的数据以确定服务器状态。强烈反对使用此错误。

o JMAP Error Code: serverUnavailable Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: Some internal server resource was temporarily unavailable. Attempting the same operation later (perhaps after a backoff with a random factor) may succeed.

o JMAP错误代码:服务器不可用预期用途:通用更改控制器:IETF参考:RFC 8620,第3.6.2节说明:某些内部服务器资源暂时不可用。稍后尝试相同的操作(可能在使用随机因素进行退避之后)可能会成功。

o JMAP Error Code: singleton Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: This is a singleton type, so you cannot create another one or destroy the existing one.

o JMAP错误代码:singleton预期用途:通用更改控制器:IETF参考:RFC 8620,第5.3节说明:这是一种singleton类型,因此您无法创建另一种类型或销毁现有类型。

o JMAP Error Code: stateMismatch Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: An ifInState argument was supplied, and it does not match the current state.

o JMAP错误代码:状态不匹配预期用途:通用更改控制器:IETF参考:RFC 8620,第5.3节说明:提供了ifInState参数,但该参数与当前状态不匹配。

o JMAP Error Code: tooLarge Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: The action would result in an object that exceeds a server-defined limit for the maximum size of a single object of this type.

o JMAP错误代码:tooLarge预期用途:通用更改控制器:IETF参考:RFC 8620,第5.3节说明:该操作将导致对象超出服务器定义的该类型单个对象最大大小限制。

o JMAP Error Code: tooManyChanges Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.6 Description: There are more changes than the client's maxChanges argument.

o JMAP错误代码:tooManyChanges预期用途:通用更改控制器:IETF参考:RFC 8620,第5.6节说明:有比客户端的maxChanges参数更多的更改。

o JMAP Error Code: unknownCapability Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.1 Description: The client included a capability in the "using" property of the request that the server does not support.

o JMAP错误代码:未知能力预期用途:通用更改控制器:IETF参考:RFC 8620,第3.6.1节说明:客户端在请求的“使用”属性中包含了服务器不支持的功能。

o JMAP Error Code: unknownMethod Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 3.6.2 Description: The server does not recognise this method name.

o JMAP错误代码:Unknown方法预期用途:通用更改控制器:IETF参考:RFC 8620,第3.6.2节说明:服务器无法识别此方法名称。

o JMAP Error Code: unsupportedFilter Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.5 Description: The filter is syntactically valid, but the server cannot process it.

o JMAP错误代码:不支持筛选器预期用途:通用更改控制器:IETF参考:RFC 8620,第5.5节说明:筛选器在语法上有效,但服务器无法处理它。

o JMAP Error Code: unsupportedSort Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.5 Description: The sort is syntactically valid but includes a property the server does not support sorting on or a collation method it does not recognise.

o JMAP错误代码:不支持排序预期用途:通用更改控制器:IETF参考:RFC 8620,第5.5节说明:排序在语法上有效,但包含服务器不支持排序的属性或它无法识别的排序方法。

o JMAP Error Code: willDestroy Intended Use: Common Change Controller: IETF Reference: RFC 8620, Section 5.3 Description: The client requested an object be both updated and destroyed in the same /set request, and the server has decided to therefore ignore the update.

o JMAP错误代码:willDestroy预期用途:通用更改控制器:IETF参考:RFC 8620,第5.3节描述:客户端请求在同一个/set请求中更新和销毁对象,因此服务器决定忽略更新。

10. References
10. 工具书类
10.1. Normative References
10.1. 规范性引用文件

[EventSource] Hickson, I., "Server-Sent Events", World Wide Web Consortium Recommendation REC-eventsource-20150203, February 2015, <https://www.w3.org/TR/eventsource/>.

[EventSource]Hickson,I.,“服务器发送的事件”,万维网联盟建议REC-EventSource-20150203,2015年2月<https://www.w3.org/TR/eventsource/>.

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC2119]Bradner,S.,“RFC中用于表示需求水平的关键词”,BCP 14,RFC 2119,DOI 10.17487/RFC2119,1997年3月<https://www.rfc-editor.org/info/rfc2119>.

[RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for specifying the location of services (DNS SRV)", RFC 2782, DOI 10.17487/RFC2782, February 2000, <https://www.rfc-editor.org/info/rfc2782>.

[RFC2782]Gulbrandsen,A.,Vixie,P.和L.Esibov,“用于指定服务位置(DNS SRV)的DNS RR”,RFC 2782,DOI 10.17487/RFC2782,2000年2月<https://www.rfc-editor.org/info/rfc2782>.

[RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, DOI 10.17487/RFC2818, May 2000, <https://www.rfc-editor.org/info/rfc2818>.

[RFC2818]Rescorla,E.,“TLS上的HTTP”,RFC 2818,DOI 10.17487/RFC2818,2000年5月<https://www.rfc-editor.org/info/rfc2818>.

[RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, <https://www.rfc-editor.org/info/rfc3339>.

[RFC3339]Klyne,G.和C.Newman,“互联网上的日期和时间:时间戳”,RFC 3339,DOI 10.17487/RFC3339,2002年7月<https://www.rfc-editor.org/info/rfc3339>.

[RFC3553] Mealling, M., Masinter, L., Hardie, T., and G. Klyne, "An IETF URN Sub-namespace for Registered Protocol Parameters", BCP 73, RFC 3553, DOI 10.17487/RFC3553, June 2003, <https://www.rfc-editor.org/info/rfc3553>.

[RFC3553]Mealling,M.,Masinter,L.,Hardie,T.,和G.Klyne,“注册协议参数的IETF URN子命名空间”,BCP 73,RFC 3553,DOI 10.17487/RFC3553,2003年6月<https://www.rfc-editor.org/info/rfc3553>.

[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November 2003, <https://www.rfc-editor.org/info/rfc3629>.

[RFC3629]Yergeau,F.,“UTF-8,ISO 10646的转换格式”,STD 63,RFC 3629,DOI 10.17487/RFC3629,2003年11月<https://www.rfc-editor.org/info/rfc3629>.

[RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, <https://www.rfc-editor.org/info/rfc4648>.

[RFC4648]Josefsson,S.,“Base16、Base32和Base64数据编码”,RFC 4648,DOI 10.17487/RFC4648,2006年10月<https://www.rfc-editor.org/info/rfc4648>.

[RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, "Internet Application Protocol Collation Registry", RFC 4790, DOI 10.17487/RFC4790, March 2007, <https://www.rfc-editor.org/info/rfc4790>.

[RFC4790]Newman,C.,Duerst,M.和A.Gulbrandsen,“互联网应用程序协议整理注册表”,RFC 4790,DOI 10.17487/RFC4790,2007年3月<https://www.rfc-editor.org/info/rfc4790>.

[RFC5051] Crispin, M., "i;unicode-casemap - Simple Unicode Collation Algorithm", RFC 5051, DOI 10.17487/RFC5051, October 2007, <https://www.rfc-editor.org/info/rfc5051>.

[RFC5051]Crispin,M.,“i;unicode案例图-简单unicode排序算法”,RFC 5051,DOI 10.17487/RFC5051,2007年10月<https://www.rfc-editor.org/info/rfc5051>.

[RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security (TLS) Protocol Version 1.2", RFC 5246, DOI 10.17487/RFC5246, August 2008, <https://www.rfc-editor.org/info/rfc5246>.

[RFC5246]Dierks,T.和E.Rescorla,“传输层安全(TLS)协议版本1.2”,RFC 5246,DOI 10.17487/RFC5246,2008年8月<https://www.rfc-editor.org/info/rfc5246>.

[RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and W. Polk, "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008, <https://www.rfc-editor.org/info/rfc5280>.

[RFC5280]Cooper,D.,Santesson,S.,Farrell,S.,Boeyen,S.,Housley,R.,和W.Polk,“Internet X.509公钥基础设施证书和证书撤销列表(CRL)配置文件”,RFC 5280,DOI 10.17487/RFC5280,2008年5月<https://www.rfc-editor.org/info/rfc5280>.

[RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322, DOI 10.17487/RFC5322, October 2008, <https://www.rfc-editor.org/info/rfc5322>.

[RFC5322]Resnick,P.,Ed.,“互联网信息格式”,RFC 5322,DOI 10.17487/RFC5322,2008年10月<https://www.rfc-editor.org/info/rfc5322>.

[RFC6186] Daboo, C., "Use of SRV Records for Locating Email Submission/Access Services", RFC 6186, DOI 10.17487/RFC6186, March 2011, <https://www.rfc-editor.org/info/rfc6186>.

[RFC6186]Daboo,C.“使用SRV记录查找电子邮件提交/访问服务”,RFC 6186,DOI 10.17487/RFC6186,2011年3月<https://www.rfc-editor.org/info/rfc6186>.

[RFC6335] Cotton, M., Eggert, L., Touch, J., Westerlund, M., and S. Cheshire, "Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry", BCP 165, RFC 6335, DOI 10.17487/RFC6335, August 2011, <https://www.rfc-editor.org/info/rfc6335>.

[RFC6335]Cotton,M.,Eggert,L.,Touch,J.,Westerlund,M.,和S.Cheshire,“互联网分配号码管理局(IANA)服务名称和传输协议端口号注册管理程序”,BCP 165,RFC 6335,DOI 10.17487/RFC6335,2011年8月<https://www.rfc-editor.org/info/rfc6335>.

[RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., and D. Orchard, "URI Template", RFC 6570, DOI 10.17487/RFC6570, March 2012, <https://www.rfc-editor.org/info/rfc6570>.

[RFC6570]Gregorio,J.,Fielding,R.,Hadley,M.,Nottingham,M.,和D.Orchard,“URI模板”,RFC 6570,DOI 10.17487/RFC657012年3月<https://www.rfc-editor.org/info/rfc6570>.

[RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework", RFC 6749, DOI 10.17487/RFC6749, October 2012, <https://www.rfc-editor.org/info/rfc6749>.

[RFC6749]Hardt,D.,Ed.“OAuth 2.0授权框架”,RFC 6749,DOI 10.17487/RFC6749,2012年10月<https://www.rfc-editor.org/info/rfc6749>.

[RFC6764] Daboo, C., "Locating Services for Calendaring Extensions to WebDAV (CalDAV) and vCard Extensions to WebDAV (CardDAV)", RFC 6764, DOI 10.17487/RFC6764, February 2013, <https://www.rfc-editor.org/info/rfc6764>.

[RFC6764]Daboo,C.“查找WebDAV日历扩展(CalDAV)和WebDAV vCard扩展(CardDAV)的服务”,RFC 6764,DOI 10.17487/RFC6764,2013年2月<https://www.rfc-editor.org/info/rfc6764>.

[RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type Specifications and Registration Procedures", BCP 13, RFC 6838, DOI 10.17487/RFC6838, January 2013, <https://www.rfc-editor.org/info/rfc6838>.

[RFC6838]Freed,N.,Klensin,J.和T.Hansen,“介质类型规范和注册程序”,BCP 13,RFC 6838,DOI 10.17487/RFC6838,2013年1月<https://www.rfc-editor.org/info/rfc6838>.

[RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed., "JavaScript Object Notation (JSON) Pointer", RFC 6901, DOI 10.17487/RFC6901, April 2013, <https://www.rfc-editor.org/info/rfc6901>.

[RFC6901]Bryan,P.,Ed.,Zyp,K.,和M.Nottingham,Ed.,“JavaScript对象表示法(JSON)指针”,RFC 6901,DOI 10.17487/RFC69011913年4月<https://www.rfc-editor.org/info/rfc6901>.

[RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing", RFC 7230, DOI 10.17487/RFC7230, June 2014, <https://www.rfc-editor.org/info/rfc7230>.

[RFC7230]Fielding,R.,Ed.和J.Reschke,Ed.,“超文本传输协议(HTTP/1.1):消息语法和路由”,RFC 7230,DOI 10.17487/RFC7230,2014年6月<https://www.rfc-editor.org/info/rfc7230>.

[RFC7231] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content", RFC 7231, DOI 10.17487/RFC7231, June 2014, <https://www.rfc-editor.org/info/rfc7231>.

[RFC7231]Fielding,R.,Ed.和J.Reschke,Ed.,“超文本传输协议(HTTP/1.1):语义和内容”,RFC 7231,DOI 10.17487/RFC72312014年6月<https://www.rfc-editor.org/info/rfc7231>.

[RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, DOI 10.17487/RFC7493, March 2015, <https://www.rfc-editor.org/info/rfc7493>.

[RFC7493]Bray,T.,Ed.,“I-JSON消息格式”,RFC 7493,DOI 10.17487/RFC7493,2015年3月<https://www.rfc-editor.org/info/rfc7493>.

[RFC7525] Sheffer, Y., Holz, R., and P. Saint-Andre, "Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)", BCP 195, RFC 7525, DOI 10.17487/RFC7525, May 2015, <https://www.rfc-editor.org/info/rfc7525>.

[RFC7525]Sheffer,Y.,Holz,R.,和P.Saint Andre,“安全使用传输层安全性(TLS)和数据报传输层安全性(DTLS)的建议”,BCP 195,RFC 7525,DOI 10.17487/RFC7525,2015年5月<https://www.rfc-editor.org/info/rfc7525>.

[RFC7617] Reschke, J., "The 'Basic' HTTP Authentication Scheme", RFC 7617, DOI 10.17487/RFC7617, September 2015, <https://www.rfc-editor.org/info/rfc7617>.

[RFC7617]Reschke,J.“基本”HTTP认证方案”,RFC 7617,DOI 10.17487/RFC76172015年9月<https://www.rfc-editor.org/info/rfc7617>.

[RFC7807] Nottingham, M. and E. Wilde, "Problem Details for HTTP APIs", RFC 7807, DOI 10.17487/RFC7807, March 2016, <https://www.rfc-editor.org/info/rfc7807>.

[RFC7807]诺丁汉,M.和E.王尔德,“HTTP API的问题细节”,RFC 7807,DOI 10.17487/RFC7807,2016年3月<https://www.rfc-editor.org/info/rfc7807>.

[RFC8030] Thomson, M., Damaggio, E., and B. Raymor, Ed., "Generic Event Delivery Using HTTP Push", RFC 8030, DOI 10.17487/RFC8030, December 2016, <https://www.rfc-editor.org/info/rfc8030>.

[RFC8030]Thomson,M.,Damaggio,E.,和B.Raymor,Ed.,“使用HTTP推送的通用事件交付”,RFC 8030,DOI 10.17487/RFC80302016年12月<https://www.rfc-editor.org/info/rfc8030>.

[RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, DOI 10.17487/RFC8126, June 2017, <https://www.rfc-editor.org/info/rfc8126>.

[RFC8126]Cotton,M.,Leiba,B.,和T.Narten,“在RFC中编写IANA考虑事项部分的指南”,BCP 26,RFC 8126,DOI 10.17487/RFC8126,2017年6月<https://www.rfc-editor.org/info/rfc8126>.

[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8174]Leiba,B.,“RFC 2119关键词中大写与小写的歧义”,BCP 14,RFC 8174,DOI 10.17487/RFC8174,2017年5月<https://www.rfc-editor.org/info/rfc8174>.

[RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10.17487/RFC8259, December 2017, <https://www.rfc-editor.org/info/rfc8259>.

[RFC8259]Bray,T.,Ed.“JavaScript对象表示法(JSON)数据交换格式”,STD 90,RFC 8259,DOI 10.17487/RFC8259,2017年12月<https://www.rfc-editor.org/info/rfc8259>.

[RFC8264] Saint-Andre, P. and M. Blanchet, "PRECIS Framework: Preparation, Enforcement, and Comparison of Internationalized Strings in Application Protocols", RFC 8264, DOI 10.17487/RFC8264, October 2017, <https://www.rfc-editor.org/info/rfc8264>.

[RFC8264]Saint Andre,P.和M.Blanchet,“PRECIS框架:应用协议中国际化字符串的准备、实施和比较”,RFC 8264,DOI 10.17487/RFC8264,2017年10月<https://www.rfc-editor.org/info/rfc8264>.

[RFC8291] Thomson, M., "Message Encryption for Web Push", RFC 8291, DOI 10.17487/RFC8291, November 2017, <https://www.rfc-editor.org/info/rfc8291>.

[RFC8291]Thomson,M.,“Web推送的消息加密”,RFC 8291,DOI 10.17487/RFC8291,2017年11月<https://www.rfc-editor.org/info/rfc8291>.

[RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018, <https://www.rfc-editor.org/info/rfc8446>.

[RFC8446]Rescorla,E.“传输层安全(TLS)协议版本1.3”,RFC 8446,DOI 10.17487/RFC8446,2018年8月<https://www.rfc-editor.org/info/rfc8446>.

[RFC8615] Nottingham, M., "Well-Known Uniform Resource Identifiers (URIs)", RFC 8615, DOI 10.17487/RFC8615, May 2019, <https://www.rfc-editor.org/info/rfc8615>.

[RFC8615]诺丁汉,M.,“众所周知的统一资源标识符(URI)”,RFC 8615,DOI 10.17487/RFC8615,2019年5月<https://www.rfc-editor.org/info/rfc8615>.

10.2. Informative References
10.2. 资料性引用

[RFC8246] McManus, P., "HTTP Immutable Responses", RFC 8246, DOI 10.17487/RFC8246, September 2017, <https://www.rfc-editor.org/info/rfc8246>.

[RFC8246]McManus,P.,“HTTP不可变响应”,RFC 8246,DOI 10.17487/RFC8246,2017年9月<https://www.rfc-editor.org/info/rfc8246>.

Authors' Addresses

作者地址

Neil Jenkins Fastmail PO Box 234, Collins St. West Melbourne, VIC 8007 Australia

澳大利亚维多利亚州墨尔本西柯林斯街234号尼尔·詹金斯快件邮政信箱8007

   Email: neilj@fastmailteam.com
   URI:   https://www.fastmail.com
        
   Email: neilj@fastmailteam.com
   URI:   https://www.fastmail.com
        

Chris Newman Oracle 440 E. Huntington Dr., Suite 400 Arcadia, CA 91006 United States of America

Chris Newman Oracle 440 E.Huntington博士,美国加利福尼亚州阿卡迪亚400室,91006

   Email: chris.newman@oracle.com
        
   Email: chris.newman@oracle.com