golang несколько parseBody для http.request

Привет, я хотел бы проанализировать http.resquest два раза, как показано ниже. Когда я разобрал Тело в первый раз, тело будет закрыто. Мне нужна помощь/подсказка, как лучше всего справиться с этим, мне нужно создать копию запроса или есть лучший способ?

func myfunc(w http.ResponseWriter, req *http.Request) {
  err := parseBody(req, &type1){
  .....
  }

  err := parseBody(req, &type2){
  .....
  }
}

Спасибо за помощь


person CrimsonKing    schedule 19.07.2018    source источник
comment
Да, вы должны скопировать тело, чтобы прочитать его 2 копии.   -  person JimB    schedule 19.07.2018
comment
Тело не закроется, пока вы этого не сделаете. Также вы можете читать с тела только один раз.   -  person Himanshu    schedule 19.07.2018


Ответы (2)


Это правда, что вы можете прочитать тело только один раз, и это нормально, потому что для разбора тела более одного раза вам не нужно читать его больше одного раза. Рассмотрим простой пример:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

type RequestData1 struct {
    Code   string `json:"code"`
    Status string `json:"status"`
}

type RequestData2 struct {
    Status  string `json:"status"`
    Message string `json:"message"`
}

func main() {
    http.HandleFunc("/post", post)
    http.ListenAndServe(":8080", nil)
}

Если мы используем этот код:

func post(w http.ResponseWriter, r *http.Request) {
    body1, err := ioutil.ReadAll(r.Body)
    if err != nil {
        panic(err)
    }
    rd1 := RequestData1{}
    err = json.Unmarshal(body1, &rd1)
    if err != nil {
        panic(err)
    }

    body2, err := ioutil.ReadAll(r.Body)
    if err != nil {
        panic(err)
    }
    rd2 := RequestData2{}
    err = json.Unmarshal(body2, &rd2)
    if err != nil {
        panic(err) // panic!!!
    }

    fmt.Printf("rd1: %+v \nrd2: %+v", rd1, rd2)
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`Look into console.`))
}

у нас будет паника: http: panic serving [::1]:54581: unexpected end of JSON input
но со следующим кодом:

func post(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        panic(err)
    }

    rd1 := RequestData1{}
    err = json.Unmarshal(body, &rd1)
    if err != nil {
        panic(err)
    }

    rd2 := RequestData2{}
    err = json.Unmarshal(body, &rd2)
    if err != nil {
        panic(err)
    }

    fmt.Printf("rd1: %+v \nrd2: %+v", rd1, rd2)
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`Look into console.`))
}

все работает! Вы можете протестировать его, отправив запрос:

curl -X POST 'http://localhost:8080/post' \
-H 'Content-Type: application/json' -d '{"code":"200", "status": "OK", "message": "200 OK"}'

Результат будет:

rd1: {Code:200 Status:OK}
rd2: {Status:OK Message:200 OK}
person cn007b    schedule 19.07.2018
comment
@CrimsonKing Добро пожаловать в любое время ;) - person cn007b; 20.07.2018

Когда вы читаете request.Body, вы читаете поток с клиента (например, веб-браузера). Клиент отправляет запрос только один раз. Если вы хотите проанализировать его несколько раз, прочитайте все это в буфер (например, []byte), а затем проанализируйте его столько раз, сколько хотите. Просто помните о потенциальном использовании памяти многими одновременными запросами с большими полезными нагрузками, так как вы будете хранить полную полезную нагрузку в памяти, по крайней мере, до тех пор, пока не завершите ее синтаксический анализ.

person Adrian    schedule 19.07.2018