跳至主要内容

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

您可以向应用后端添加资源处理器,以使用您自己的应用特定路由扩展 Grafana HTTP API。本指南解释了您可能需要添加资源处理器的理由以及一些常见的操作方法。

资源处理器的用途

应用通常与某种 HTTP 服务(例如第三方服务)集成以检索和发送数据。例如,此服务可能具有特定的身份验证和授权需求,或者具有不适合返回到 Grafana 和插件前端的响应格式。

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

资源处理器也可用于构建允许用户写回应用的控制面板。例如,您可以添加一个资源处理器来更新物联网设备的状态。

实现资源处理器接口

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

您可以在插件中通过两种方式实现此功能,使用httpadapter手动实现它。

使用httpadapter

Grafana 插件 Go SDK提供的httpadapter包是处理资源的推荐方法。此包提供对使用http.Handler接口处理资源调用的支持,并允许以更 Go 独立的方式响应 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包的一些其他示例: