將資料視覺化發佈到網路上

與網路上友善的使用者分享獨立圖表和儀表板。

在本教學課程中,我們將向您展示發佈 Metabase 圖表和儀表板的幾種選項,從最低到最高的努力程度。

我們將從無需程式碼的公開連結開始,逐步升級到僅需單一程式碼片段的公開嵌入,最後提供範例程式碼給想要啟動自己的 Web 應用程式的使用者。

如果您想查看何時以及為什麼選擇一種發佈方式而不是另一種方式的一些範例,請查看Metabase 謎團

為了快速分享問題和儀表板,您可以簡單地傳送公開連結,或將 iframe 放入您的網站(或任何可以呈現 HTML 的內容)。這是隨時隨地分享圖表和儀表板的好方法。

分享選項

假設我們想要分享儀表板。我們將點擊分享圖示,然後選取分享與嵌入選項。

An example dashboard using the Sample Database included with Metabase. To access the sharing options, click on the arrow in the upper right and select Sharing and embedding.

這將顯示我們的分享選項

Sharing options include: Public link, Public embed, and Embed this dashboard in an Application (a static embed).

公開連結是分享儀表板最簡單的方式。公開連結甚至不是嵌入;它們只是單一問題或儀表板的連結,儘管這些公開項目與其原始版本略有不同。

An example of using a public link to share a dashboard, with the Powered by Metabase footer.

這些公開連結將包含 Powered by Metabase 頁尾,如果您有 Pro 或 Enterprise 方案,您可以移除此頁尾。圖表也將停用鑽取功能,而且我們將無法自訂儀表板上的點擊行為

如果我們設定了預設篩選條件值,Metabase 會將該篩選條件套用至問題或儀表板。使用者將可以變更篩選條件,因此我們無法依賴篩選條件來限制使用者可以查看哪些資料。若要鎖定(或隱藏)篩選條件,我們需要使用靜態嵌入

我們也可以格式化 URL 以將值指派給篩選條件,或完全隱藏篩選條件,但請記住,收件者可以輕鬆編輯 URL。

雖然這些公開連結很難猜到,但任何擁有該連結的人都可以檢視您的儀表板,因此這不是分享敏感資料的最佳解決方案。不過,我們可以快速與客戶等分享儀表板的公開連結,然後在他們看過後停用它。如果我們再次分享儀表板,Metabase 將產生新的連結(因此我們不必擔心舊連結的存取權)。如果您不小心分享了連結,您可以隨時停用它;只需將分享切換為關閉即可。管理員可以從管理面板檢視和停用所有公開連結。

公開嵌入

我們可以透過 iframe 將問題或儀表板嵌入到您的網站中。這就像從您的 Metabase 複製並貼上程式碼,然後將其放入網頁的原始碼中一樣簡單。我們甚至可以將其與無需程式碼的網站建立工具搭配使用 - 任何您可以放入 HTML 的地方。例如,您可以將儀表板嵌入到部落格中,以協助使用資料說故事,或只是用儀表板填滿整個頁面

以下是用於顯示儀表板的 iframe

<iframe
  src="http://your-website.com/public/dashboard/f54f5ae5-39a4-4662-85d5-a02e78165279"
  frameborder="0"
  width="800"
  height="600"
  allowtransparency
></iframe>

iframe 會在目前的瀏覽器視窗內建立另一個巢狀的瀏覽器視窗。iframe 視窗指向自己的 URL,並呈現來自該位址的回應 - 在此案例中,是我們想要呈現的圖表或儀表板。與公開連結類似,圖表將包含 Powered by Metabase 頁尾

我們可以調整寬度和高度,以符合您的圖表或儀表板。如果我們嵌入儀表板,且 iframe 的寬度不足以容納儀表板的版面配置,Metabase 會依照問題在儀表板上從左到右的順序堆疊問題。

啟用在其他應用程式中嵌入

如果我們想要限制誰可以查看您的圖表或儀表板,或鎖定篩選條件,我們需要使用靜態嵌入。此功能與公開分享選項分開,且僅管理員可以存取。我們也必須先啟用 在其他應用程式中嵌入 設定,才能使其運作。在管理面板設定索引標籤中,我們將點擊 在其他應用程式中嵌入,然後切換為 已啟用

讓我們回到我們想要分享的儀表板。啟用嵌入後,我們會取得一個密鑰。

With embedding enabled, you

若要設定靜態嵌入,我們需要在您的伺服器上插入一些程式碼,以為您的使用者簽署 JSON Web Tokens (JWT)。Metabase 將產生 Clojure、Python、Ruby 和 JavaScript (Node.js) 的程式碼,但您應該也能夠將該程式碼翻譯為以其他語言撰寫的伺服器。

Metabase will supply the code you

在我們點擊發佈按鈕之前,讓我們先檢閱一些選項。

隱藏或鎖定參數以限制顯示哪些資料

如果您的問題或儀表板有篩選條件,我們可以停用篩選條件,或鎖定參數以設定固定的篩選條件值。

假設我們想要向某人顯示儀表板,但我們只想讓他們看到 Gadget 類別中的訂單。

Locking a filter with the value Gadget.

在我們的範例中,參數會在儀表板上設定篩選條件。在這裡,我們將「類別」篩選條件設定為 Gadget

以下是以 Clojure 撰寫的伺服器的範例程式碼

(require '[buddy.sign.jwt :as jwt])

(def metabase-site-url   "MY-DOMAIN-HERE")
(def metabase-secret-key "SECRET-KEY-HERE")

(def payload
  {:resource {:dashboard 3}
   :params   {"category" ["Gadget"]}
   :exp      (+ (int (/ (System/currentTimeMillis) 1000)) (* 60 10))}) ; 10 minute expiration

(def token (jwt/sign payload metabase-secret-key))

(def iframe-url (str metabase-site-url "/embed/dashboard/" token "#bordered=true&titled=true"))

此程式碼將使用 Metabase 提供給我們的密鑰來簽署 JWT 權杖:您的使用者將不會 - 而且不應該 - 看到密鑰。

以下說明其運作方式。

  1. 在 Metabase 中,我們發佈要嵌入到您的應用程式中的儀表板。
  2. 我們將 iframe 插入到您的應用程式的頁面中。
  3. 我們將程式碼放置在您的伺服器中,以簽署 JSON Web Tokens。
  4. 您的使用者登入您的應用程式。
  5. 使用者在您的應用程式中請求具有嵌入式儀表板的頁面。
  6. 當伺服器處理該頁面的請求時,它將簽署使用者的權杖,並將該權杖作為 iframe 的來源 URL 插入。
  7. 當頁面載入時,頁面會使用已簽署的權杖從您的 Metabase 執行個體請求儀表板。
  8. 儀表板會使用您的應用程式伺服器設定的參數和到期時間,載入到您的應用程式的 iframe 中。

如果權杖未簽署,或以任何方式遭到竄改,則儀表板將不會載入。

payload 對應中,我們可以指定已簽署的 JWT 何時到期(在上述程式碼中,權杖在 10 分鐘後到期)。

:params 欄位是我們可以在其中鎖定參數,以在您的儀表板或問題上設定預設的固定篩選條件的位置。例如,我們可以建立一個具有使用者 ID 篩選條件的儀表板,然後 - 在您的伺服器上 - 以程式設計方式插入使用者的 ID 作為參數。已簽署的權杖將篩選條件鎖定為該 ID,因此任何看到該儀表板的使用者都只會看到已依其 ID 篩選的資料。

範例

我們在公開 Git 儲存庫中以多種語言維護嵌入範例。以下小節將逐步介紹 Django(熱門的 Python Web 框架)和 Shiny(最熱門的 R Web 框架)中的最小範例。

使用 Django 的範例

我們的最小 Django 應用程式僅包含兩個檔案:index.html 中的 HTML 範本,以及 index.py 中的簡短 Python 程式。範本簡潔明瞭

<!DOCTYPE html>
<html>
  <head>
    <title>{{ title }}</title>
  </head>
  <body>
    <h1>Embed {{ title }}</h1>
    <iframe
      src="{{iframeUrl}}"
      frameborder="1"
      width="800"
      height="600"
    ></iframe>
  </body>
</html>

它需要兩個值:title 中的頁面標題,以及 iframeUrl 中包含靜態嵌入的 iframe 的 URL。標題只是一個字串,但如上所述,我們需要做一些工作來建構 URL。程式從 Django 所需的一些程式庫和設定開始

# Required by Django
import os
from django.conf.urls import url
from django.http import HttpResponse
from django.template.loader import render_to_string

DEBUG = True
SECRET_KEY = '4l0ngs3cr3tstr1ngw3lln0ts0l0ngw41tn0w1tsl0ng3n0ugh'
ROOT_URLCONF = __name__
TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.getcwd()]
}]

然後,我們包含幾個程式庫,並為嵌入定義幾個值

# Required for Metabase embedding
import time
import jwt
METABASE_SITE_URL = 'https://127.0.0.1:3000'
METABASE_SECRET_KEY = '40e0106db5156325d600c37a5e077f44a49be1db9d02c96271e7bd67cc9529fa'

我們需要 time 來計算已簽署權杖的到期時間,以及 jwt 程式庫來簽署權杖。METABASE_SITE_URL 會告訴程式在哪裡找到您的 Metabase 執行個體 - 在此案例中,我們在本機執行它 - 而 METABASE_SECRET_KEY 是 Metabase 產生的值,我們將其傳回給 Metabase,以證明我們有權存取問題。我們不會將其放在生產環境中的原始碼中;相反地,我們會將其儲存在環境變數或個別的組態檔中。

我們必須在傳送給 Metabase 的 token 中包含三個值。由於其中一個是 token 過期時間,每次請求都會變更,因此我們將建構 token 的程式碼放在處理頁面請求的函式中。舉例來說,我們想要問題 #1,並且希望 token 從現在起十分鐘內有效

# Handle requests for '/'.
def home(request):
    payload = {
        'resource': {'question': 1},
        'params': {},
        'exp': round(time.time()) + (60 * 10)
    }
    token = jwt.encode(payload, METABASE_SECRET_KEY, algorithm='HS256')
    iframeUrl = METABASE_SITE_URL + '/embed/question/' + token + '#bordered=true&titled=true'
    html = render_to_string('index.html', {
        'title': 'Embedding Metabase',
        'iframeUrl': iframeUrl
    })
    return HttpResponse(html)

我們使用 jwt 函式庫中的 jwt.encode 函式,使用從 Metabase 取得的密鑰來加密我們的 token 參數。(參數 algorithm='HS256' 告訴 jwt.encode 要使用哪種雜湊演算法—我們必須始終使用該演算法。)然後,我們將加密後的 token 插入 URL 中,呈現 HTML 範本,並將其傳回應用程式。最後,我們的程式透過告知 Django 如何將傳入的請求與呈現函式匹配來結束

urlpatterns = [
    url(r'^$', home, name='homepage')
]

如果我們從命令列執行我們的應用程式,使用

$ django-admin runserver --pythonpath=. --settings=index

然後將瀏覽器指向 https://127.0.0.1:8000/,則會依序發生以下事情

  1. 瀏覽器傳送針對 / (網站根目錄) 的 HTTP 請求給監聽埠 8000 的 Django 應用程式。

  2. 該應用程式將請求中的 URL 與 home 函式匹配。

  3. home 產生一個新的 token,其過期時間為未來 10 秒。

  4. 然後它讀取 index.html 並將 {{title}} 替換為 “Embedding Metabase”,並將 {{iframeUrl}} 替換為包含新產生的 token 的 URL。

  5. home 然後將該 HTML 送回瀏覽器。

  6. 當瀏覽器顯示該 HTML 時,它會遇到 iframe。iframe 標籤中的 src 屬性告訴它向 Metabase 發送請求。

  7. 當 Metabase 收到 URL 以 /embed/question 開頭的請求時,它會從 URL 中提取路徑的其餘部分並解密。

  8. 由於 URL 是使用 Metabase 產生的密鑰進行編碼的,因此解密成功,這告訴 Metabase 發送者被允許查看問題。嵌入在 token 中的值告訴 Metabase 正在請求哪個問題。

  9. Metabase 執行問題並產生它將在其自身介面中顯示的 HTML,然後將該 HTML 送回發出請求的瀏覽器。

  10. 瀏覽器將該 HTML 插入 iframe 並向使用者顯示。

使用 Shiny 的範例

我們最簡化的 Shiny 範例比上面顯示的 Django 範例稍微簡單一些,因為 Shiny 需要較少的樣板程式碼。首先,我們載入 Shiny 本身、管理網路 token 和將字串黏合在一起的函式庫

library(shiny)
library(jose)
library(glue)

然後我們定義兩個值,指定 Metabase 的執行位置(我們將使用本機實例)以及 Metabase 提供的用於身份驗證的密鑰

METABASE_SITE_URL <- 'https://127.0.0.1:3000'
METABASE_SECRET_KEY <- '40e0106db5156325d600c37a5e077f44a49be1db9d02c96271e7bd67cc9529fa'

使用者介面基於一個流行的 CSS 框架,稱為 Bootstrap,並且有兩個元素:包含頁面標題的一級標題和包含我們 iframe 的 div。UI 本身不建構 iframe;相反地,它使用 uiOutput 函式來呈現稱為 container 的東西

ui <- bootstrapPage(
  h1('Page title'),
  uiOutput('container')
)

container 從哪裡來?在 Shiny 中,答案是:「伺服器」。如下所示,server 建構一個 JWT 聲明並對其進行加密,以建構 iframe 的 URL。然後,它呼叫 renderUI 以取得 iframe 的 HTML 內容,並將其指派給 output$container。當此指派發生時,Shiny 會自動告訴 UI 它需要重新繪製頁面

server <- function(input, output) {
  # Token expires 10 minutes in the future.
  expiry <- as.integer(unclass(Sys.time())) + (60 * 10)

  # Construct params in two steps so that JSON conversion knows it's a list.
  params <- list()
  names(params) <- character(0)

  # Create the JWT claim.
  claim <- jwt_claim(exp = expiry, resource = list(question = 1), params = params)

  # Encode token and use it to construct iframe URL.
  token <- jwt_encode_hmac(claim, secret = METABASE_SECRET_KEY)
  url <- glue("{METABASE_SITE_URL}/embed/question/{token}#bordered=true&titled=true")
  output$container <- renderUI(tags$iframe(width = "600", height = "600", src = url))
}

讓我們更詳細地了解 server 函式。首先,我們希望我們的 token 有效十分鐘,因此我們將目前時間作為整數取得,並加上 600 秒

  expiry <- as.integer(unclass(Sys.time())) + (60 * 10)

然後,我們使用 jose 函式庫中的 jwt_claim 函式來建構 token。此函式得名於我們聲稱我們被允許做某事,並且它將任意數量的具名參數作為輸入

  params <- list()
  names(params) <- character(0)
  claim <- jwt_claim(exp = expiry, resource = list(question = 1), params = list())

上面顯示的程式碼的前兩行是必要的,因為 jose 依賴另一個名為 jsonlite 的函式庫將結構轉換為 JSON 字串,並且預設情況下,該函式庫將空列表轉換為空陣列 [],而不是空映射 {}。這是一個小差異,但是當 Metabase 解碼結構時,它要求 params 是一個帶有鍵的映射。修復方法是給 params 一個空名稱列表,以便 jsonlite 產生所需的 {};為了確保這能正常運作,請確保您擁有 jose 版本 0.3 或更高版本。

一旦我們有了聲明,我們就可以使用 jwt_encode_hmac 對其進行編碼,並建構 iframe URL。jwt_encode_hmac 預設使用 HS256 編碼演算法,因此我們不必明確指定它

  token <- jwt_encode_hmac(claim, secret = METABASE_SECRET_KEY)
  url <- glue("{METABASE_SITE_URL}/embed/question/{token}#bordered=true&titled=true")

最後,我們使用 renderUI 來取得 iframe 的 HTML,並將其指派給 output$container,這會自動觸發瀏覽器頁面的更新

  output$container <- renderUI(tags$iframe(width = "600", height = "600", src = url))

檔案的最後一行在埠 8001 上執行我們的應用程式

shinyApp(ui = ui, server = server, options = list(port = 8001))

如果我們從命令列使用 Rscript app.R 執行此程式,並將我們的瀏覽器指向 https://127.0.0.1:8001,我們就會看到我們嵌入的問題。

互動式嵌入

如果您想解鎖 Metabase 在嵌入時的完整潛力,這將允許人們鑽取資料,或將他們發送到 自訂目的地,例如其他儀表板或外部 URL,您將需要互動式嵌入。若要了解更多資訊,請參閱 為您的客戶提供分析在您的應用程式中嵌入 Metabase 以提供多租戶、自助式分析

延伸閱讀

下一步:Metabase 謎團

公開連結、公開嵌入和簽名嵌入之間有什麼區別?

下一篇文章