Elasticsearch Go 客户端错误:在中间件中索引文档时出现“无效的 JSON 格式:EOF”

问题描述 投票:0回答:1

我正在用 Go 开发一个中间件,它可以拦截 HTTP 请求,从请求正文中读取 JSON 数据,并根据 此文档将其索引为 Elasticsearch 中的文档。

但是,尽管该文档似乎已在 Elasticsearch 中建立索引,但该过程会返回

Invalid JSON format: EOF
错误。此错误会阻止中间件继续执行其他数据库操作的主处理程序。

这是我的中间件代码的相关部分:

package middlewares

import (
    "encoding/json"
    "net/http"

    "github.com/elastic/go-elasticsearch/v8"
    "github.com/elastic/go-elasticsearch/v8/typedapi/types"
    "github.com/google/uuid"
)

// IndexDocumentMiddleware creates a middleware to index documents into Elasticsearch
func IndexDocumentMiddleware(es *elasticsearch.TypedClient) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            ctx := r.Context()
            // Read and decode the request body into a generic map to determine the type of document
            var doc map[string]interface{}
            if err := json.NewDecoder(r.Body).Decode(&doc); err != nil {
                http.Error(w, "Error parsing request body", http.StatusBadRequest)
                return
            }

            var indexName string
            if typeName, ok := doc["type"].(string); ok {
                indexName = typeName
            } else {
                http.Error(w, "Error: 'type' is not a string or is missing", http.StatusBadRequest)
                return
            }

            existsRes, err := es.Indices.Exists(indexName).Do(ctx)
            if err != nil {
                http.Error(w, "Error existsRes: "+err.Error(), http.StatusInternalServerError)
                return
            }

            if !existsRes {
                _, err := es.Indices.Create(indexName).Mappings(types.NewTypeMapping()).Do(ctx)
                if err != nil {
                    http.Error(w, "Error creating index: "+err.Error(), http.StatusInternalServerError)
                    return
                }
            }

            docID := uuid.New().String()

            _, err = es.Index(indexName).
                Id(docID).
                Document(doc).Do(ctx)
            if err != nil {
                http.Error(w, "Error indexing document: "+err.Error(), http.StatusInternalServerError)
                return
            }

            next.ServeHTTP(w, r)
        })
    }
}

失眠要求/res

Kibana 文档

具体问题:

  1. 即使文档似乎已成功索引,可能会导致“无效的 JSON 格式:EOF”错误?
  2. 如何确保 JSON 数据得到正确处理并且不返回任何错误,以便中间件可以继续执行主处理程序?

任何解决此问题的见解或建议将不胜感激!

json go elasticsearch middleware
1个回答
0
投票

答案:

出现此问题的原因是,当您从

http.Request.Body
(即
io.ReadCloser
)读取数据时,数据将被消耗并且无法用于后续读取。显然,这是一个常见的错误,特别是当多个处理程序或中间件需要访问请求主体时。

要解决此问题,您需要先读取整个正文,然后恢复它,以便后续处理程序或中间件可以重新读取它。这是完成此任务的基本代码:

// Read the entire body
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
    http.Error(w, "Error reading request body", http.StatusInternalServerError)
    return
}
// Restore the io.ReadCloser to its original state
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))

说明:

  1. 消耗主体

    json.NewDecoder(r.Body).Decode(&doc)
    操作读取
    io.ReadCloser
    流的全部内容。读取后,流为空,因为
    io.ReadCloser
    不支持倒带。这有效地“消耗”了主体,使其为空,以便后续尝试读取它。

  2. 恢复身体:阅读后,使用

    r.Body
    重新分配
    io.NopCloser(bytes.NewBuffer(bodyBytes))
    。这一行从我们之前读到的
    io.ReadCloser
    创建了一个新的
    bodyBytes
    ,有效地复制了原始内容。
    io.NopCloser
    用于将
    io.Reader
    (由
    bytes.NewBuffer
    返回)转换为
    io.ReadCloser
    ,而不添加任何关闭功能,因为不需要关闭缓冲区。

此方法可确保在此代码之后运行的任何中间件或处理程序仍然可以访问完整的请求正文,就像未受影响一样。

为什么这很重要:

不正确处理请求正文可能会导致微妙的错误,特别是在中间件链的多个部分可能需要检查或修改请求的大型应用程序中。这项技术可确保 HTTP 服务器的所有部分都能正确运行而不会互相干扰。

© www.soinside.com 2019 - 2024. All rights reserved.