Webhook 后端是 FaaS(功能即服務)平臺的流行用例。它們可用于許多用例,例如發(fā)送客戶通知以使用有趣的 GIF 進行響應!使用 Serverless 功能,封裝 webhook 功能并以 HTTP 端點的形式公開它非常方便。在本文章中,你將學習如何使用Azure Functions和Go將Slack 應用實現(xiàn)為無服務器后端。您可以通過實施自定義應用程序或工作流來擴展 Slack 平臺并集成服務,這些應用程序或工作流可以訪問平臺的全部范圍,從而允許您在 Slack 中構(gòu)建強大的體驗。
這是Slack的Giphy的更簡單版本。最初的 Giphy Slack 應用程序通過響應多個 GIF 來響應搜索請求。為簡單起見,本文中演示的函數(shù)應用程序僅使用Giphy Random API返回與搜索關鍵字對應的單個(隨機)圖像。這篇博文提供了將應用程序部署到Azure Functions并將其與 Slack 工作區(qū)集成的分步指南。
在這篇文章中,您將:
- 了解Azure Functions 中的自定義處理程序
- 通過簡短的代碼演練了解幕后發(fā)生的事情
- 了解如何使用配置 Azure Functions 和 Slack 設置解決方案
- 當然,在工作區(qū)中運行您的 Slack 應用程序!
后端函數(shù)邏輯是用 Go 編寫的(代碼可在 GitHub 上找到。使用過 Azure Functions 的人可能還記得 Go不是默認支持的語言處理程序之一。這就是自定義處理程序來救援的地方!
什么是自定義處理程序?
簡而言之,自定義處理程序是一個輕量級的 Web 服務器,它接收來自 Functions 主機的事件。在您最喜歡的運行時/語言中實現(xiàn)自定義處理程序唯一需要的是:HTTP 支持!這并不意味著自定義處理程序僅限于HTTP 觸發(fā)器- 您可以通過擴展包自由使用其他觸發(fā)器以及輸入和輸出綁定。
這是自定義處理程序如何在高層工作的摘要(下圖摘自文檔)
事件觸發(fā)器(通過 HTTP、存儲、事件中心等)調(diào)用 Functions 主機。該辦法自定義處理程序從傳統(tǒng)的功能不同的是,該功能的主機充當中間人:它有一個沿發(fā)出請求負載到自定義處理程序(功能)的Web服務器負載包含觸發(fā)器,輸入數(shù)據(jù)綁定,等函數(shù)的元數(shù)據(jù)。該函數(shù)將響應返回給 Functions 主機,該主機將數(shù)據(jù)從響應傳遞到函數(shù)的輸出綁定以進行處理。
概述
在我們深入研究其他領域之前,通過探索代碼(順便說一下相對簡單)可能有助于理解本質(zhì)
應用結(jié)構(gòu)
讓我們看看應用程序是如何設置的。這是在文檔中定義的
java:
├── cmd
│ └── main.go
├── funcy
│ └── function.json
├── go.mod
├── host.json
└── pkg
└── function
├── function.go
├── giphy.go
└── slack.go
該function.json文件位于一個文件夾中,其名稱按照慣例用作函數(shù)名稱。
java:
{
"bindings": [
{
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
host.json通過指向能夠處理 HTTP 事件的 Web 服務器,告訴 Functions 主機將請求發(fā)送到何處。注意 customHandler.description.defaultExecutablePath,它定義了go_funcy將用于運行網(wǎng)絡服務器的可執(zhí)行文件的名稱。"enableForwardingHttpRequest": true確保將原始 HTTP 數(shù)據(jù)發(fā)送到自定義處理程序而無需任何修改。
java:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
},
"customHandler": {
"description": {
"defaultExecutablePath": "go_funcy"
},
"enableForwardingHttpRequest": true
},
"logging": {
"logLevel": {
"default": "Trace"
}
}
}
該cmd和pkg目錄包含圍棋源代碼。讓我們在下一個小節(jié)中探討這一點。
代碼演練
cmd/main.go設置并啟動 HTTP 服務器。請注意,/api/funcy端點是 Function 主機向自定義處理程序 HTTP 服務器發(fā)送請求的端點。
java:
func main() {
port, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
if !exists {
port = "8080"
}
http.HandleFunc("/api/funcy", function.Funcy)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
所有繁重的工作都在function/function.go.
第一部分是讀取請求正文(來自 Slack)并通過基于Slack 定義的配方的簽名驗證過程確保其完整性。
java:
signingSecret := os.Getenv("SLACK_SIGNING_SECRET")
apiKey := os.Getenv("GIPHY_API_KEY")
if signingSecret == "" || apiKey == "" {
http.Error(w, "Failed to process request. Please contact the admin", http.StatusUnauthorized)
return
}
slackTimestamp := r.Header.Get("X-Slack-Request-Timestamp")
b, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to process request", http.StatusBadRequest)
return
}
slackSigningBaseString := "v0:" + slackTimestamp + ":" + string(b)
slackSignature := r.Header.Get("X-Slack-Signature")
if !matchSignature(slackSignature, signingSecret, slackSigningBaseString) {
http.Error(w, "Function was not invoked by Slack", http.StatusForbidden)
return
}
一旦我們確認該函數(shù)確實是通過 Slack 調(diào)用的,下一部分是提取 (Slack) 用戶輸入的搜索詞。
java:
vals, err := parse(b)
if err != nil {
http.Error(w, "Failed to process request", http.StatusBadRequest)
return;
}
giphyTag := vals.Get("text")
通過調(diào)用 GIPHY REST API 使用搜索詞查找 GIF。
java:
giphyResp, err := http.Get("http://api.giphy.com/v1/gifs/random?tag=" + giphyTag + "&api_key=" + apiKey)
if err != nil {
http.Error(w, "Failed to process request", http.StatusFailedDependency)
return
}
resp, err := ioutil.ReadAll(giphyResp.Body)
if err != nil {
http.Error(w, "Failed to process request", http.StatusInternalServerError)
return
}
解組 GIPHY API 發(fā)回的響應,將其轉(zhuǎn)換為 Slack 可以理解的形式,然后返回。就是這樣!
java:
var gr GiphyResponse
json.Unmarshal(resp, &gr)
title := gr.Data.Title
url := gr.Data.Images.Downsized.URL
slackResponse := SlackResponse{Text: slackResponseStaticText, Attachments: []Attachment{{Text: title, ImageURL: url}}}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(slackResponse)
fmt.Println("Sent response to Slack")
matchSignature如果您對檢查簽名驗證過程感興趣,請檢查該函數(shù),并查看 slack.go、giphy.go(在function目錄中)以查看所使用的 Go 結(jié)構(gòu)體表示在各個組件之間交換的信息(JSON)。為了保持這篇文章的簡潔性,這里沒有包含這些內(nèi)容。
好的,到目前為止,我們已經(jīng)介紹了很多理論和背景信息。是時候把事情做好了!在繼續(xù)之前,請確保您滿足以下提到的先決條件。
先決條件
- 如果您還沒有Go,請下載并安裝它。
- 安裝Azure Functions Core Tools ? 這將允許您使用 CLI 部署該函數(shù)(并且還可以在本地運行它進行測試和調(diào)試)。
- 如果沒有,請創(chuàng)建一個 Slack 工作區(qū)。
- 獲取 GIPHY API 密鑰 - 您需要創(chuàng)建一個 GIHPY 帳戶(它是免費的?。┎?chuàng)建一個應用程序。您創(chuàng)建的每個應用程序都有自己的 API 密鑰。
請復制您的 GIPHY API 密鑰,因為您稍后將使用它。
接下來的部分將指導您完成部署 Azure 函數(shù)和為 Slash 命令配置 Slack 的過程。
Azure 函數(shù)設置
首先創(chuàng)建一個資源組來托管解決方案的所有組件。
創(chuàng)建函數(shù)應用
- 首先在 Azure 門戶中搜索Function App,然后單擊添加。
- 輸入所需的詳細信息:您應該選擇自定義處理程序作為運行時堆棧。
- 在Hosting部分,分別為操作系統(tǒng)和計劃類型選擇Linux和Consumption (Serverless)。
- 啟用 Application Insights(如果需要)。
- 查看最終設置并單擊創(chuàng)建以繼續(xù)。
- 該過程完成后,還將與 Function App 一起創(chuàng)建以下資源:
- 應用服務計劃(在這種情況下是消費/無服務器計劃)。
- 一個Azure存儲賬戶。
- 一個Azure應用程序啟示作用。
部署函數(shù)
克隆 GitHub 存儲庫并構(gòu)建函數(shù)。
java:
git clone https://github.com/abhirockzz/serverless-go-slack-app
cd serverless-go-slack-app
GOOS=linux go build -o go_funcy cmd/main.go
GOOS=linux用于構(gòu)建Linux可執(zhí)行文件,因為我們Linux為 Function App選擇了操作系統(tǒng)。
若要部署,請使用 Azure Functions 核心工具 CLI。
java:
func azure functionapp publish <enter name of the function app>
部署后,復制命令 ? 返回的函數(shù) URL,您將在后續(xù)步驟中使用它。
配置松弛
本節(jié)將介紹在工作區(qū)中設置 Slack 應用程序(Slash 命令)所需執(zhí)行的步驟:
- 創(chuàng)建一個 Slack 應用程序。
- 創(chuàng)建斜線命令。
- 將應用程序安裝到您的工作區(qū)。
創(chuàng)建 Slack 應用程序和 Slash 命令
登錄到您的Slack 工作區(qū)并開始創(chuàng)建一個新的 Slack 應用程序。
單擊“創(chuàng)建新命令”以使用所需信息定義新的斜線命令。請注意,請求 URL字段是您將輸入函數(shù)的 HTTP 端點的字段,它只是您在上一節(jié)中部署函數(shù)后獲得的 URL。完成后,點擊保存完成。
將應用程序安裝到您的工作區(qū)
完成 Slash 命令的創(chuàng)建后,前往應用程序的設置頁面,單擊導航菜單中的基本信息功能,選擇將應用程序安裝到工作區(qū), 然后單擊將應用程序安裝到工作區(qū)? 這會將應用程序安裝到您的工作區(qū) 用于測試您的應用程序并生成與 Slack API 交互所需的令牌的 Slack 工作區(qū)。應用程序安裝完成后,應用程序憑據(jù)將顯示在同一頁面上。
記下您的應用簽名密鑰,因為您稍后將使用它。
在你進入有趣的部分之前
確保更新 Function App 配置以添加 Slack Signing Secret ( SLACK_SIGNING_SECRET) 和 Giphy API 密鑰 ( GIPHY_API_KEY) ? 它們將作為函數(shù)內(nèi)的環(huán)境變量可用。
FUN(cy)時間!
我們可以從你的 Slack 工作區(qū),調(diào)用命令/funcy <search term>. 例如嘗試/funcy dog。
你應該拿回一個隨機的 GIF 作為回報!
簡單回顧一下正在發(fā)生的事情:當您/funcy在 Slack 中調(diào)用命令時,它會調(diào)用該函數(shù),然后與 Giphy API 交互并最終將 GIF 返回給用戶(如果一切順利)。
您可能會timeout error在第一次調(diào)用后從 Slack 中看到。這很可能是因為cold start該函數(shù)在第一次調(diào)用時需要幾秒鐘的時間來引導。這與Slack 期望在 3 秒內(nèi)得到響應的事實相結(jié)合,給出了錯誤消息。
沒有什么可擔心的。您所需要的只是重試,一切都會好起來的!
清理
當我們在完成后這個測試之后不要忘記刪除資源組,就是將刪除之前創(chuàng)建的所有資源(功能應用、應用服務計劃等)。
結(jié)論
沒有什么能阻止您在 Azure 上使用 Go 實現(xiàn)無服務器功能!我希望這是一種嘗試自定義處理程序的有趣方式。