作為架構風格的 REST 到底是什麼

来源:https://www.cnblogs.com/linvanda/archive/2020/07/26/13381251.html
-Advertisement-
Play Games

很多人搞不明白 REST(Representational State Transfer 表述性狀態轉移)原因在於一開始就是把它當做設計風格而不是架構風格來理解,因而一上來就大談特談什麼 RESTful API,結果是只見樹木不見森林。 僅從設計的角度去理解 REST(僅把它作為 API 設計原則) ...


很多人搞不明白 REST(Representational State Transfer 表述性狀態轉移)原因在於一開始就是把它當做設計風格而不是架構風格來理解,因而一上來就大談特談什麼 RESTful API,結果是只見樹木不見森林。

僅從設計的角度去理解 REST(僅把它作為 API 設計原則),最多僅能理解其資源、表述這些概念,卻很難理解狀態轉移到底是怎麼回事。

要想搞清楚 REST,必須透徹理解三個關鍵概念:資源、表述、狀態轉移

REST 架構風格提出者和 HTTP 1.1 規範主要設計者都是同一個人 Roy Fielding。事實上,HTTP 1.1 正是 REST 風格的實現,因而認識 REST 最好的方式是從基於 HTTP 的 Web 應用開始。

場景:

我們看一個典型場景。

李小四想在京東上買一部 iPhone。

首先他在瀏覽器地址欄輸入 www.jd.com(當然也可以通過搜索引擎進入),打開京東商城首頁,然後在首頁搜索欄輸入“iPhone”,回車,頁面切換到含有 iPhone 關鍵字的商品列表。

李小四用滑鼠點擊其中一個商品,進入該商品詳情頁。

李小四看了看介紹,覺得中意,於是選定顏色、型號、規格、數量,點擊“加入購物車”,再點擊“去購物車結算“,填寫收貨人信息、支付方式、開票信息,點擊“提交訂單”,選擇一種支付方式支付並完成訂單。

李小四這個人性子比較急,下了單後,每隔一段時間就點開“我的訂單”,點開物流信息看看手機到哪了。

終於,手機送到了,李小四從快遞員那裡簽收後,京東立馬通過微信給他推送一條貨物簽收通知,並且附上開票鏈接。李小四點擊進入開票頁面,獲取一張電子發票。

資源及其表述:

在整個購物過程中,李小四與之交互的是一個叫“京東商城”的 Web 應用——這是 REST 的作用對象。作為架構風格的 REST,其作用對象是一個完整的應用(或者系統)——確切地說是異構的分散式應用——而不是某一兩個 API。這樣的視角是理解 REST 全貌的關鍵。

李小四是如何獲取到他想要的信息的?跑到賣家倉庫去看實體 iPhone?如果這樣,就沒有 Web 什麼事了。李小四在瀏覽器地址欄輸入了一串叫 URL 的東西,然後瀏覽器就顯示出京東商城首頁了。

到底什麼是資源?

本例中,真正的資源是 iPhone、物流、發票、錢等,但在談論 Web 的時候,我們說的資源一般不是指這些真正的實物資源,而是指存儲在伺服器上的特定數據,如這裡的 iPhone、訂單、物流、發票、賬戶的數據信息

對於實體 iPhone,我們可以去專賣店看看摸摸,那 Web 上的 iPhone 數據,我們如何找到它,又如何看如何摸呢?

前輩們設計了個偉大的東西叫 URI,你每台 iPhone 不是有唯一編號嘛,那 Web 上這些虛擬的數據我們也以虛擬物品(資源)的方式給它做唯一編號(標識)。雖然是由資源(虛擬數據)的擁有者來給它做標識,但為了統一、通用,前輩們對資源標識做了一些約束(協議),就形成了統一資源標識符(Uniform Resource Identifier,URI),這樣便解決瞭如何找到資源的問題。比如 URL 通過 schema、功能變數名稱、埠定位到伺服器(資源擁有者),伺服器內部再通過 path 和其他參數找到並處理資源。

從 URI (URL 是 URI 的一種實現方案)的定義看,它本身就是用來表達資源的,天生就是名詞特性,只是在實際使用過程中不知為啥就跑歪了,各種 /pathto/getuserinfo 動詞性的 URL 滿天飛(個人認為是成也 HTTP 動詞,敗也 HTTP 動詞,更詳細的分析見後面)。

資源是找到了,但我們如何跟它交互呢?

如果是在本機,我們可以通過程式直接操作資源(如通過程式指針直接操作記憶體數據),但 Web 是個分散式環境,指針沒那麼長,夠不到對方的記憶體怎麼辦?

於是我們需要在本地(客戶端)擁有一份資源的副本。在 C/S 架構中,只有伺服器擁有資源本身,其它客戶端拿到的都是副本,而且擁有者(伺服器)可以決定提供什麼樣的副本給客戶端(提供哪些信息、以什麼樣的格式提供信息)。

這種帶特定格式的資源副本就是資源的表述

作為資源擁有者,伺服器當然可以決定提供什麼樣的表述形式,但正如 URI 一樣,如果沒有大家都認可的、通用的、被廣泛支持的格式,伺服器們各說自話,互相語言不通,那萬維網恐怕就會成為巴比倫塔了。

於是前輩們又定義了一些通用的資源表述格式,官方話叫媒體類型。Web 上用的最廣泛的媒體類型應該是 text/html,其他還有 image/jpeg、application/json、text/xml 等。

一個資源可以有多種表述(多種媒體類型),也就是說客戶端(如瀏覽器)通過一個 URI(如 URL )可以獲得該資源的多種表述中的一種。那麼客戶端和伺服器端是如何溝通以在表述形式上達成一致呢?

在 HTTP 中,通過頭信息協商。HTTP 有一系列 accept 請求頭就是用來乾這事的,如 accept、accept-encoding、accept-language。比如 accept: image/webp,image/apng,image/* 告知伺服器“我能處理這些媒體類型,你給其中任意一種給我就行”。伺服器端響應頭 content-type 則告知瀏覽器該資源表述的確切媒體類型,如 content-type: image/jpeg 表示它是一張 jpeg 格式的圖片。

另外,和現實世界一樣,Web 上的資源具有集合特性,比如 iphone,並不是指某一個 iphone,而是指 iphone 集合。從中我們得出以下推論:

  1. 用來表示資源的 URI 應該使用名詞複數形式;
  2. 對應集合的包含關係(集合中包含子集合),資源具有層級性;
  3. 集合中的元素具有集合範圍內的唯一標識,通過在 URI 中帶入該唯一標識來定位集合中的元素,如 /iphones/123456。

假設有這樣一個 url:http://www.jd.com/mobiles/iphones/123456

首先這裡體現了資源的層級性:手機是一個大資源集合,其下包含了 iphone 這個子集合,而通過資源標識 123456 定位到某一個 iphone。

那麼,瀏覽器訪問這個 url 時會返回什麼呢?

首先取決於伺服器端決定提供哪些媒體類型,我們假設伺服器端提供了 text/html、application/json、application/xml 和 image/jpeg 類型。

瀏覽器會決定請求什麼類型呢?

如果我們在地址欄輸入該 url,瀏覽器一般會發送如下頭部:accept: text/html,... 要求返回 html 文本。但如果我們在 標簽裡面寫該 url,瀏覽器會發送諸如 accept:image/* 要求返回圖片格式——也就是說,取決於我們在哪裡用這個 url,這是瀏覽器的工作機制,也是 HTML 的魅力所在(後面分析超媒體時再詳細分析)。

你可能會發現,現實中我們見到的多數不是這樣,更可能是這樣:

當要訪問 html 類型時:http://www.jd.com/mobiles/iphone.html?id=123456(或者是編程語言尾碼)

當要訪問圖片時:http://www.jd.com/mobiles/iphones/123456.jpg

現實中,我們不但在 URL 中寫入動詞來表達要進行的操作,還寫入類型尾碼來表達要什麼樣的媒體類型——這兩者都違背了 URI 和 REST 設計初衷,讓 URI 這個標識符同時承擔了操作和媒體類型,對外暴露了設計細節,且該 URI 只能用於極其狹隘的特定場景,違背了可擴展性設計原則(無法給該 URI 擴展更多的操作能力,也無法擴展其表述能力)。

現在我們知道如何定位資源和如何傳遞(展示)資源,接下來的問題是,客戶端如何操作資源呢?客戶端無法通過操作表述(資源副本)改變資源狀態,必須通過和伺服器端交互來實現。

在 HTTP 中是通過幾個通用的動詞來表達客戶端的操作意圖的,最典型的 CRUD,對應 HTTP 動詞(Method)POST、GET、PUT/PATCH、DELETE
狀態轉移:

通過 URL 定位資源,通過 HTTP 動詞操作資源,通過狀態碼表示操作結果——現在大部分聲稱 RESTful API 的也都是做到了且僅做到了這些,大部分分析 REST 的文章也是到此便結束了,但實際上這隻是開始。

相比於資源表述,REST 中更重要的第二部分是狀態轉移。Roy Fielding 提出一個術語叫將超媒體作為應用狀態的引擎(Hypermedia As The Engine Of Application State)。這句話過於拗口,翻譯過來更是難以理解,結果被很多人忽略掉了,但這正是 REST 的精髓。

我們先解釋下這個術語。

超媒體:就是我們再熟悉不過的超鏈接,HTML 標簽中的 a、script、img、link等都屬於超媒體鏈接。

應用狀態:這裡明確指出是應用的狀態而不是資源的。比如上面購物場景中的京東商城就是一個 Web 應用,而應用的狀態則是該應用在某時刻呈現出來的樣子(各個頁面)。

引擎:驅動狀態改變(遷移)的東西,說得白話一點就是京東商城的一個個超鏈接(主要是只 a 標簽鏈接)驅動其從一個頁面切換到另一個頁面。

應用為何要發生狀態轉移?為了完成一個完整的活動,比如上面的購物。應用本質上是一個有限狀態機,其中囊括的一個個活動就是一個個工作流,應用的狀態就是工作流中的節點。我們把上面購物過程畫出來如下(只畫了主流程,實際中會有很多分支流程,比如用戶付款後取消訂單、簽收後退貨等):

李小四購物流程圖

這裡涉及到一次購物活動(一個大的流程圖)中的三個子流程:購物(下單-支付)、查看物流、開發票。每個節點對應應用的一個狀態(也就是頁面,前兩個是京東商城的,後一個是微信的)。

回想一下李小四是怎樣在這些頁面(應用狀態)間跳來跳去的?不停地在地址欄輸入 URL?如果沒有超鏈接(那個小小的 a)恐怕就只能這樣了。如果沒有超鏈接,京東首頁就不是現在這個樣子了,而是一坨長長的 URL 列表,且附上難看的流程圖告訴用戶要想買一部 iPhone 得按照順序依次在地址欄輸入哪些 URL——這是多麼令人崩潰的事情。

所以超鏈接是個偉大的發明,它使資源(的表述)之間建立聯繫,用戶能夠從應用的一個狀態轉移到另一個狀態,進而完成整個工作流。而且,這種轉移是發現式的,即應用的狀態切換不是既定的,一個狀態的下一個狀態可能並不確定,比如李小四打開京東商城首頁後,對某款手錶感興趣,於是點擊其鏈接進入手錶詳情頁——結果買了一款手錶而不是 iPhone。

那麼,資源的表述應用的狀態之間又是什麼關係呢?

應用的狀態就是資源的表述,或者說應用是通過不同的資源表述來展現自己的。應用狀態的轉移就是不同的資源表述之間或者同一個資源的不同狀態的表述之間的轉移。

上面購物流程中,首頁是一個特殊的資源;商品列表、商品詳情是不同層次的商品資源;添加購物車生成新的購物車資源(或者更新購物車資源),從創建購物車到購物車詳情頁屬於購物車資源的不同狀態之間的轉移;下單操作創建了新的訂單資源,支付則產生支付資源,並且在京東商城應用內部產生了一系列新資源比如物流資源;訂單簽收後開具發票則產生了發票資源。

至此我們發現,整個 Web 應用的核心仍然是資源,但既不是某一個資源,也不是某幾個毫無關聯的資源,而是一系列通過超鏈接建立聯繫、能夠形成工作流來完成一系列活動的有機資源池。

在資源的表述中納入超鏈接,讓資源的表述帶有相關資源的 URI,從而讓應用能夠自動進行狀態轉移,這種媒體類型(表述)叫超媒體類型。HTML(XHTML) 是最常見的一種超媒體類型,而且是超媒體文本類型(超文本)。雖然 XHTML 基於 XML,但 XML(以及 JSON)不是超媒體類型,它們的原生語義中不帶有超鏈接,無法從 XML 形式的資源表述進入其它資源表述。

XHTML 之所以是超媒體類型,是它在 XML 基礎上做了語義化(標記)處理,HTML(XHTML)處理器知道,a 標簽表示超鏈接,點擊可以打開新頁面,標簽表示需要從其指向的 URI 獲取圖像格式的資源表述,發起 HTTP 請求時會帶上諸如 accept: image/*(而不是 text/html)的請求頭。

基於 XML 的另一個廣泛使用的超媒體類型是 Atom。

我們也可以基於 XML 和 JSON 來設計自己的超媒體類型嗎?當然可以。比如我們可以定義如下 JSON 格式:

{
	"id": 123,
	"money": 3000.00,
	...
	"links": [{
		"rel": "mydomain/logistics",
		"uri": "https://www.domain.com/v1/logistics/47589"
	}]
}

其中 links 表示相關資源鏈接列表,這裡給出了本訂單相關的物流資源鏈接。該 JSON 是一個超媒體類型,它不但表述了 123 這個訂單資源的信息,還給出了指向相關物流資源的鏈接。一般地,我們還要編寫對應的 JSON Schema,讓其它 JSON 解析器能夠理解我們定義的類型協議。假如我們將該超媒體類型定義為 application/my.hyperproto+json,能夠處理該媒體類型的客戶端發起 HTTP 請求時請求頭帶上 accept:application/my.hyperproto+json,我們伺服器響應時帶上 content-type:application/my.hyperproto+json,雙方便可以自如地你來我往了(這也正是設計 RESTful API 的一個要點,雖然事實上被大部分實現者忽略了)。

現實:

回顧歷史,最早人們並沒有重視 HTTP 動詞和超媒體類型,通過在 URI 中添加動詞和類型尾碼來表達意圖,早期一些瀏覽器和庫甚至不支持除了 GET 和 POST 之外的動詞。URI 被動詞和類型尾碼污染的後果是它不再是“URI”(資源標識),而是操作者意圖傳輸工具,某些角度說,它影響了 URI 的通用性和可擴展性。

還有一種對 HTTP 協議的退化使用是 XML-RPC,通過一個 URL 搞定一切,其他所有的信息都寫在 XML 請求體中——在這裡,HTTP 僅僅被當做傳輸協議而不是應用協議來使用,之
所以使用 HTTP 僅僅是因為它被各種庫廣泛支持,較好地滿足了異構系統環境。

後來,可能是一些流行框架的支持,大家趕時髦式地談論起 RESTful API 起來。這些所謂的 RESTful API 不過是把動詞和類型尾碼從 URI 中拿走了,給 URI“正了名”,重新用起 HTTP Method。他們並沒有用起超媒體特性,HTTP 響應類型僅僅是普通的 XML 或 JSON,資源表述本身不能驅動工作流的行進,使用者仍然需要通過帶外方式(文檔)獲取相關資源 URI。

我想,這可能是 REST 和 HTTP 協議自身特質造成的。

將操作(動作)極度抽象化(通用化)是一項偉大的設計,但“成也蕭何敗也蕭何”。一方面 HTTP 動詞高度抽象化(標準化、通用化),迫使開發者需要絞盡腦汁去把現實世界中成百上千的操作映射到那幾個動詞上——這不是一項簡單的思想活動,同時它還要求開發者需合理的定義“資源”,有些可能是極度抽象的。另一方面,和嚴謹的動詞形成鮮明對比的是 URI(URL)的極度靈活性,開發者可以任意書寫 URL,只要能定位到正確的伺服器,而後便是“我的地盤我做主”,沒有任何硬性約束要求 URL 裡面只能出現名詞。於是為了少死幾個腦細胞,開發人員普遍性地忽略掉 HTTP 動詞(甚至忽略掉了媒體類型協商),把這些信息一股腦全塞入那個“萬能”的 URL 裡面。

使用超媒體的一個困惑是,當我們使用自定義的超媒體類型時,客戶端需要進行額外的解析工作,還不如直接傳遞大家都認識的 JSON 或 XML 來得短平快。

另外,通過超媒體驅動,意味著應用(系統)僅需要對外公佈少數幾個入口 URI,其它 URI 都是通過上游資源表述的超鏈接獲取的。那麼,我們到底要暴露哪些入口 URI 呢?這又是一個需要深入思考的問題,而人都是懶惰的。

不過,REST 給我們設計 API 提供了一些啟示或原則。

  • 在系統的頂層架構上,面向資源而不是操作去規劃系統,能站在全局的視角思考系統構架,讓系統規劃和對外暴露的 API 儘可能趨向穩定。
  • URI 僅僅代表資源,通過 HTTP 動詞規範化操作,能倒逼我們更合理地劃分資源邊界,使得系統更模塊化、層次化。另外,它能讓我們更深層次地思考“資源”,比如登錄,好像是個純動詞,但如果進一步思考,登錄這個行為是為了創建會話,對應的登出則是銷毀會話,因而我們操作的實際上是“會話”(Session)資源。
  • 儘可能使用超媒體類型。通過超鏈接對外暴露 URI 的一個好處是將具體的 URI 細節隱藏起來,比如上面的 JSON 中,客戶端僅關心 rel 的值,然後提取相應的 uri 的值,這裡 rel 是不變的,但 uri 可能會發生變化(比如我們的某個服務外包給第三方了),當 URI變化時,我們無需廣而告之所有的客戶端你要改鏈接哈,否則服務不可用了哈。

總結:

最後我們總結下對 REST 中資源、表述、狀態轉移的理解:

  • 資源是伺服器端的原始數據,比如訂單數據,它是應用的核心。資源通過 URI 對外暴露自身;
  • 在分散式應用中(如 Web),客戶端無法直接觸達資源本身,能觸達的是資源的表述。表述是某種格式的資源副本;
  • 客戶端無法通過修改表述(資源副本)來改變資源本身。伺服器端擁有資源的控制權,它決定可以提供哪些表述給客戶端,也能決定提供什麼樣的操作(動詞);
  • 客戶端通過通用動詞來獲取資源表述以及修改資源狀態;
  • 狀態是指應用的狀態,狀態轉移體現為應用中工作流程的行進(從一個頁面切換到另一個頁面);
  • 狀態轉移是通過超鏈接驅動的。超鏈接由資源的表述攜帶,這種攜帶了超鏈接的表述稱為超媒體;
  • 超媒體使得應用能夠自我驅動狀態轉移(而不需要通過帶外方式);

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 接下來我們來看鏈表題 206. 反轉鏈表反轉一個單鏈表。 示例: 輸入: 1->2->3->4->5->NULL 輸出: 5->4->3->2->1->NULL 解題:鏈表題需要我們設立更多的指針來保存我們當前操作的細節;1.我們需要定義3個指針 pre,cur ,next,pre為當前鏈表的前一個 ...
  • 我們今天繼續研究數組在演算法中的應用 167. 兩數之和 II - 輸入有序數組 給定一個已按照升序排列 的有序數組,找到兩個數使得它們相加之和等於目標數。 函數應該返回這兩個下標值 index1 和 index2,其中 index1 必須小於 index2。 說明: 返回的下標值(index1 和 ...
  • 前端程式員怎麼才能學好演算法呢?目前演算法優秀的視頻集中在c++,java,python,本人通過幾個月專心看c++的視頻掌握了演算法的基本思路,都翻譯成前端代碼一一寫出來,從真題到思維全面提升演算法思維面對演算法面試,不畏懼 二分查找法O(logn)尋找數組中的最大/最小值O(N)歸併排序演算法 O(nlog ...
  • (一)兩種打包表單區別 |屬性 |特點 |應用 | | | | | |get |加到url,直接可見 |書簽,歷史瀏覽 | |post |間接可見,請求發送量多 |私密,訂購,評論,反饋 | (二)三種溯源區別 |屬性 |特點 |應用 | | | | | |url(uniform resource ...
  • 自媒體的發展越來越快,今日頭條旗下的西瓜視頻,火山和抖音在網路上的地位也在發生微妙的變化。包括快手和抖音,微視小視頻平臺的競爭和衝擊也是不容小覷。但是很多用戶還是執著於今日頭條搬運,不離不棄。今天老生常談,跟大家說說,如何利用批量去水印下載西瓜視頻的短視頻。工具不僅僅是支持西瓜的,快手的、抖音的、微 ...
  • 數據類型的分類和判斷 基本(值)類型 Number 任意數值 typeof String 任意字元串 typeof Boolean true/false typeof undefined undefined typeof/ null null 對象(引用)類型 Object typeof/insta ...
  • 《應用框架的設計與實現 .NET 平臺》 [作者] (美) Xin Chen[譯者] (中) 溫昱 靳向陽[出版] 電子工業出版社[版次] 2005年07月 第1版[印次] 2005年07月 第1次 印刷[定價] 39.80元 【第01章】 【應用框架介紹】 (P004) 使用應用框架有五大優點 : ...
  • 前言 本篇文章收錄於專輯:http://dwz.win/HjK,點擊解鎖更多數據結構與演算法的知識。 你好,我是彤哥,一個每天爬二十六層樓還不忘讀源碼的硬核男人。 上一節,我們一起學習了複雜度分析的套路和常見的複雜度。 但是,我們的案例基本都是以時間複雜度為主,很少接觸到空間複雜度。 那麼,到底什麼才 ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 準備開個系列來聊一下 PerfView 這款工具,熟悉我的朋友都知道我喜歡用 WinDbg,這東西雖然很牛,但也不是萬能的,也有一些場景他解決不了或者很難解決,這時候藉助一些其他的工具來輔助,是一個很不錯的主意。 很多朋友喜歡在項目中以記錄日誌的方式來監控項目的流轉情況,其實 CoreCL ...
  • 什麼是工業物聯網網關 工業物聯網網關是連接工業場景本地設備(如PLC、掃碼槍、機器人、數控機床等)與遠端業務系統(如SCADA系統、MES系統等)之間的硬體設備或軟體程式。終端設備和遠端業務系統之間的所有數據通信都通過工業物聯網網關來實現。 我們為什麼需要工業物聯網網關 因為從事汽車工業行業,在我們 ...
  • 在.NET Core的依賴註入框架中,服務註冊的信息將會被封裝成ServiceDescriptor對象,而這些對象都會存儲在IServiceCollection介面類型表示的集合中,另外,IServiceCollection介面類型預設使用的實現類型為ServiceCollection。這樣來看,實 ...
  • 如果業務邏輯比較簡單的話,一條主管道就夠了,確實用不到分支管道。不過當業務邏輯比較複雜的時候,有時候我們可能希望根據情況的不同使用特殊的一組中間件來處理 HttpContext。這種情況下如果只用一條管道,處理起來會非常麻煩和混亂。此時就可以使用 Map/MapWhen/UseWhen 建立一個分支 ...
  • .NET nanoFramework 安裝教程 準備材料​ esp32單片機(支持wifi藍牙) 安卓數據線(需要支持傳輸) 註意!請先安裝esp32驅動程式​ ESP32驅動鏈接 安裝 .NET nanoFramework固件快閃記憶體​ 安裝工具 dotnet tool install -g nano ...
  • Redis是大規模互聯網應用常用的記憶體高速緩存資料庫,它的讀寫速度非常快,據官方 Bench-mark的數據,它讀的速度能到11萬次/秒,寫的速度是8.1萬次/秒。 1. 認識Spring Cache 在很多應用場景中通常是獲取前後相同或更新不頻繁的數據,比如訪問產品信息數據、網頁數據。如果沒有使用 ...
  • 1、應用場景 1.1 kafka場景 ​ Kafka最初是由LinkedIn公司採用Scala語言開發,基於ZooKeeper,現在已經捐獻給了Apache基金會。目前Kafka已經定位為一個分散式流式處理平臺,它以 高吞吐、可持久化、可水平擴展、支持流處理等多種特性而被廣泛應用。 ​ Apache ...
  • Python帶我起飛——入門、進階、商業實戰_ 免費下載地址 內容簡介 · · · · · · 《Python帶我起飛——入門、進階、商業實戰》針對Python 3.5 以上版本,採用“理論+實踐”的形式編寫,通過大量的實例(共42 個),全面而深入地講解“Python 基礎語法”和“Python ...
  • 原文連接:https://www.zhoubotong.site/post/67.html Go 標準庫的net/url包提供的兩個函可以直接檢查URL合法性,不需要手動去正則匹配校驗。 下麵可以直接使用ParseRequestURI()函數解析URL,當然這個只會驗證url格式,至於功能變數名稱是否存在或 ...
  • 多商戶商城系統,也稱為B2B2C(BBC)平臺電商模式多商家商城系統。可以快速幫助企業搭建類似拼多多/京東/天貓/淘寶的綜合商城。 多商戶商城系統支持商家入駐加盟,同時滿足平臺自營、旗艦店等多種經營方式。平臺可以通過收取商家入駐費,訂單交易服務費,提現手續費,簡訊通道費等多手段方式,實現整體盈利。 ...