You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Microsoft Graph SDK for Go创建Run Hunting Query分页迭代器报错解决

问题:Microsoft Graph SDK for Go分页迭代器适配Run Hunting Query失败

调用Run Hunting Query接口时,尝试使用msgraphcore.NewPageIterator处理分页,出现错误:Error creating page iterator: value property missing in response object。移除分页相关代码后功能可正常运行,需解决分页迭代器的适配问题,当前使用Echo Web框架。

相关代码

package handlers

import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"

    "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    "github.com/labstack/echo/v4"
    msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
    msgraphcore "github.com/microsoftgraph/msgraph-sdk-go-core"
    "github.com/microsoftgraph/msgraph-sdk-go/models/security"
    graphsecurity "github.com/microsoftgraph/msgraph-sdk-go/security"
)

func getClientWithCredentials() (*msgraphsdk.GraphServiceClient, error) {

    tenantID := os.Getenv("AZURE_TENANT_ID")
    clientID := os.Getenv("AZURE_CLIENT_ID")
    clientSecret := os.Getenv("AZURE_CLIENT_SECRET")

    cred, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, nil)

    if err != nil {
        fmt.Printf("Error creating credentials: %v\n", err)
    }

    client, err := msgraphsdk.NewGraphServiceClientWithCredentials(cred, nil)
    if err != nil {
        fmt.Printf("Error creating client: %v\n", err)
        return client, err
    }
    return client, nil
}

func RunKQL(c echo.Context) error {

    body, err := io.ReadAll(c.Request().Body)
    if err != nil {
        return err
    }
    query := string(body)

    timespan := c.QueryParam("timespan")

    client, err := getClientWithCredentials()
    if err != nil {
        c.Logger().Fatalf("Could not get access token: %v", err)
    }

    requestBody := graphsecurity.NewMicrosoftgraphsecurityrunhuntingqueryRunHuntingQueryPostRequestBody()
    requestBody.SetQuery(&query)
    if timespan != "" {
        requestBody.SetTimespan(&timespan)
    }
    result, err := client.Security().MicrosoftGraphSecurityRunHuntingQuery().Post(context.Background(), requestBody, nil)
    if err != nil {
        c.Logger().Fatalf("Error running hunting query: %v", err)
    }

    // Initialize iterator - this is not working to create a NewPageIterator
    pageIterator, err := msgraphcore.NewPageIterator[*security.HuntingQueryResultsable](
        result,
        client.GetAdapter(),
        security.CreateHuntingQueryResultsFromDiscriminatorValue)
    if err != nil {
        log.Fatalf("Error creating page iterator: %v\n", err)
    }

    //Iterate over all pages
    jsonObjects := make([]map[string]interface{}, 0)
    err2 := pageIterator.Iterate(
        context.Background(),
        func(pageItem *security.HuntingQueryResultsable) bool {
            // Process each page item here
            if results := (*pageItem).GetResults(); results != nil {
                for _, result := range results {
                    jsonData := result.GetAdditionalData()
                    jsonObjects = append(jsonObjects, jsonData)
                }
            }
            // Return true to continue iterating to the next page
            return true
        })
    if err2 != nil {
        log.Fatalf("Error iterating over security results: %v\n", err2)
    }

    //Working without using Page Iterator
    // jsonObjects := make([]map[string]interface{}, 0)
    // for _, result := range result.GetResults() {
    //  jsonData := result.GetAdditionalData()
    //  jsonObjects = append(jsonObjects, jsonData)
    // }

    jsonRes, err := json.MarshalIndent(jsonObjects, "", "  ")
    if err != nil {
        fmt.Println("Error marshalling JSON array:", err)
        return c.String(http.StatusInternalServerError, "Error generating JSON response")
    }

    return c.String(http.StatusOK, string(jsonRes))
}

问题原因

msgraphcore.NewPageIterator是为Microsoft Graph的标准分页集合结构设计的,这类结构用value字段存储当前页数据,配合@odata.nextLink实现分页。但runHuntingQuery API的返回结构是HuntingQueryResults,它用results字段存储查询结果,而非标准的value,所以迭代器找不到value属性时直接报错。

解决方案

由于默认分页迭代器不兼容该API的返回结构,需手动处理分页逻辑:

替换原代码中分页迭代器的部分,改为以下手动分页实现:

// 手动处理分页,替代默认PageIterator
jsonObjects := make([]map[string]interface{}, 0)
currentResult := result

for {
    // 处理当前页的查询结果
    if results := currentResult.GetResults(); results != nil {
        for _, res := range results {
            jsonData := res.GetAdditionalData()
            jsonObjects = append(jsonObjects, jsonData)
        }
    }

    // 检查是否存在下一页链接
    nextLink := currentResult.GetOdataNextLink()
    if nextLink == nil || *nextLink == "" {
        break // 没有下一页,终止循环
    }

    // 构造下一页请求
    reqInfo, err := msgraphsdk.NewRequestInformation()
    if err != nil {
        c.Logger().Fatalf("Failed to create request info: %v", err)
    }
    reqInfo.SetUri(*nextLink)
    reqInfo.SetMethod(http.MethodGet)

    // 请求下一页数据
    nextPageResult := security.NewHuntingQueryResults()
    err = client.GetAdapter().Send(context.Background(), reqInfo, nextPageResult, nil)
    if err != nil {
        c.Logger().Fatalf("Failed to fetch next page: %v", err)
    }

    // 更新当前结果为下一页数据,继续循环
    currentResult = nextPageResult
}

说明

  1. 手动循环检查@odata.nextLink,判断是否需要请求下一页
  2. 使用Graph客户端的Adapter直接发送请求到下一页链接,复用身份验证等逻辑
  3. 每一页都提取results字段中的数据,和原非分页逻辑保持一致

内容的提问来源于stack exchange,提问作者Rob Buhler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:15:13