Ошибка клиента Elasticsearch Go: «Неверный формат JSON: EOF» при индексировании документов в промежуточном программном обеспечении

Я разрабатываю промежуточное программное обеспечение на 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)
        })
    }
}

Бессонница требуется/раз.

Документ о Кибане

Конкретные вопросы:

  1. Что может быть причиной ошибки «Неверный формат JSON: EOF», хотя документ, кажется, успешно проиндексирован?
  2. Как я могу гарантировать, что данные JSON обрабатываются правильно и ошибки не возвращаются, чтобы промежуточное программное обеспечение могло перейти к основному обработчику?

Будем очень признательны за любые идеи или предложения по решению этой проблемы!

Вы читаете тело запроса в промежуточном программном обеспечении, не заменяя его впоследствии. Я предполагаю, что другой обработчик пытается прочитать его еще раз, но вы не можете прочитать тело более одного раза. Чтобы исправить это, замените тело запроса в промежуточном программном обеспечении свежей копией после его прочтения: используйте io.ReadAll и json.Unmarshal или io.TeeReader с bytes.Buffer в промежуточном программном обеспечении, затем используйте io.NopCloser(bytes.NewReader(b)) или io.NopCloser(buf) для создания нового ReadCloser).

Peter 22.04.2024 14:38

Большое спасибо, @Peter! Очень полезно! Я не знал, что чтение тела HTTP-запроса поглотит его, не оставив ничего для следующих обработчиков.

kawa 22.04.2024 15:50
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
2
2
86
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ответ:.Decode(&doc)→ Декодирование делает тело пустым

Эта проблема возникает потому, что когда вы читаете из 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))

//code continues...
// Read and decode the request body into a generic map to determine the type of document
var doc map[string]interface{} //...

Объяснение:

  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-сервера смогут работать правильно, не мешая друг другу.

Другие вопросы по теме