REST URI约定 - 创建资源时的单数或复数名称

JPR*_*ddy 491 rest resources uri naming-conventions

我是REST的新手,我发现在一些RESTful服务中,他们使用不同的资源URI进行更新/获取/删除和创建.如

  • 创建 - 使用/ resource(单数)在某些地方使用POST方法(观察复数)使用/ resources
  • 更新 - 使用/ resource/123和PUT方法
  • 获取 - 使用/ resource/123和GET方法

我对这个URI命名约定有点困惑.我们应该使用复数或单数来创建资源?决定时应该有什么标准?

jla*_*jla 571

对我来说最好有一个可以直接映射到代码的模式(易于自动化),主要是因为代码是两端的.

GET  /orders          <---> orders 
POST /orders          <---> orders.push(data)
GET  /orders/1        <---> orders[1]
PUT  /orders/1        <---> orders[1] = data
GET  /orders/1/lines  <---> orders[1].lines
POST /orders/1/lines  <---> orders[1].lines.push(data) 
Run Code Online (Sandbox Code Playgroud)

  • 这种困难或容易是因为不尊重HATEOS.它是复数还是单数或其他任何东西都无关紧要.你应该尊重从服务器发送的uri,而不是在客户端"建立"你的uri.然后你有0映射为您的代码. (20认同)
  • @richard客户端仍然需要进行映射.在HATEOS中,他们必须映射到表示URI构造的关系(rel)的名称.然后,rel,方法(动词)和Content-Type构成资源媒体.这并不排除需要良好的URI设计.即使客户端可能优先考虑rel名称,API的开发人员仍然需要一个良好的人类可读标准来构建URI. (5认同)
  • 这对我来说很有意义.但是,我们是数据库第一的商店,这意味着我们从数据库模式生成代码和api实体.并且数据库标准倾向于提倡单数表名称,因此我们将继续使用它,但仍然采用与此答案相同的逻辑. (4认同)
  • 我认为这是一个更好的答案。除了我一直更喜欢使用单数而不是复数。User.getList(),User.getById,User.delete等。 (3认同)
  • 我喜欢简单。映射的好处还在于,可以很容易地编写关于路由的文档和测试。 (3认同)

Wil*_*ung 268

使用的前提/resources是它代表"所有"资源.如果您这样做GET /resources,您可能会返回整个集合.通过POST /resources,您将添加到集合中.

但是,个人资源可在/ resource获得.如果你这样做GET /resource,你可能会出错,因为这个请求没有任何意义,但是很/resource/123有道理.

使用/resource,而不是/resources类似于你会怎么做,如果你用,比如工作,文件系统和文件的集合,/resource是"目录"与个人123,456在它的文件.

无论哪种方式都是对或错,请选择您最喜欢的方式.

  • "无论哪种方式都是对或错,请选择你最喜欢的方式." 我经常听到这条着名的线路,生病了,厌倦了听到人们的声音.公约很重要,应该在社区中进行建设性的辩论,这是更好的解决方案和良好实践的地方.当你在URI中使用复数和单数的复数和单数时,它会使你的代码和API变得复杂,因为API后面的用户和代码必须考虑路由和逻辑中的代码来区分单个与复数,而如果你只是坚持复数一直没有问题. (174认同)
  • 很棒的答案!但Windows中的"默认"目录有**多个**名称.像"程序文件","用户","文档","视频"等.我也经常在网站网址中遇到复数名称. (52认同)
  • 事实上,大多数人和API所采用的事实都是在任何时候保持多元化.Ids指定一个资源车/ id (47认同)
  • @TomaszPluskiewicz你完全正确_clients_不关心.作为_software开发者_我们应该关心 - 为此我同意WTF的评论,关于公约的建设性辩论是有价值的. (28认同)
  • 所以,有人可以只用一个单词的答案让它接受,所以我不必再读这个(再次). (7认同)
  • @WTF这完全不正确.URI模板应该是完全不透明的.以下是Roy Fielding的论文"在任何时候服务器或客户端软件都不需要知道或理解URI的含义"的引用.不必在客户端和服务器上构造或解构URI.即使所有使用GUID的URL(如http://example.org/ {GUID}),一个好的API也应该同样有效.这是因为URL应作为链接进行通信.客户端不关心服务器如何分配标识符,并且可以通过使用完整URL作为ID来减轻服务器端路由 (3认同)
  • 我相信您在提及单个项目时应该使用复数。也许两者都被接受为 REST 最佳实践,但复数更常见。GET /resources(所有资源的列表) GET /resources/{id}(单个资源) (3认同)
  • 我不同意为单个 GET 与 GET 列表使用不同的 URL。这意味着用户需要根据他们的方法管理额外的 URL。保持相同以实现最佳易用性 (3认同)
  • 我建议坚持其中任何一个.单数复数是英语人类可以轻易理解的东西,但机器理解并不是微不足道的.库存是单数还是复数?为什么用户的复数是User + s,但Baby为婴儿?这使得它非常不一致.对于一台机器. (2认同)
  • 通常,URL末尾的斜杠"/"表示容器.即容器/资源/基本上有子资源/资源/ 12和/ resource/23 .. (2认同)
  • 此页面网址为/sf/ask/479204071/,即复数:) (2认同)
  • 让我们以 StackOverflow 作为参考。当前的 URL 是 stackoverflow.com/questions/6845772/title-of-the-question-with-hyphens 我相信这在某种程度上详细阐述了科技巨头使用的良好实践。 (2认同)

Jan*_*ard 261

我也没有看到这样做的重点,我认为这不是最好的URI设计.作为RESTful服务的用户,我希望列表资源具有相同的名称,无论我是否访问列表中的列表或特定资源.无论您是要使用列表资源还是特定资源,都应使用相同的标识符.

  • 就我而言,这是最好的答案.我很欣赏API设计人员喜欢说"获取资源#123"的语言正确性,但在编写API的客户端以及帮助文档时,这会带来额外的编码麻烦.(GET/api/people vs. GET/api/person/123?euuuchh.)....而不是像"获取资源#123"那样思考它,在你脑海中将它称为"从资源集合中获取"匹配#123". (65认同)
  • 选择/ employees/12来表示对最近被解雇的员工(或任何子集)的搜索查询,我认为这将是糟糕的设计.如果你想表示任何类型的子集,而不是我建议将它们作为资源(具有适当的名称)引入它们自己的权利. (9认同)
  • 区分复数/奇异资源不是关于语言的正确性,而是关于规模./ employees/12作为id为'12'的员工资源的子集读取给我(它可能意味着任何事情,例如对最近被解雇的员工的已保存搜索查询).如果您将上述内容视为ID为"12"的员工,您将如何表示该子集?唯一的选择是使URI更复杂,区分包含对象本身的集合(即单数与复数). (4认同)
  • 如果您认为任意URL不漂亮,请随意识别具有复数名称的集合资源和具有单数名称的单个项目.如果您不喜欢英文网址,并且您的自然语言不支持单数/复数表示法的方式,请使用其他内容以您的首选语言定义它我想所有语言都能让您以某种方式区分'/ the-collection-of- bla/2321'与'bla/61'的书面形式.当发送GET/PUT/DELETE/POST/PATCH等时,这两个不同资源中的每一个都代表完全不同的结果. (4认同)
  • 这与客户的可理解性无关.它是关于使用不同的URL处理不同的事情.并且能够在不发生冲突的情况下响应所有HTTP方法.您可以拥有一个项目集合的资源,以及一个表示项目本身的资源.对于所有我关心的集合资源可以是https://example.org/166316e2-e1和该集合中的一个特定项目https://example.org/20d68348-ccc-001c4200de.客户端不应该构造URL(显然不会扩展,它不是RESTful,而是链接关系类型的用途). (3认同)
  • 理查德,你很难理解我的立场?好的,我会尝试为你简化它:它不是'更一致','更容易编程','更容易维护'或'更容易监控'一个程序,你使用两个不同的单词代表一种类型的资源.考虑"人"与"人":无论您是系统管理员扫描日志文件,中国开发人员是否使用英语,或者是第一次学习您的API的开发人员,这都会增加学习时间,同时提供零利益应用程序的功能. (2认同)

Eri*_*son 65

复数

  • 简单 - 所有网址都以相同的前缀开头
  • 逻辑 - orders/获取订单的索引列表.
  • 标准 - 最广泛采用的标准,其次是绝大多数公共和私有API.

例如:

GET /resources - 返回资源项列表

POST /resources - 创建一个或多个资源项

PUT /resources - 更新一个或多个资源项

PATCH /resources - 部分更新一个或多个资源项

DELETE /resources - 删除所有资源项

对于单个资源项:

GET /resources/:id- 根据:id参数返回特定资源项

POST /resources/:id - 创建一个具有指定id的资源项(需要验证)

PUT /resources/:id - 更新特定资源项

PATCH /resources/:id - 部分更新特定资源项

DELETE /resources/:id - 删除特定资源项

对于单数的拥护者,可以这样想:你会问一个人order并且期待一件事或一系列事情吗?那么,为什么您希望服务在您键入时返回一个列表/order

  • **奇异**:如果您的系统的一部分只有一个对象(0-1,是否存在),例如users/1/avatar,您可以使用单数形式标记此单个对象(例如头像) - 更详细例如:/sf/answers/2680735221/.顺便说一句 - 非常好的答案:) (8认同)
  • 表名的复数与单数争论一直很复杂,因为表定义了其模式/结构(暗示单数)以及数据的容器/集合(暗示复数)。在编程语言中不会遇到这个问题,因为类定义是单数,但保存集合的属性将是复数。重点是,甚至不要尝试将类命名约定应用于表,反过来也不要将表命名约定应用于其余服务。 (5认同)
  • @WillSheppard - 类名最好采用单数形式,表名最好采用复数形式。例如,对于处理引用一个订单的对象的单个实例的类来说,“Order”是一个很好的名称。“OrderList”是处理多个“Order”实例的类的名称。“订单表”是一个包含许多订单的数据库表的好名字。 (3认同)

Sor*_*ter 41

单数

便利 事物可以有不规则的复数名称.有时他们没有.但奇异的名字总是在那里.

例如CustomerAddresses上的CustomerAddress

考虑这个相关的资源.

这比/order/12/orderdetail/12它更具可读性和逻辑性/orders/12/orderdetails/4.

数据库表

资源表示像数据库表这样的实体.它应该具有逻辑上的单数名称.这是表名的答案.

类映射

类总是单数.ORM工具生成与类名相同的表.随着越来越多的工具被使用,奇异的名称正在成为一种标准.

阅读有关REST API Developer's Dilemma的更多信息

  • *奇异的名字总是在那里*`/衣服/ 12 /裤子/ 34` :) (32认同)
  • @GertArnold这个词"衣服"是一个动词.在讨论资源时,Rest API通常会粘到名词上,在描述操作时会使用动词.单数形式是"clout",但是过时,可能更适合用"服装"代替. (15认同)
  • @SteveBuzonas 裤子和太阳镜呢? (2认同)
  • 相反/fish/fish{id}。由于使用大量名词而进行分组时也存在问题,这些名词也可能是过时的:/murders/murder{id}/crow{id}; /gaggles/gaggle{id}/鹅{id}。所以复数也是可以的。“简单的标准规则”永远不会起作用,规则与人类语言的“自然”表达能力之间总会存在不匹配的地方。真正的问题是是否 a) 接受笨拙的 uri 设计作为事实上的标准 b) 拒绝粗糙且过于简单化的“标准约定”。 (2认同)

red*_*edi 30

虽然最普遍的做法是RESTful apis,其中使用复数,例如/api/resources/123,有一个特殊情况,我发现使用比复数名称更恰当/富有表现力的单数名称.这是一对一关系的情况.特别是如果目标项是值对象(在Domain-driven-design范例中).

让我们假设每个资源都是一对一的accessLog,可以将其建模为值对象,即不是实体,因此没有ID.它可以表达为/api/resources/123/accessLog.通常的动词(POST,PUT,DELETE,GET)将恰当地表达意图以及这种关系确实是一对一的事实.

  • @TomRussell为什么?这一点的含义很重要.我理解为什么你会使用复数,即使你通过标识符访问资源,但对于多对一或一对一,这是非常误导.考虑一个管理多地点公司员工的api.每个工作人员都在一个地方工作.`GET/users/123/location`应该获取用户工作的位置.不是'GET/users/123/locations`真的误导消费者吗? (8认同)
  • 不错的尝试.但它会更好"accessLogEntries".:-) (4认同)

Sla*_*mir 21

为什么不遵循数据库表名的普遍趋势,通常接受单数形式?在那里,完成了 - 让我们重用.

表命名困境:奇异与多个名称

  • Das Auto比Die Autos更好.此外,英语复数约定不一致. (7认同)
  • 资源名称空间是语义问题,而不是实现问题.所以,使用DB表类比,并不是很幸运.此外,在使用DB-s时,您只操作表,当然您可以影响内容(行),但在REST中没有约束直接操作**单**资源. (7认同)
  • 它不仅仅是表名,它也与OO中的类名相当(我的类称为Customer而不是Customers). (3认同)
  • 我认为这是一个很好的类比,但是比决定采用单数还是复数更重要的一点是要与您选择的任何一个保持一致。您不会插入用户,然后从用户中选择。相同的规则应适用于REST资源-请勿根据您的工作重命名它们。 (2认同)

小智 19

我很惊讶地看到很多人会跳上复数名词的潮流.在实现单数转换为多数转换时,您是否正在处理不规则复数名词?你喜欢疼吗?

http://web2.uvcs.uvic.ca/elc/studyzone/330/grammar/irrplu.htm

有许多类型的不规则复数,但这些是最常见的:

名词类型形成复数的例子

Ends with -fe   Change f to v then Add -s   
    knife   knives 
    life   lives 
    wife   wives
Ends with -f    Change f to v then Add -es  
    half   halves 
    wolf   wolves
    loaf   loaves
Ends with -o    Add -es 
    potato   potatoes
    tomato   tomatoes
    volcano   volcanoes
Ends with -us   Change -us to -i    
    cactus   cacti
    nucleus   nuclei
    focus   foci
Ends with -is   Change -is to -es   
    analysis   analyses
    crisis   crises
    thesis   theses
Ends with -on   Change -on to -a    
    phenomenon   phenomena
    criterion   criteria
ALL KINDS   Change the vowel or Change the word or Add a different ending   
     man   men
     foot   feet
     child   children
     person   people
     tooth   teeth
     mouse   mice
 Unchanging Singular and plural are the same    
     sheep deer fish (sometimes)
Run Code Online (Sandbox Code Playgroud)

  • 我不明白这里的担忧。我们不应该通过编程将单数更改为复数。上述多数复数形式中的大多数都是众所周知的,因此不必担心。如果某人英语知识较差,那么他将错误地拼写您变量的任何部分。另外,按照您的逻辑,是否还建议您使用单数形式在源代码中引用集合? (3认同)
  • 有些英语单词不规则,甚至在英语圈内也经常出现问题,它们是常用的术语,例如索引/索引/索引、顶点/顶点/顶点、矩阵/矩阵/矩阵、半径/半径/无论如何,我不认为将 REST 路径设为复数有什么意义,因为如果它们始终是单数,那么对每个人来说都更明显。 (3认同)
  • @DamingFu 单一资源可能并不总是有与其关联的 id。例如。/user/{id}/nickName 通过查看,不清楚它是否会返回昵称列表或单个昵称?因此,API 在使用复数形式时更加直观。是的,很少有单词会有不规则的复数形式。对于阅读复数形式的人来说,这不是问题。仅在编写 API 签名时才会出现问题。但此类单词出现的频率并不高,而且查找任何单词的复数形式也不费时间。为了使 API 更加直观,我们应该接受这种权衡。 (3认同)
  • @kishorborate,使用复数作为 URI 更容易出错,即使对于以英语为母语的人也是如此。正如 damd 所指出的,像索引/索引/索引这样的复数正在引入更多的问题。还有不可数名词。将不可数名词与复数混合是另一个问题。为什么让程序员更难在这些上花费更多的时间呢?我建议一切都使用单数。如果存在 /{id},则 API 应返回一条记录。如果后面没有 /{id},则 API 应返回该集合。 (2认同)

cos*_*r11 13

从API使用者的角度来看,端点应该是可预测的

理想的情况下...

  1. GET /resources 应该返回资源列表.
  2. GET /resource 应返回400级状态代码.
  3. GET /resources/id/{resourceId} 应该返回一个包含一个资源的集合.
  4. GET /resource/id/{resourceId} 应该返回一个资源对象.
  5. POST /resources 应该批量创建资源.
  6. POST /resource 应该创建一个资源.
  7. PUT /resource 应该更新资源对象.
  8. PATCH /resource 应该通过仅发布更改的属性来更新资源.
  9. PATCH /resources 应该批量更新仅发布已更改属性的资源.
  10. DELETE /resources应删除所有资源; 开玩笑:400状态代码
  11. DELETE /resource/id/{resourceId}

这种方法最灵活,功能最丰富,但也是最耗时的开发方法.因此,如果您赶时间(软件开发总是这样),只需命名您的端点resource或复数形式resources.我更喜欢单数形式,因为它为您提供了以编程方式进行内省和评估的选项,因为并非所有复数形式都以's'结尾.

说了这么多,无论出于何种原因,最常用的练习开发者选择的是使用复数形式.这是最终我选择的路线,如果你看流行的API,如githubtwitter,这是他们做什么.

决定的一些标准可能是:

  1. 我的时间限制是什么?
  2. 我允许消费者做什么操作?
  3. 请求和结果有效负载是什么样的?
  4. 我是否希望能够使用反射并在我的代码中解析URI?

所以这取决于你.无论你做什么都是一致的.

  • 似乎选择了 *plural* 形式,因为开发人员似乎假设所有资源本质上都是某个集合的一部分。但是,“接受的约定”似乎表明`POST /users` 应该创建一个用户,并将其添加到集合中。我不同意。`POST /users` 应该创建一个用户列表(即使这是一个 1 的列表),而 `POST /user` 应该创建一个用户。我看不出为什么复数和单数资源端点不能共存。它们描述了不同的行为,不应让任何人对它们的功能感到惊讶。 (2认同)
  • @TomRussell 通常是服务器创建 id,所以你还不知道要 POST 的 id。 (2认同)
  • @TomRussell,当客户端在创建新资源时确定(某种)id时,更常见的是使用`PUT/users/<id>`而不是`POST`.`POST`的解释是"将此添加到集合中,并将id确定为其中的一部分".`PUT`的解释是"使用此id更新(或添加)此资源".有关此原则的更长解释,请参见http://restcookbook.com/HTTP%20Methods/put-vs-post/. (2认同)

Tig*_*Too 10

路由中的 id 应该被视为与列表的索引相同,并且命名应该相应地进行。

numbers = [1, 2, 3]

numbers            GET /numbers
numbers[1]         GET /numbers/1
numbers.push(4)    POST /numbers
numbers[1] = 23    UPDATE /numbers/1
Run Code Online (Sandbox Code Playgroud)

但是有些资源在它们的路由中不使用 id,因为要么只有一个,要么用户永远无法访问多个,所以这些不是列表:

GET /dashboard
DELETE /session
POST /login
GET /users/{:id}/profile
UPDATE /users/{:id}/profile
Run Code Online (Sandbox Code Playgroud)

  • 我认为使用会话进行登录 POST 是有意义的,但我不同意将其复数化。您的用户/浏览器组合一次永远无法访问多个会话。你有一个,当你完成后它就会被删除。前端或后端上都没有一段代码会引用用户的多个会话。这对我来说很独特。 (3认同)
  • 不要使用 POST /登录。使用 POST /sessions 将会话添加到会话集合(有效地使用户登录)并使用 DELETE /sessions 从会话集合中删除会话(有效地使用户注销) (2认同)

mtr*_*eur 9

最重要的事情

每当您在界面和代码中使用复数时,问问自己,您的惯例如何处理这样的单词:

  • /pants, /eye-glasses- 这些是单数路径还是复数路径?

  • /radii- 你是否立刻就知道该路径的唯一路径是/radiusor /radix

  • /index- 你是否立即知道复数路径是否是/indexesor/indeces/indices

理想情况下,约定应该能够无规则地扩展。英语复数形式不会这样做,因为

  1. 它们有例外,例如以复数形式调用的某物之一,并且
  2. 没有简单的算法可以从单数得到单词的复数,从复数得到单数,或者判断未知名词是单数还是复数。

这有缺点。我脑海中最突出的几个:

  1. 单数和复数形式相同的名词将强制您的代码处理“复数”端点和“单数”端点具有相同路径的情况。
  2. 您的用户/开发人员必须精通英语,才能知道名词的正确单数和复数。在日益国际化的世界中,这可能会导致不可忽视的挫败感和开销。
  3. 它单枪匹马地变成了“我知道了/foo/{{id}},有什么办法可以得到一切foo呢?” 变成自然语言问题,而不是“只删除最后一个路径部分”问题。

与此同时,一些人类语言甚至没有不同的名词单数和复数形式。他们管理得很好。你的 API 也可以。


Sha*_*ews 8

请参阅GoogleAPI 设计指南:资源名称,了解另一种命名资源的方法。

该指南要求以复数形式命名集合。

|--------------------------+---------------+-------------------+---------------+--------------|
| API Service Name         | Collection ID | Resource ID       | Collection ID | Resource ID  |
|--------------------------+---------------+-------------------+---------------+--------------|
| //mail.googleapis.com    | /users        | /name@example.com | /settings     | /customFrom  |
| //storage.googleapis.com | /buckets      | /bucket-id        | /objects      | /object-id   |
|--------------------------+---------------+-------------------+---------------+--------------|
Run Code Online (Sandbox Code Playgroud)

如果您正在考虑这个主题,则值得一读。


小智 6

我的两分钱:花费时间从复数变为单数或反之亦然的方法是浪费CPU周期。我可能是高中生,但是在我的时代,事物被称为相同。我如何查找有关人的方法?没有规律的表达不会覆盖人和人,而不会产生不良副作用。

英文复数可以是非常任意的,它们不必要地妨碍了代码。遵守一个命名约定。计算机语言应该是数学上的清晰度,而不是模仿自然语言。

  • 这解决了尝试“自动生成/修改”端点的代码(有许多自以为是的库,它们假设复数/奇异性并尝试映射);然而,这确实适用于*明确*选择的端点名称,而不是选择正确的单词(无论它如何复数)。 (2认同)

小智 6

我更喜欢使用单数形式来实现简单性和一致性.

例如,考虑以下网址:

/客户/ 1

我会将客户视为客户收集,但为简单起见,将删除收集部分.

另一个例子:

/设备/ 1

在这种情况下,设备不是正确的复数形式.因此,将其视为设备集合并简化集合,使其与客户案例保持一致.

  • POST /customer 听起来像是要替换唯一的客户。这是我对使用单一资源名称的最大悲痛。 (2认同)
  • @ andrew-t-finnell`POST / customer`不是应该做的吗?插入一个客户? (2认同)

joh*_*384 5

我不喜欢看到{id}URL 的一部分与子资源重叠,因为id理论上可以是任何东西,并且会产生歧义。它混合了不同的概念(标识符和子资源名称)。

类似的问题经常出现在enum常量或文件夹结构中,其中不同的概念混合在一起(例如,当您有文件夹TigersLionsCheetahs,然后还有一个Animals在同一级别调用的文件夹时 - 这没有任何意义,因为其中一个是文件夹的子集)其他)。

一般来说,我认为端点的最后命名部分如果一次处理单个实体,则应该是单数;如果它处理实体列表,则应该是复数。

因此处理单个用户的端点:

GET  /user             -> Not allowed, 400
GET  /user/{id}        -> Returns user with given id
POST /user             -> Creates a new user
PUT  /user/{id}        -> Updates user with given id
DELETE /user/{id}      -> Deletes user with given id
Run Code Online (Sandbox Code Playgroud)

然后有单独的资源用于对用户进行查询,通常返回一个列表:

GET /users             -> Lists all users, optionally filtered by way of parameters
GET /users/new?since=x -> Gets all users that are new since a specific time
GET /users/top?max=x   -> Gets top X active users
Run Code Online (Sandbox Code Playgroud)

这里有一些处理特定用户的子资源的示例:

GET /user/{id}/friends -> Returns a list of friends of given user
Run Code Online (Sandbox Code Playgroud)

交个朋友(多对多链接):

PUT /user/{id}/friend/{id}     -> Befriends two users
DELETE /user/{id}/friend/{id}  -> Unfriends two users
GET /user/{id}/friend/{id}     -> Gets status of friendship between two users
Run Code Online (Sandbox Code Playgroud)

永远不会有任何歧义,资源的复数或单数命名是向用户暗示他们可以期望什么(列表或对象)。对 s 没有限制id,理论上可以让用户的 idnew不与(未来可能的)子资源名称重叠。

  • 复数版本位于答案“/user/{id}/friends”中,它将返回所有朋友。单数版本“/user/{id}/friend”将是一个错误请求 400,就像“/user”一样。 (2认同)