跳转到主要内容

为应用插件添加资源处理器

您可以在您的应用后端添加资源处理器来扩展 Grafana HTTP API 并添加您自己的应用特定路由。本指南解释了为什么您可能想要添加 资源 处理器,以及一些常见的实现方式。

资源处理器的用途

应用通常需要集成某种类型的 HTTP 服务,例如第三方服务,以检索和发送数据。例如,该服务可能具有特定的认证和授权需求或响应格式不适合返回给 Grafana 和插件前端。

此外,您可能还想 保护您的资源,以便只有具有特定权限的用户才能访问某些路由。

资源处理器对于构建允许用户写入应用的控件面板也非常有用。例如,您可以在更新物联网设备状态时添加资源处理器。

实现资源处理器接口

要将资源处理器添加到您的后端插件,您需要实现 backend.CallResourceHandler 接口。

您可以在插件中通过两种方式实现此接口,使用 使用 httpadapter在插件中手动实现

使用 httpadapter

httpadapter 包提供的 Grafana Plugin SDK for Go 是处理资源的推荐方式。这个包提供了使用 http.Handler 接口处理资源调用的支持,允许以更 Go-agnostic 的方式响应 HTTP 请求,并使支持多个路由和方法(GET、POST 等)变得更加容易。

使用 http.Handler 允许您使用 Go 内置的名为 ServeMux 的路由功能,或者您选择的 HTTP 路由库(例如,gorilla/mux)。

注意

Go 1.22 包含路由增强,增加了对使用 ServeMux 进行方法匹配和通配符的支持。

以下示例展示了如何使用 httpadapter 包、ServeMuxhttp.Handler 添加对获取命名空间(/namespaces)、项目(/projects)和更新某些设备状态(/device)的支持。

package myplugin

import (
"context"
"net/http"

"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
)

type MyPlugin struct {
resourceHandler backend.CallResourceHandler
}

func New() *MyPlugin {
p := &MyPlugin{}
mux := http.NewServeMux()
mux.HandleFunc("/namespaces", p.handleNamespaces)
mux.HandleFunc("/projects", p.handleProjects)
p.resourceHandler := httpadapter.New(mux)
return p
}

func (p *MyPlugin) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
return p.resourceHandler.CallResource(ctx, req, sender)
}

func (p *MyPlugin) handleNamespaces(rw http.ResponseWriter, req *http.Request) {
rw.Header().Add("Content-Type", "application/json")
_, err := rw.Write([]byte(`{ "namespaces": ["ns-1", "ns-2"] }`))
if err != nil {
return
}
rw.WriteHeader(http.StatusOK)
}

func (p *MyPlugin) handleProjects(rw http.ResponseWriter, req *http.Request) {
rw.Header().Add("Content-Type", "application/json")
_, err := rw.Write([]byte(`{ "projects": ["project-1", "project-2"] }`))
if err != nil {
return
}
rw.WriteHeader(http.StatusOK)
}

访问后端插件上下文

您可以使用 backend.PluginConfigFromContext 函数访问 backend.PluginContext。它包含有关插件请求的上下文信息,例如执行请求的用户。

func (p *MyPlugin) handleSomeRoute(rw http.ResponseWriter, req *http.Request) {
pCtx := backend.PluginConfigFromContext(req.Context())
bytes, err := json.Marshal(pCtx.User)
if err != nil {
return
}

rw.Header().Add("Content-Type", "application/json")
_, err := rw.Write(bytes)
if err != nil {
return
}
rw.WriteHeader(http.StatusOK)
}

手动实现 backend.CallResourceHandler

手动实现 backend.CallResourceHandler 接口可能足以满足基本需求。为了支持检索数据的几个不同路由,您可以使用带有 req.Path 的 switch。

func (p *MyPlugin) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
switch req.Path {
case "namespaces":
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusOK,
Body: []byte(`{ "namespaces": ["ns-1", "ns-2"] }`),
})
case "projects":
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusOK,
Body: []byte(`{ "projects": ["project-1", "project-2"] }`),
})
default:
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusNotFound,
})
}
}

访问应用程序资源

实现后,您可以通过 Grafana HTTP API 和前端访问资源。

使用 Grafana HTTP API

您可以通过使用端点 http://<GRAFANA_HOSTNAME>:<PORT>/api/plugins/<PLUGIN_ID>/resources{/<RESOURCE>} 来通过 Grafana HTTP API 访问资源。其中,PLUGIN_ID 是唯一标识您的应用程序的插件标识符,而 RESOURCE 则取决于资源处理器的实现以及支持哪些资源(路由)。

使用上述示例,您可以访问以下资源

  • HTTP GET http://<GRAFANA_HOSTNAME>:<PORT>/api/plugins/<PLUGIN_ID>/resources/namespaces
  • HTTP GET http://<GRAFANA_HOSTNAME>:<PORT>/api/plugins/<PLUGIN_ID>/resources/projects

从前端

您可以使用运行时服务backendSrv中的get函数,向http://<GRAFANA_HOSTNAME>:<PORT>/api/plugins/<PLUGIN_ID>/resources/namespaces发送HTTP GET请求,在组件中访问您的资源。

import { getBackendSrv } from '@grafana/runtime';

const namespaces = await getBackendSrv().get(`/api/plugins/<PLUGIN_ID>/resources/namespaces`);

更多示例

使用资源处理程序和httpadapter包的一些其他示例