跳到主要内容

为应用插件添加资源处理程序

你可以为你的应用后端添加一个资源处理程序,以使用你自己的应用特定路由扩展 Grafana HTTP API。本指南解释了为什么你可能需要添加资源处理程序以及一些常见的做法。

资源处理程序的用途

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

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

资源处理程序对于构建允许用户写回应用的控制面板也非常有用。例如,你可以添加一个资源处理程序来更新 IoT 设备的状态。

实现资源处理程序接口

要向你的后端插件添加资源处理程序,你需要实现 backend.CallResourceHandler 接口。

有两种方法可以在你的插件中实现此功能:使用 httpadapter或在插件中手动实现

使用 httpadapter

Grafana Plugin SDK for Go 提供的 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 GET 请求到 http://<GRAFANA_HOSTNAME>:<PORT>/api/plugins/<PLUGIN_ID>/resources/namespaces

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

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

更多示例

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