Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
文最初發(fā)表于STX Next博客網(wǎng)站,經(jīng)原作者 Sebastian Buczyński 同意由 InfoQ 中文站翻譯分享。
現(xiàn)在,每個(gè)人都在關(guān)注 API。API 最早開(kāi)始流行于大約 20 年前,2000 年,Roy Fielding 在他的博士論文中首次提出了 REST 這個(gè)術(shù)語(yǔ)。同年,Amazon、Salesforce 和 eBay 向全世界的開(kāi)發(fā)者介紹了他們的 API,永遠(yuǎn)改變了我們構(gòu)建軟件的方式。
在 REST 之前,Roy Fielding 論文中的原則被稱為“HTTP 對(duì)象模型”,隨后你會(huì)明白這為何非常重要。
隨著閱讀的深入,你還會(huì)看到如何確定你的 API 是否成熟,好 API 的主要品質(zhì)是什么以及為何在構(gòu)建 API 的時(shí)候,要注重適應(yīng)性。
REST 代表表述性狀態(tài)轉(zhuǎn)移(Representational State Transfer),由 Roy Fielding 在他的博士論文中定義,長(zhǎng)期以來(lái),它就是服務(wù) API 的圣杯。它并不是構(gòu)建 API 的唯一方式,但是由于它的流行,即便是非開(kāi)發(fā)人員也知道這種標(biāo)準(zhǔn)。
RESTful 軟件有如下六種特點(diǎn):
但是,對(duì)日常使用來(lái)說(shuō),這過(guò)于理論化了。我們需要更具操作性的東西,這也就是 API 成熟度模型。
該模型是由 Leonard Richardson 提出的,它將 RESTful 開(kāi)發(fā)原則結(jié)合成四個(gè)簡(jiǎn)單易行的步驟。
在模型中的位置越高,就越接近 Roy Fielding 所定義的 RESTful 原始理念。
Level 0 的 API 是一組簡(jiǎn)單 XML 或 JSON 的描述。在前文中,我曾經(jīng)說(shuō)過(guò)在 Fielding 的論文之前,RESTful 原則被稱為“HTTP 對(duì)象模型”。
這是因?yàn)?HTTP 是 RESTful 開(kāi)發(fā)中最重要的組成部分。REST 要盡可能多地使用 HTTP 固有屬性中的理念。
在 Level 0,沒(méi)有使用任何這樣的東西。我們只是構(gòu)建自己的協(xié)議并把它作為一個(gè)專有層。這種架構(gòu)被稱為遠(yuǎn)程過(guò)程調(diào)用(Remote Procedure Call,RPC),適用于遠(yuǎn)程過(guò)程/命令。
通常我們會(huì)有一個(gè)端點(diǎn),可以對(duì)它進(jìn)行調(diào)用以獲取一堆 XML。在這方面,一個(gè)典型的例子就是 SOAP 協(xié)議:
另外一個(gè)很好的例子就是 Slack API。它有些多樣化,有多個(gè)端點(diǎn),但依然是 RPC 風(fēng)格的 API。它暴露了 Slack 的各種功能,中間沒(méi)有附加任何特性。如下的代碼展示了如何向一個(gè)特定的通道發(fā)送消息:
雖然按照 Richardson 的模型,這是一個(gè) Level 0 的 API,但是這并不意味著它是不好的。只要它是可用的,并且恰當(dāng)?shù)胤?wù)于業(yè)務(wù)需求,那它就是很棒的 API。
為了構(gòu)建 Level 1 的 API,我們需要找出系統(tǒng)中的名詞并將它們通過(guò)不同的 URL 暴露出來(lái),如下面的樣例所示:
其中,“/api/books”能讓我訪問(wèn)一個(gè)通用的圖書(shū)目錄,“/api/profile”能夠讓我訪問(wèn)這些書(shū)的作者的基本信息。為了獲取某個(gè)資源的第一個(gè)特定實(shí)例,我可以在 URL 中添加 ID(或其他引用)。
在 URL 中還可以嵌套資源,這展示了它們是以層級(jí)結(jié)構(gòu)的形式組織的。
回到 Slack 的樣例,如下展示了按照 Level 1 API,它們會(huì)是什么樣子的:
現(xiàn)在,URL 發(fā)生了變化,從原先的“/api/chat.postMessage”變成了現(xiàn)在的“/api/channels/general/messages”。
信息中“channel”部分從請(qǐng)求體轉(zhuǎn)移到了 URL 中。從字面就能看出,通過(guò)使用這個(gè) URL,我們可以預(yù)期有條消息發(fā)布到了“general”通道上。
Level 2 利用 HTTP 動(dòng)作(verb)來(lái)添加更多的含義和意圖。在這方面可用的動(dòng)作比較多,我這里只用到一個(gè)基礎(chǔ)的子集:PUT / DELETE / GET / POST。
借助這些動(dòng)作,我們可以預(yù)期包含它們的 URL 有不同的行為:
以上面提到的“/api/books”為例:
那這里的“安全”和“冪等”又是什么意思呢?
“安全”的方法指的是永遠(yuǎn)不會(huì)改變數(shù)據(jù)的方法。REST 建議 GET 方法只能用來(lái)獲取數(shù)據(jù),所以在上面的集合中,它是唯一一個(gè)安全的方法。不管你調(diào)用多少次基于 REST 的 GET 方法,它永遠(yuǎn)不會(huì)改變數(shù)據(jù)庫(kù)中的任何東西。但是,這并不是該動(dòng)作的固有特性,而是關(guān)系到你該如何實(shí)現(xiàn)它,所以我們需要確保它是這樣運(yùn)行的。所有其他的方法都會(huì)以不同的方式改變數(shù)據(jù),不能隨意使用。在 REST 中,GET 方法既是安全的,又是冪等的。
“冪等”的方法指的是多次使用不會(huì)產(chǎn)生不同結(jié)果的方法。按照 REST,DELETE 方法應(yīng)該是冪等的,如果刪除了某個(gè)資源,然后針對(duì)相同的資源再次調(diào)用 DELETE,它不會(huì)改變?nèi)魏螙|西。資源應(yīng)該早就已經(jīng)消失了。在 REST 規(guī)范中,POST 是唯一一個(gè)非冪等的方法,所以我們可以對(duì)相同的資源多次調(diào)用 POST 方法,這樣我們會(huì)得到重復(fù)的資源。
我們重新看一下 Slack 樣例,如果我們使用 HTTP 動(dòng)作來(lái)進(jìn)行更多的操作會(huì)是什么樣子:
我們可以使用 POST 方法發(fā)送消息到通用的通道,我們也可以使用 GET 方法從通用通道獲取消息。我們還可以使用 DELETE 方法和特定的 ID 刪除消息,這里比較有意思的一點(diǎn)在于,消息并不是與特定通道關(guān)聯(lián)的,所以我可以設(shè)計(jì)一個(gè)單獨(dú)的 API 來(lái)刪除資源。這個(gè)例子表明,設(shè)計(jì) API 并不總是那么簡(jiǎn)單,這方面有很多可選項(xiàng)和權(quán)衡。
還記得純文字、沒(méi)有任何圖像的電腦游戲嗎?我們只能看到一些文本,描述了你在哪里,以及接下來(lái)能干什么。為了取得進(jìn)展,我們必須要輸入自己的選擇。在一定程度上來(lái)講,HATEOAS 就是做這件事情的。
HATEOAS 指的是“超媒體作為應(yīng)用狀態(tài)引擎(Hypermedia as the Engine of Application State)”。
有了 HATEOAS 之后,當(dāng)其他人使用你的 API 的時(shí)候,他們就能看到通過(guò) API 還能做哪些其他的事情。HATEOAS 回答了“從這里出發(fā),我還能去哪里?”的問(wèn)題。
但這還不是所有的內(nèi)容。HATEOAS 還可以對(duì)數(shù)據(jù)關(guān)系進(jìn)行建模。我們可能會(huì)有一個(gè)關(guān)于圖書(shū)的資源,并且在 URL 中沒(méi)有將作者信息嵌套進(jìn)來(lái),但是我們可以包含它們的鏈接,如果有人對(duì)作者感興趣的話,那么他們可以訪問(wèn)這些鏈接并探索相關(guān)的數(shù)據(jù)。
HATEOAS 不像其他成熟度模型的等級(jí)那樣流行,但是有些開(kāi)發(fā)人員確實(shí)在使用它。其中一個(gè)樣例就是 Jira,如下是它們的搜索 API 的響應(yīng):
他們將鏈接嵌入到了其他我們可以探索的資源中,以及該 issue 的狀態(tài)過(guò)渡列表。
另外一個(gè)使用 HATEOAS 的樣例是 Artsy。他們的 API 嚴(yán)重依賴 HATEOAS,并且還使用了 JSON Plus 調(diào)用規(guī)范,按照該規(guī)范強(qiáng)制要求使用一種特殊的約定來(lái)構(gòu)建鏈接。下面是一個(gè)分頁(yè)的例子,這是使用 HATEOAS 最酷的樣例之一:
我們可以提供到下一頁(yè)、上一頁(yè)、第一頁(yè)和最后一頁(yè)的鏈接,還可以按照需要添加其他頁(yè)面的鏈接。這樣簡(jiǎn)化了 API 的消費(fèi),因?yàn)檫@樣不需要在客戶端添加 URL 的解析邏輯,也不需要追加頁(yè)碼的方法。我們只需要在客戶端使用已經(jīng)實(shí)現(xiàn)結(jié)構(gòu)化的鏈接就可以了。
我們已經(jīng)介紹完了 Richardson 模型,但這并不是實(shí)現(xiàn)好的 API 的全部?jī)?nèi)容。其他重要的品質(zhì)還有什么呢?
我對(duì)自己使用的 API 的基本期望之一就是,需要有一種明確的方式來(lái)判斷是否有錯(cuò)誤或異常。我想要知道請(qǐng)求是否得到了處理。
HTTP 有一種簡(jiǎn)單的方式來(lái)實(shí)現(xiàn)這一點(diǎn):HTTP 狀態(tài)碼。
管理狀態(tài)碼的基本規(guī)則是:
我們的 API 至少要提供 4xx 和 5xx 狀態(tài)碼。有時(shí)候,5xx 是自動(dòng)生成的。例如,客戶端發(fā)送了一些內(nèi)容到服務(wù)器端,但是這非法的請(qǐng)求,而我們的校驗(yàn)是有缺陷的,從而導(dǎo)致這個(gè)問(wèn)題繼續(xù)在代碼中執(zhí)行了下去,最終導(dǎo)致出現(xiàn)了異常,這樣就會(huì)返回一個(gè) 5xx 的狀態(tài)碼。
如果你想要承諾使用特定的狀態(tài)碼,那么你會(huì)遇到“哪種狀態(tài)碼最適合當(dāng)前情況?”的問(wèn)題。這樣的問(wèn)題并不總是那么容易回答,我推薦你去閱讀聲明這些狀態(tài)碼的 RFC,它們給出了比其他來(lái)源更廣泛的解釋,并且告訴了你何時(shí)使用這些狀態(tài)碼更合適等。幸運(yùn)的是,網(wǎng)上有些資源可以幫助我們做出選擇,比如Mozilla的HTTP狀態(tài)碼指南。
優(yōu)秀的 API 必須要有優(yōu)秀的文檔。在文檔方面,最大的問(wèn)題在于,隨著 API 的發(fā)展需要找人同步更新文檔。有個(gè)更好的方案是不脫離代碼自更新文檔。
例如,注釋與代碼的脫節(jié)。當(dāng)代碼發(fā)生變化的時(shí)候,注釋依然保持不變,這樣的話,注釋就過(guò)時(shí)了。這甚至?xí)雀揪蜎](méi)有任何注釋更糟糕,因?yàn)樵陔S后的一段時(shí)間內(nèi),它們會(huì)提供錯(cuò)誤的信息。注釋不會(huì)自動(dòng)更新,所以開(kāi)發(fā)人員需要記得在維護(hù)代碼的時(shí)候同時(shí)維護(hù)它們。
自更新的文檔工具可以解決這個(gè)問(wèn)題。在這方面,一個(gè)流行的工具就是 Swagger,它是基于 OpenAPI 構(gòu)建的工具,可以很容易地描述你的 API。
Swagger 很酷的一點(diǎn)在于它是可執(zhí)行的,所以如果你嘗試修改 API,能立即看到它的作用和變化。
為了給 Swagger 添加自動(dòng)更新功能,我們需要使用其他的插件和工具。在 Python 中,有針對(duì)大多數(shù)主流框架的插件。它們能生成 API 請(qǐng)求該如何組織的描述,并定義數(shù)據(jù)的輸入和輸出。
如果你不想要使用 Swagger,而是想使用更簡(jiǎn)單的工具,那該怎么辦呢?有個(gè)流行的替代方案是Slate。
還有一些值得推薦的中間方案,如widdershins和api2html的組合,它允許我們從 Swagger 的定義中生成類似 Slate 的文檔。
在有些系統(tǒng)中,緩存可能并不是什么大問(wèn)題。這樣的系統(tǒng)可能沒(méi)有很多的數(shù)據(jù)可供緩存,所有的數(shù)據(jù)都在不斷地發(fā)生變化,或者系統(tǒng)根本沒(méi)有很大的流量。
但是,在大多數(shù)情況下,緩存對(duì)于良好的性能至關(guān)重要。它與 RESTful API 密切相關(guān),因?yàn)?HTTP 協(xié)議在緩存方面做了很多事情,比如 HTTP 頭信息允許我們控制緩存的行為。
你可能想要在客戶端緩存東西,或者如果有注冊(cè)表或值存儲(chǔ)的話,那么你可能想要在應(yīng)用程序中緩存數(shù)據(jù)。但是,HTTP 讓我們能夠基本上免費(fèi)就可以獲得一個(gè)很好的緩存,所以如果可能的話,請(qǐng)不要錯(cuò)過(guò)這個(gè)免費(fèi)的午餐。
同時(shí),因?yàn)榫彺媸?HTTP 規(guī)范的一部分,所以很多涉及 HTTP 的技術(shù)都知道如何進(jìn)行緩存:瀏覽器原生支持緩存,客戶端和服務(wù)器之間的中間技術(shù)也是如此。
構(gòu)建 API 以及現(xiàn)代軟件最重要的部分就是適應(yīng)性。如果沒(méi)有適應(yīng)性,開(kāi)發(fā)就會(huì)變慢,在合理的時(shí)間發(fā)布特性就會(huì)變得更加困難,當(dāng)面對(duì)最后截止時(shí)間的時(shí)候更是如此。
“軟件架構(gòu)”在不同的上下文語(yǔ)境中有不同的含義,不過(guò)我們現(xiàn)在采用這個(gè)定義:
軟件架構(gòu)一種行為/藝術(shù),能夠避免會(huì)阻礙未來(lái)變化的決策。
記住了這一點(diǎn),在設(shè)計(jì)軟件的時(shí)候,當(dāng)你必須要在具有相似優(yōu)點(diǎn)的方案中做出選擇時(shí),你應(yīng)該始終選擇更多考慮到未來(lái)的方案。
好的實(shí)踐并不是萬(wàn)能的。按照正確的方式構(gòu)建錯(cuò)誤的東西并不是你想要的結(jié)果。最好采取一種成長(zhǎng)的心態(tài),接受變化是不可避免的,尤其是如果你的項(xiàng)目要持續(xù)發(fā)展的話更是如此。
要想讓你的 API 更具適應(yīng)性,其中很關(guān)鍵的一點(diǎn)就是保持盡可能薄的 API 層,真正的復(fù)雜性應(yīng)該往下層轉(zhuǎn)移。
公開(kāi)的 API 發(fā)布之后,它就已經(jīng)完成了,是不可改變的,你就不能再去觸碰它了。如果你已經(jīng)有了一個(gè)設(shè)計(jì)古怪的 API,除了接受現(xiàn)狀之外,還能做些什么呢?
你應(yīng)該不斷尋找簡(jiǎn)化實(shí)現(xiàn)的方法。有時(shí)候,你可以通過(guò)一個(gè)特定的 HTTP 頭信息來(lái)控制 API 響應(yīng)的格式,相對(duì)于構(gòu)建另外一個(gè)叫做 v2 的新 API,這是一種更簡(jiǎn)單的解決方案。
API 只是另外一層的抽象。它們不應(yīng)該決定如何實(shí)現(xiàn),為了避免這種問(wèn)題,我們可以采用如下幾種開(kāi)發(fā)模式。
這是一種類似于門(mén)面的開(kāi)發(fā)模式。如果你要把一個(gè)單體結(jié)構(gòu)拆分為一組微服務(wù),并且希望向外部暴露一些功能的話,那么你只需要構(gòu)建一個(gè)類似門(mén)面的 API 網(wǎng)關(guān)。
它將為不同的微服務(wù)提供一個(gè)統(tǒng)一的接口(這些微服務(wù)可能有不同的 API,使用不同的錯(cuò)誤格式等等)。
如果你必須要構(gòu)建一個(gè) API 來(lái)滿足一堆不同的客戶端的話,那么這可能會(huì)非常困難。針對(duì)某個(gè)客戶端所作出的決策可能會(huì)影響其他客戶端的功能。
按照適用于前端的后端(backend for frontend)理念,如果你有不同的客戶端,它們喜歡不同形式的 API,比如移動(dòng)應(yīng)用可能會(huì)喜歡使用 GraphQL,那么就單獨(dú)為它們構(gòu)建吧。
只有當(dāng)你的 API 是一層抽象,并且這個(gè)抽象層很薄的時(shí)候,這種方式才有效。如果它與你的數(shù)據(jù)庫(kù)耦合,或者太大,具有太多的邏輯,那么就無(wú)法這樣做了。
很多人都在熱炒 GraphQL。它是一項(xiàng)新興的技術(shù),但是已經(jīng)有了很多粉絲,以至于有些開(kāi)發(fā)者聲稱它將取代 REST。
盡管 GraphQL 比 RESTful 要新的多,但是它們有很多相似之處。GraphQL 最大的不足之處在于它的緩存,它必須要在客戶端或應(yīng)用程序中實(shí)現(xiàn)。現(xiàn)在,有內(nèi)置的實(shí)現(xiàn)了緩存功能的客戶端庫(kù)(比如Apollo),但是這仍然要比使用 HTTP 提供的幾乎免費(fèi)的緩存功能要困難。
從技術(shù)講,GraphQL 位于 Richardson 模型的 Level 0 層級(jí),但是它具有良好 API 的特質(zhì)。我們可能無(wú)法同時(shí)使用多個(gè) HTTP 的功能,但是 GraphQL 的出現(xiàn)就是解決這一問(wèn)題的。
GraphQL 的殺手锏就是聚合不同的 API,并將它們作為一個(gè) GraphQL API 暴露出來(lái)。
GraphQL 在處理數(shù)據(jù)抓取不足和數(shù)據(jù)過(guò)量抓取方面有很好的效果,而這些問(wèn)題是 REST API 很難進(jìn)行管理的。這兩個(gè)問(wèn)題都與性能有關(guān),如果數(shù)據(jù)抓取不足,那說(shuō)明你沒(méi)有高效地使用 API,所以必須要進(jìn)行大量的調(diào)用。如果數(shù)據(jù)過(guò)量抓取的話,那么 API 調(diào)用的數(shù)據(jù)傳輸會(huì)比必要的數(shù)據(jù)傳輸更大,這是對(duì)帶寬的一種浪費(fèi)。
借助 REST 與 GraphQL 的比較,我們能夠總結(jié)出一個(gè)好的 API 最重要的品質(zhì)。
好的 API 的特性
我們需要一個(gè)清晰的數(shù)據(jù)表述方式:RESTful 以資源的方式提供了表述。我們需要有一種方式顯示有哪些可用的操作:RESTful 通過(guò)組合資源和 HTTP 動(dòng)作實(shí)現(xiàn)這一點(diǎn)。我們需要有一種方式來(lái)確認(rèn)是否存在錯(cuò)誤/異常:HTTP 狀態(tài)碼可以實(shí)現(xiàn)這一點(diǎn),可能還會(huì)包含闡述它們的響應(yīng)信息。最好能夠提供 API 發(fā)現(xiàn)和導(dǎo)航的功能:在 RESTful 中,HATEOAS 負(fù)責(zé)實(shí)現(xiàn)這一點(diǎn)。有好的文檔是非常重要的:在這方面,可執(zhí)行、自更新的文檔可以解決這個(gè)問(wèn)題,這超出了 RESTful 規(guī)范的范圍。最后,但同樣重要的是,優(yōu)秀的 API 應(yīng)該具有緩存功能,除非你的特定情況認(rèn)為它是不必要的。
REST 和 GraphQL 之間最大的區(qū)別是它們處理緩存性的方式。當(dāng)我們使用 REST 方式構(gòu)建 API 的時(shí)候,我們基本上可以免費(fèi)獲得 HTTP 的緩存功能。如果選擇 GraphQL 的話,你需要自行負(fù)責(zé)為客戶端或應(yīng)用程序添加緩存。
原文鏈接:
https://www.stxnext.com/blog/how-to-build-a-good-api-that-wont-embarrass-you
延伸閱讀:
關(guān)注我并轉(zhuǎn)發(fā)此篇文章,即可獲得學(xué)習(xí)資料~若想了解更多,也可移步InfoQ官網(wǎng),獲取InfoQ最新資訊~
Excel VBA中可以引用Excel工作表函數(shù),也可以引用Excel VBA函數(shù),還可以引用用戶自己定義的函數(shù)或過(guò)程。除此以外,還可以調(diào)用Windows API。
Windows API包括幾千個(gè)可調(diào)用的函數(shù),這些函數(shù)是Windows提供給應(yīng)用程序與操作系統(tǒng)的接口,他們猶如“積木塊”一樣,可以搭建出各種界面豐富,功能靈活的應(yīng)用程序。它們大致可以分為以下幾個(gè)大類:
基本服務(wù); 組件服務(wù); 用戶界面服務(wù); 圖形多媒體服務(wù); 消息和協(xié)作; 網(wǎng)絡(luò); Web服務(wù)。
Windows API使用 .lib 文件。.lib 文件也就是庫(kù)文件,分為靜態(tài)庫(kù)文件和動(dòng)態(tài)庫(kù)文件(DLL)
靜態(tài)鏈接就是把靜態(tài)鏈接庫(kù)中的函數(shù)直接復(fù)制到程序中,成為程序的一部分。
動(dòng)態(tài)鏈接是指在程序運(yùn)行時(shí)將已駐留在內(nèi)存中的動(dòng)態(tài)鏈接庫(kù)中的函數(shù)在需要時(shí)鏈接起來(lái)一起運(yùn)行。
API 的 dll 在 windows 系統(tǒng)的 system32 目錄下, 圖形界面相關(guān)的 API 在 USER32.dll 里,進(jìn)程、文件之類的操作在 kernel32.dll 里。MSDN 的每個(gè)函數(shù)都會(huì)說(shuō)明它在哪個(gè)頭文件, 哪個(gè) lib, 哪個(gè) dll 里的。
動(dòng)態(tài)鏈接庫(kù)中的函數(shù)可以被VBA調(diào)用,下面就以一個(gè)實(shí)例來(lái)說(shuō)明。
在kernel32.dll中有一個(gè) GetSystemDirectory()函數(shù),可以到到Windows的系統(tǒng)路徑。以下就是通過(guò)一個(gè)過(guò)程來(lái)調(diào)用這個(gè)函數(shù)的實(shí)例:
Private Declare Function GetSystemDirectory Lib "kernel32" Alias "GetSystemDirectoryA" _ (ByVal lpBuffer As String, ByVal nSize As Long) As Long Sub info() Dim sPath As String * 260, lLen As Long lLen=GetSystemDirectory(sPath, 260) Text3=Left(sPath, lLen) VBA.MsgBox (Text3) End Sub
效果如下:
1.1查詢?nèi)绾蝹鬟f參數(shù)到DLL函數(shù)的Declare語(yǔ)句格式
為了調(diào)用Windows API中的函數(shù),需要描述這些可用的函數(shù)的文檔規(guī)范,如何在VBA中聲明這些函數(shù),以及如何調(diào)用它們。下面是兩個(gè)有用的資源:
I 可以使用API Viewer加載宏查找和復(fù)制需要的Declare語(yǔ)句。可以在下面的站點(diǎn)下載API聲明查看器:
http://www.activevb.de/rubriken/apiviewer/index-apiviewereng.html
登錄以下頁(yè)面后在以下點(diǎn)擊下載:
安裝后打開(kāi)需要的文件:
即可查看可以使用的API及聲明:
II Microsoft Platform SDK,包含復(fù)雜的Windows API文檔。可以在下面的地址中查看:http://www.office-cn.net/t/api/index.html?apihelp.htm
可以使用的API函數(shù)類別:
1.2 使用Declare語(yǔ)句
在從VBA中調(diào)用DLL里的函數(shù)之前,必須為VBA提供在哪里找到函數(shù)以及如何調(diào)用該函數(shù)的信息:
Declare語(yǔ)句是一個(gè)定義,告訴VBA在哪里找到特定的DLL函數(shù)以及如何調(diào)用該函數(shù)。在代碼中添加Declare語(yǔ)句最簡(jiǎn)單的辦法是使用API Viewer加載宏,其中包含Windows API中大多數(shù)函數(shù)的Declare語(yǔ)句,也包含一些函數(shù)所需要的常量和類型定義。
Declare語(yǔ)句聲明的形式如下:
[Public|Private]Declare Sub name Lib "libname" [Alias "aliasname"][([arglist])] [Public|Private]Declare Function name Lib "libname" [Alias "aliasname"] [([arglist])] [As type]
下面是GetTempPath函數(shù)的Declare語(yǔ)句的示例,該函數(shù)返回Windows臨時(shí)文件夾的路徑(默認(rèn)為C:\Windows\Temp):
Private Declare Function GetTempPath Lib "kernel32" _ Alias "GetTempPathA" (ByVal nBufferLength As Long, _ ByVal lpBuffer As String) As Long
I 關(guān)鍵字Declare告訴VBA在工程中要包含的DLL函數(shù)的定義。在標(biāo)準(zhǔn)模塊中的Declare語(yǔ)句可以是公共的或私有的,取決于你希望API函數(shù)僅用于單個(gè)模塊還是整個(gè)工程。在類模塊中,Declare語(yǔ)句必須是私有的。
II 在關(guān)鍵字Function之后是函數(shù)的名字,具體地說(shuō),是從VBA中調(diào)用該函數(shù)時(shí)使用的名字。這個(gè)名字可以與API函數(shù)本身的名字相同,也可以在Declare語(yǔ)句中使用關(guān)鍵字Alias指定打算在VBA中通過(guò)不同的名字(別名)調(diào)用該函數(shù)。
III 在上面的示例中,在DLL中API函數(shù)的名字是GetTempPathA,從VBA中調(diào)用該函數(shù)時(shí)使用的名字是GetTempPath。注意,DLL函數(shù)的實(shí)際名字出現(xiàn)在關(guān)鍵字Alias之后,同時(shí)也注意到GetTempPath是Win32API.txt文件用于該函數(shù)的別名(有充分必要使用別名),但你可以將其改變?yōu)槿魏文阆胍拿帧?/p>
IV 關(guān)鍵字Lib指定包含函數(shù)的DLL。
1.3 在過(guò)程中調(diào)用API函數(shù)。
有如下任務(wù):判斷一個(gè)文件框如果內(nèi)容為空,則顯示一個(gè)提示文本框進(jìn)行提示,并在3秒后自動(dòng)消息。
我們知道,VBA提供以下代碼的提示對(duì)話框,但它需要用戶自己點(diǎn)擊關(guān)閉按鈕才可以關(guān)閉,不能滿足我們的需要。
MsgBox ("提示:編號(hào)不能為空!")
2.1 插入用戶窗體,并添加兩個(gè)文本框,如下:
2.2 在VBE中插入模塊,并添加以下代碼:
Private Declare Function MsgBoxTimeout Lib "user32" _ Alias "MessageBoxTimeoutA" ( _ ByVal hwnd As Long, _ ByVal lpText As String, _ ByVal lpCaption As String, _ ByVal wType As VbMsgBoxStyle, _ ByVal wlange As Long, _ ByVal dwTimeout As Long) _ As Long Sub PopupMsgbox(Optional prompt As String="OK", Optional title As String="友情提示", Optional seconds As Long=300) MsgBoxTimeOut 0, prompt, title, 64, 0, seconds End Sub
2.3 為文本框添加事件代碼:
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean) If TextBox1.Value="" Then 'MsgBox ("提示:編號(hào)不能為空!") PopupMsgbox "編號(hào)不能為空!", "提示", 1500 '1.5秒后提示窗口自動(dòng)關(guān)閉 End If End Sub
2.4 當(dāng)文本框?yàn)榭眨髽?biāo)光標(biāo)點(diǎn)擊到文本框以外的其它位置時(shí),就會(huì)彈出提示對(duì)話框,并在1.5秒后自動(dòng)關(guān)閉,如下:
MsgBoxTimeout()是一個(gè)未公開(kāi)的 API 函數(shù)。
此函數(shù)的參數(shù)如下:
總結(jié)上例使用API的流程:
I Declare MsgBoxTimeOut
II 定義PopupMsgbox過(guò)程,在此過(guò)程中調(diào)用了MsgBoxTimeOut
III 在其他過(guò)程中調(diào)用PopupMsgbox()
也可以聲明后直接調(diào)用:
Dim i As Long i=MsgBoxTimeout(0, "編號(hào)不能為空!", "提示", vbYesNo, 0, 1500)
-End-
束驗(yàn)證 DOM 方法
Property | Description |
---|---|
checkValidity() | 如果 input 元素中的數(shù)據(jù)是合法的返回 true,否則返回 false。 |
setCustomValidity() | 設(shè)置 input 元素的 validationMessage 屬性,用于自定義錯(cuò)誤提示信息的方法。使用 setCustomValidity 設(shè)置了自定義提示后,validity.customError 就會(huì)變成true,則 checkValidity 總是會(huì)返回false。如果要重新判斷需要取消自定義提示,方式如下:setCustomValidity('') setCustomValidity(null) setCustomValidity(undefined) |
以下實(shí)例如果輸入信息不合法,則返回錯(cuò)誤信息:
checkValidity() 方法
<inputid="id1"type="number"min="100"max="300"required><buttononclick="myFunction()">驗(yàn)證</button><pid="demo"></p><script>
functionmyFunction(){varinpObj=document.getElementById("id1"); if(inpObj.checkValidity()==false){document.getElementById("demo").innerHTML=inpObj.validationMessage; }}
</script>
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。