2019-02-13良好的 API 設(shè)計(jì)指南-RESTful API

要說RESTful首先來說說REST – REpresentational State Transfer (表述性狀態(tài)傳遞)

表述性狀態(tài)轉(zhuǎn)移是一組架構(gòu)約束條件和原則。滿足這些約束條件和原則的應(yīng)用程序或設(shè)計(jì)就是RESTful。需要注意的是,REST是設(shè)計(jì)風(fēng)格而不是標(biāo)準(zhǔn)。

以上的概念大概是許多關(guān)于RESTful中都會(huì)出現(xiàn)的定義概念。

那么什么是表述性狀態(tài)轉(zhuǎn)移呢?

首先,之所以晦澀是因?yàn)榍懊嬷髡Z被去掉了,全稱是 Resource Representational State Transfer,通俗來講就是,資源在網(wǎng)絡(luò)中以某種表現(xiàn)形式進(jìn)行狀態(tài)轉(zhuǎn)移。

在查詢很多資料后看到一句很精簡的總結(jié):

URL定位資源,用HTTP動(dòng)詞(GET,POST,DELETE,PUT等)描述操作。

既然說到了是用HTTP動(dòng)詞進(jìn)行操作。那么需要了解這里列出的4.5個(gè)非常重要的HTTP動(dòng)作,這里的0.5個(gè)是指PATCH,因?yàn)樗诠δ苌吓cPUT非常類似,剩下4個(gè)通常被API開發(fā)人員兩兩結(jié)合使用

GET(SELECT):從服務(wù)器獲取一個(gè)指定資源或一個(gè)資源集合;

POST(CREATE):在服務(wù)器上創(chuàng)建一個(gè)資源;

PUT(UPDATE):更新服務(wù)器上的一個(gè)資源,需要提供整個(gè)資源;

PATCH(UPDATE):更新服務(wù)器上的一個(gè)資源,只提供資源中改變的那部分屬性;

DELETE(DELETE):移除服務(wù)器上的一個(gè)資源;

還有兩個(gè)不常見的HTTP動(dòng)作:

HEAD – 獲取一個(gè)資源的元數(shù)據(jù),例如一組hash數(shù)據(jù)或者資源的最近一次更新時(shí)間;

OPTIONS – 獲取當(dāng)前用戶(Consumer)對(duì)資源的訪問權(quán)限;

關(guān)于RESTful的API設(shè)計(jì)風(fēng)格,說完RESTful接下來該說說API了。

API是服務(wù)提供方和使用方之間的契約,打破該契約將會(huì)給服務(wù)端開發(fā)人員招來非常大的麻煩,這些麻煩來自于使用API的開發(fā)人員,因?yàn)閷?duì)API的改動(dòng)會(huì)導(dǎo)致他們的移動(dòng)app無法工作。一個(gè)好的文檔對(duì)于解決這些事情能起到事半功倍的作用,但是絕對(duì)多數(shù)程序員都不喜歡寫文檔。如果想讓服務(wù)端的價(jià)值更好的體現(xiàn)出來,就要好好設(shè)計(jì)API。通過這些API,你的服務(wù)/核心程序?qū)⒂锌赡艹蔀槠渌?xiàng)目所依賴的平臺(tái);你提供的API越易用,就會(huì)有越多人愿意使用它。規(guī)劃API的展示形式可能比你想象的要簡單,首先要確定你的數(shù)據(jù)是如何設(shè)計(jì)以及核心程序是如何工作的。

image.png

也就是說Server提供的RESTful API中,URL中只使用名詞來指定資源,原則上不使用動(dòng)詞?!百Y源”是REST架構(gòu)或者說整個(gè)網(wǎng)絡(luò)處理的核心。

那么下面來具體說說如何形成良好的RESTful風(fēng)格的API設(shè)計(jì)

1. 使用名詞而不是動(dòng)詞

Server提供的RESTful API中,URL中只使用名詞來指定資源,原則上不使用動(dòng)詞?!百Y源”是REST架構(gòu)或者說整個(gè)網(wǎng)絡(luò)處理的核心。比如:

http://api.qc.com/v1/newsfeed: 獲取某人的新鮮;

http://api.qc.com/v1/friends: 獲取某人關(guān)系列表;

http://api.qc.com/v1/profile: 獲取某人的詳細(xì)信息;

URL是對(duì)資源描述的抽象,資源的描述一定是名詞,如果引入了動(dòng)詞,那這個(gè)URL就表示了一個(gè)動(dòng)作,而非一個(gè)資源,這樣就偏離了REST的設(shè)計(jì)思想

2.Get方法和查詢參數(shù)不應(yīng)該涉及狀態(tài)改變

使用PUT, POST 和DELETE 方法 而不是 GET 方法來改變狀態(tài),不要使用GET 進(jìn)行狀態(tài)改變:

通常,GET請(qǐng)求能夠被瀏覽器緩存(而且通常都會(huì)這么做),例如,當(dāng)用戶發(fā)起第二次POST請(qǐng)求時(shí),緩存的GET請(qǐng)求(依賴于緩存首部)能夠加快用戶的訪問速度。一個(gè)HEAD請(qǐng)求基本上就是一個(gè)沒有返回體的GET請(qǐng)求,因此也能被緩存。

3.使用復(fù)數(shù)名詞

不要混淆名詞單數(shù)和復(fù)數(shù),為了保持簡單,只對(duì)所有資源使用復(fù)數(shù)。

4. 使用子資源表達(dá)關(guān)系

如果一個(gè)資源與另外一個(gè)資源有關(guān)系,使用子資源:

5.使用Http頭聲明序列化格式

在客戶端和服務(wù)端,雙方都要知道通訊的格式,格式在HTTP-Header中指定

Content-Type 定義請(qǐng)求格式

Accept 定義系列可接受的響應(yīng)格式

6.使用HATEOAS

Hypermedia as the Engine of Application State 超媒體作為應(yīng)用狀態(tài)的引擎,超文本鏈接可以建立更好的文本瀏覽:

7.為集合提供過濾 排序 選擇和分頁等功能

Filtering過濾:

使用唯一的查詢參數(shù)進(jìn)行過濾:

GET /cars?color=red 返回紅色的cars

GET /cars?seats<=2 返回小于兩座位的cars集合

當(dāng)用戶請(qǐng)求獲取一組對(duì)象列表時(shí),你就需要對(duì)結(jié)果進(jìn)行過濾并返回一組嚴(yán)格符合用戶要求的對(duì)象。有時(shí)返回結(jié)果的數(shù)量可能非常大,但是你也不能隨意對(duì)此進(jìn)行約束,因?yàn)檫@種服務(wù)端的隨意約束會(huì)造成第三方開發(fā)人員的困惑。如果用戶請(qǐng)求了一個(gè)集合,并對(duì)返回結(jié)果進(jìn)行遍歷,然后只要前100個(gè)對(duì)象,那么這里就需要由用戶來指明這個(gè)限制量。這樣用戶就不會(huì)有這樣的疑惑:是他們程序的bug還是接口限制了100條?還是網(wǎng)絡(luò)只允許傳這么大的包?

Sorting排序:

允許針對(duì)多個(gè)字段排序

GET /cars?sort=-manufactorer,+model

這是返回根據(jù)生產(chǎn)者降序和模型升序排列的car集合

Field selection

移動(dòng)端能夠顯示其中一些字段,它們其實(shí)不需要一個(gè)資源的所有字段,給API消費(fèi)者一個(gè)選擇字段的能力,這會(huì)降低網(wǎng)絡(luò)流量,提高API可用性。

GET /cars?fields=manufacturer,model,id,color

Paging分頁

使用 limit 和offset.實(shí)現(xiàn)分頁,缺省limit=20 和offset=0;

GET /cars?offset=10&limit=5

為了將總數(shù)發(fā)給客戶端,使用訂制的HTTP頭: X-Total-Count.

鏈接到下一頁或上一頁可以在HTTP頭的link規(guī)定,遵循Link規(guī)定:

Link:

https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5; rel=”next”,

https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3; rel=”last”,

https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5; rel=”first”,

https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5; rel=”prev”,

8.版本化你的API

也就是進(jìn)行版本控制。無論你在設(shè)計(jì)什么系統(tǒng),也不管你事先做了多么詳盡的計(jì)劃,隨著時(shí)間的推移和業(yè)務(wù)的發(fā)展,你的程序總會(huì)發(fā)生變化,數(shù)據(jù)關(guān)系也會(huì)發(fā)生變化,資源可能會(huì)被添加或者刪除一些屬性。只要軟件還在生存期內(nèi)并且還有人在用它,開發(fā)人員就得面對(duì)這些問題,對(duì)于API設(shè)計(jì)來說,尤其如此。

在URL中加入版本號(hào)是一個(gè)優(yōu)秀的API設(shè)計(jì),當(dāng)然還有另一個(gè)常用的解決辦法就是把版本號(hào)放在請(qǐng)求首部中

使得API版本變得強(qiáng)制性,不要發(fā)布無版本的API,使用簡單數(shù)字,避免小數(shù)點(diǎn)如2.5。一般在Url后面使用?v

/blog/api/v1

9. 使用Http狀態(tài)碼處理錯(cuò)誤

如果你的API沒有錯(cuò)誤處理是很難的,只是返回500和出錯(cuò)堆棧不一定有用

Http狀態(tài)碼提供70個(gè)出錯(cuò),我們只要使用10個(gè)左右:

200 – OK – 一切正常

201 – OK – 新的資源已經(jīng)成功創(chuàng)建

204 – OK – 資源已經(jīng)成功擅長

304 – Not Modified – 客戶端使用緩存數(shù)據(jù)

400 – Bad Request – 請(qǐng)求無效,需要附加細(xì)節(jié)解釋如 “JSON無效”

401 – Unauthorized – 請(qǐng)求需要用戶驗(yàn)證

403 – Forbidden – 服務(wù)器已經(jīng)理解了請(qǐng)求,但是拒絕服務(wù)或這種請(qǐng)求的訪問是不允許的。

404 – Not found – 沒有發(fā)現(xiàn)該資源

422 – Unprocessable Entity – 只有服務(wù)器不能處理實(shí)體時(shí)使用,比如圖像不能被格式化,或者重要字段丟失。

500 – Internal Server Error – API開發(fā)者應(yīng)該避免這種錯(cuò)誤。

1XX的返回碼預(yù)留給HTTP的底層使用,在你的整個(gè)職業(yè)生涯中都不會(huì)主動(dòng)發(fā)送這種返回碼;

2XX的返回碼表示請(qǐng)求按照預(yù)期執(zhí)行并成功返回了信息。服務(wù)端要盡可能給用戶返回這種結(jié)果。

3XX的返回碼表示請(qǐng)求重定向,大多數(shù)API都不會(huì)經(jīng)常使用這種請(qǐng)求(),但是最新的超媒體API會(huì)充分使用這些功能。

4XX的返回碼主要表示由客戶端引起的錯(cuò)誤,例如請(qǐng)求參數(shù)錯(cuò)誤或者訪問一個(gè)不存在的資源,這些必須為冪等操作,并且不能改變服務(wù)器的狀態(tài)(其實(shí)服務(wù)器的狀態(tài)發(fā)生了改變就意味著操作不是冪等了)。

5XX的返回碼主要表示由服務(wù)器引起的錯(cuò)誤,通常情況下,這些錯(cuò)誤都是開發(fā)人員

使用詳細(xì)的錯(cuò)誤包裝錯(cuò)誤:

{"errors": [? {"userMessage":"Sorry, the requested resource does not exist","internalMessage":"No car found in the database","code":34,"more info":"http://dev.mwaysolutions.com/blog/api/v1/errors/12345"}? ]}12345678910111213141516171819

10.允許覆蓋http方法

一些代理只支持POST 和 GET方法, 為了使用這些有限方法支持RESTful API,需要一種辦法覆蓋http原來的方法。

使用訂制的HTTP頭 X-HTTP-Method-Override 來覆蓋POST 方法.

使用場景

版本號(hào)

在 RESTful API 中,API 接口應(yīng)該盡量兼容之前的版本。但是,在實(shí)際業(yè)務(wù)開發(fā)場景中,可能隨著業(yè)務(wù)需求的不斷迭代,現(xiàn)有的 API 接口無法支持舊版本的適配,此時(shí)如果強(qiáng)制升級(jí)服務(wù)端的 API 接口將導(dǎo)致客戶端舊有功能出現(xiàn)故障。實(shí)際上,Web 端是部署在服務(wù)器,因此它可以很容易為了適配服務(wù)端的新的 API 接口進(jìn)行版本升級(jí),然而像 Android 端、IOS 端、PC 端等其他客戶端是運(yùn)行在用戶的機(jī)器上,因此當(dāng)前產(chǎn)品很難做到適配新的服務(wù)端的 API 接口,從而出現(xiàn)功能故障,這種情況下,用戶必須升級(jí)產(chǎn)品到最新的版本才能正常使用。

為了解決這個(gè)版本不兼容問題,在設(shè)計(jì) RESTful API 的一種實(shí)用的做法是使用版本號(hào)。一般情況下,我們會(huì)在 url 中保留版本號(hào),并同時(shí)兼容多個(gè)版本。

【GET】? /v1/users/{user_id}// 版本 v1 的查詢用戶列表的 API 接口【GET】? /v2/users/{user_id}// 版本 v2 的查詢用戶列表的 API 接口

現(xiàn)在,我們可以不改變版本 v1 的查詢用戶列表的 API 接口的情況下,新增版本 v2 的查詢用戶列表的 API 接口以滿足新的業(yè)務(wù)需求,此時(shí),客戶端的產(chǎn)品的新功能將請(qǐng)求新的服務(wù)端的 API 接口地址。雖然服務(wù)端會(huì)同時(shí)兼容多個(gè)版本,但是同時(shí)維護(hù)太多版本對(duì)于服務(wù)端而言是個(gè)不小的負(fù)擔(dān),因?yàn)榉?wù)端要維護(hù)多套代碼。這種情況下,常見的做法不是維護(hù)所有的兼容版本,而是只維護(hù)最新的幾個(gè)兼容版本,例如維護(hù)最新的三個(gè)兼容版本。在一段時(shí)間后,當(dāng)絕大多數(shù)用戶升級(jí)到較新的版本后,廢棄一些使用量較少的服務(wù)端的老版本API 接口版本,并要求使用產(chǎn)品的非常舊的版本的用戶強(qiáng)制升級(jí)。

注意的是,“不改變版本 v1 的查詢用戶列表的 API 接口”主要指的是對(duì)于客戶端的調(diào)用者而言它看起來是沒有改變。而實(shí)際上,如果業(yè)務(wù)變化太大,服務(wù)端的開發(fā)人員需要對(duì)舊版本的 API 接口使用適配器模式將請(qǐng)求適配到新的API 接口上。

資源路徑

RESTful API 的設(shè)計(jì)以資源為核心,每一個(gè) URI 代表一種資源。因此,URI 不能包含動(dòng)詞,只能是名詞。注意的是,形容詞也是可以使用的,但是盡量少用。一般來說,不論資源是單個(gè)還是多個(gè),API 的名詞要以復(fù)數(shù)進(jìn)行命名。此外,命名名詞的時(shí)候,要使用小寫、數(shù)字及下劃線來區(qū)分多個(gè)單詞。這樣的設(shè)計(jì)是為了與 json 對(duì)象及屬性的命名方案保持一致。例如,一個(gè)查詢系統(tǒng)標(biāo)簽的接口可以進(jìn)行如下設(shè)計(jì)。

【GET】? /v1/tags/{tag_id}

同時(shí),資源的路徑應(yīng)該從根到子依次如下。

/{resources}/{resource_id}/{sub_resources}/{sub_resource_id}/{sub_resource_property}

我們來看一個(gè)“添加用戶的角色”的設(shè)計(jì),其中“用戶”是主資源,“角色”是子資源。

【POST】? /v1/users/{user_id}/roles/{role_id}// 添加用戶的角色

有的時(shí)候,當(dāng)一個(gè)資源變化難以使用標(biāo)準(zhǔn)的 RESTful API 來命名,可以考慮使用一些特殊的 actions 命名。

/{resources}/{resource_id}/actions/{action}

舉個(gè)例子,“密碼修改”這個(gè)接口的命名很難完全使用名詞來構(gòu)建路徑,此時(shí)可以引入 action 命名。

【PUT】? /v1/users/{user_id}/password/actions/modify// 密碼修改

請(qǐng)求方式

可以通過 GET、 POST、 PUT、 PATCH、 DELETE 等方式對(duì)服務(wù)端的資源進(jìn)行操作。其中,GET 用于查詢資源,POST 用于創(chuàng)建資源,PUT 用于更新服務(wù)端的資源的全部信息,PATCH 用于更新服務(wù)端的資源的部分信息,DELETE 用于刪除服務(wù)端的資源。

這里,筆者使用“用戶”的案例進(jìn)行回顧通過 GET、 POST、 PUT、 PATCH、 DELETE 等方式對(duì)服務(wù)端的資源進(jìn)行操作。

【GET】? ? ? ? ? /users# 查詢用戶信息列表【GET】? ? ? ? ? /users/1001# 查看某個(gè)用戶信息【POST】? ? ? ? /users# 新建用戶信息【PUT】? ? ? ? ? /users/1001# 更新用戶信息(全部字段)【PATCH】? ? ? ? /users/1001# 更新用戶信息(部分字段)【DELETE】? ? ? /users/1001# 刪除用戶信息

查詢參數(shù)

RESTful API 接口應(yīng)該提供參數(shù),過濾返回結(jié)果。其中,offset 指定返回記錄的開始位置。一般情況下,它會(huì)結(jié)合 limit 來做分頁的查詢,這里 limit 指定返回記錄的數(shù)量。

【GET】? /{version}/{resources}/{resource_id}?offset=0&limit=20

同時(shí),orderby 可以用來排序,但僅支持單個(gè)字符的排序,如果存在多個(gè)字段排序,需要業(yè)務(wù)中擴(kuò)展其他參數(shù)進(jìn)行支持。

【GET】? /{version}/{resources}/{resource_id}?orderby={field} [asc|desc]

為了更好地選擇是否支持查詢總數(shù),我們可以使用 count 字段,count 表示返回?cái)?shù)據(jù)是否包含總條數(shù),它的默認(rèn)值為 false。

【GET】? /{version}/{resources}/{resource_id}?count=[true|false]

上面介紹的 offset、 limit、 orderby 是一些公共參數(shù)。此外,業(yè)務(wù)場景中還存在許多個(gè)性化的參數(shù)。我們來看一個(gè)例子。

【GET】? /v1/categorys/{category_id}/apps/{app_id}?enable=[1|0]&os_type={field}&device_ids={field,field,…}

注意的是,不要過度設(shè)計(jì),只返回用戶需要的查詢參數(shù)。此外,需要考慮是否對(duì)查詢參數(shù)創(chuàng)建數(shù)據(jù)庫索引以提高查詢性能。

狀態(tài)碼

使用適合的狀態(tài)碼很重要,而不應(yīng)該全部都返回狀態(tài)碼 200,或者隨便亂使用。這里,列舉筆者在實(shí)際開發(fā)過程中常用的一些狀態(tài)碼,以供參考。

狀態(tài)碼描述

200請(qǐng)求成功

201創(chuàng)建成功

400錯(cuò)誤的請(qǐng)求

401未驗(yàn)證

403被拒絕

404無法找到

409資源沖突

500服務(wù)器內(nèi)部錯(cuò)誤

異常響應(yīng)

當(dāng) RESTful API 接口出現(xiàn)非 2xx 的 HTTP 錯(cuò)誤碼響應(yīng)時(shí),采用全局的異常結(jié)構(gòu)響應(yīng)信息。

HTTP/1.1 400 Bad RequestContent-Type: application/json{"code":"INVALID_ARGUMENT","message":"{error message}","cause":"{cause message}","request_id":"01234567-89ab-cdef-0123-456789abcdef","host_id":"{server identity}","server_time":"2014-01-01T12:00:00Z"}

請(qǐng)求參數(shù)

在設(shè)計(jì)服務(wù)端的 RESTful API 的時(shí)候,我們還需要對(duì)請(qǐng)求參數(shù)進(jìn)行限制說明。例如一個(gè)支持批量查詢的接口,我們要考慮最大支持查詢的數(shù)量。

【GET】? ? /v1/users/batch?user_ids=1001,1002// 批量查詢用戶信息參數(shù)說明- user_ids: 用戶ID串,最多允許20個(gè)。

此外,在設(shè)計(jì)新增或修改接口時(shí),我們還需要在文檔中明確告訴調(diào)用者哪些參數(shù)是必填項(xiàng),哪些是選填項(xiàng),以及它們的邊界值的限制。

【POST】? ? /v1/users// 創(chuàng)建用戶信息請(qǐng)求內(nèi)容{"username":"lgz",// 必填, 用戶名稱, max 10"realname":"梁桂釗",// 必填, 用戶名稱, max 10"password":"123456",// 必填, 用戶密碼, max 32"email":"lianggzone@163.com",// 選填, 電子郵箱, max 32"weixin":"LiangGzone",// 選填,微信賬號(hào), max 32"sex":1// 必填, 用戶性別[1-男 2-女 99-未知]}

響應(yīng)參數(shù)

針對(duì)不同操作,服務(wù)端向用戶返回的結(jié)果應(yīng)該符合以下規(guī)范。

【GET】? ? /{version}/{resources}/{resource_id}// 返回單個(gè)資源對(duì)象【GET】? ? /{version}/{resources}// 返回資源對(duì)象的列表【POST】? ? /{version}/{resources}// 返回新生成的資源對(duì)象【PUT】? ? /{version}/{resources}/{resource_id}// 返回完整的資源對(duì)象【PATCH】? /{version}/{resources}/{resource_id}// 返回完整的資源對(duì)象【DELETE】? /{version}/{resources}/{resource_id}// 狀態(tài)碼 200,返回完整的資源對(duì)象。// 狀態(tài)碼 204,返回一個(gè)空文檔

如果是單條數(shù)據(jù),則返回一個(gè)對(duì)象的 JSON 字符串。

HTTP/1.1 200 OK{"id":"01234567-89ab-cdef-0123-456789abcdef","name":"example","created_time": 1496676420000,"updated_time": 1496676420000,? ? ...}

如果是列表數(shù)據(jù),則返回一個(gè)封裝的結(jié)構(gòu)體。

HTTP/1.1 200 OK{"count":100,"items":[? ? ? ? {"id":"01234567-89ab-cdef-0123-456789abcdef","name":"example","created_time": 1496676420000,"updated_time": 1496676420000,? ? ? ? ? ? ...? ? ? ? },? ? ? ? ...? ? ]}

一個(gè)完整的案例

最后,我們使用一個(gè)完整的案例將前面介紹的知識(shí)整合起來。這里,使用“獲取用戶列表”的案例。

【GET】? ? /v1/users?[&keyword=xxx][&enable=1][&offset=0][&limit=20] 獲取用戶列表功能說明:獲取用戶列表請(qǐng)求方式:GET參數(shù)說明- keyword: 模糊查找的關(guān)鍵字。[選填]-enable: 啟用狀態(tài)[1-啟用 2-禁用]。[選填]- offset: 獲取位置偏移,從 0 開始。[選填]-limit: 每次獲取返回的條數(shù),缺省為 20 條,最大不超過 100。 [選填]響應(yīng)內(nèi)容HTTP/1.1 200 OK{"count":100,"items":[? ? ? ? {"id":"01234567-89ab-cdef-0123-456789abcdef","name":"example","created_time": 1496676420000,"updated_time": 1496676420000,? ? ? ? ? ? ...? ? ? ? },? ? ? ? ...? ? ]}失敗響應(yīng)HTTP/1.1 403 UC/AUTH_DENIEDContent-Type: application/json{"code":"INVALID_ARGUMENT","message":"{error message}","cause":"{cause message}","request_id":"01234567-89ab-cdef-0123-456789abcdef","host_id":"{server identity}","server_time":"2014-01-01T12:00:00Z"}錯(cuò)誤代碼- 403 UC/AUTH_DENIED? ? 授權(quán)受限

作者:趙客縵胡纓v吳鉤霜雪明

鏈接:http://www.itdecent.cn/p/a88d07ad1493

來源:簡書

簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容