Je n'ai rien trouvé d'utile en ligne sur celui-ci.

J'écris une API REST et je souhaite enregistrer la taille du corps de la requête en octets pour les métriques. L'API Go net / http ne fournit pas cela directement. http.Request a un champ Content-Length, mais ce champ peut être vide ou le client peut envoyer de fausses données.

Y a-t-il un moyen d'obtenir cela au niveau du middleware? La méthode bruteforce consisterait à lire le corps entier et à vérifier la taille. Mais si je fais cela dans le middleware, le gestionnaire n'aura pas accès au corps car il aurait été lu et fermé.

3
Husain 21 nov. 2018 à 01:32

3 réponses

Meilleure réponse

Pourquoi voulez-vous un intermédiaire ici?
Le moyen le plus simple est b, err = io.Copy(anyWriterOrMultiwriter, r.Body)
b est la longueur totale du contenu de la requête lorsque err == nil
Utilisez le corps de la requête comme vous le souhaitez. Aussi b, err = io.Copy(ioutil.Discard, r.Body)

2
KibGzr 21 nov. 2018 à 02:14

Vous pouvez écrire un ReadCloser personnalisé qui proxie un existant et compte les octets au fur et à mesure. Quelque chose comme:

type LengthReader struct {
    Source io.ReadCloser
    Length int
}

func (r *LengthReader) Read(b []byte) (int, error) {
    n, err := r.Source.Read(b)
    r.Length += n
    return n, err
}

func (r *LengthReader) Close() error {
    var buf [32]byte
    var n int
    var err error
    for err == nil {
        n, err = r.Source.Read(buf[:])
        r.Length += n
    }
    closeerr := r.Source.Close()
    if err != nil && err != io.EOF {
        return err
    }
    return closeerr
}

Cela comptera les octets au fur et à mesure que vous les lirez à partir du flux, et une fois fermé, il consommera et comptera tous les octets non lus restants en premier. Une fois que vous avez terminé avec le flux, vous pouvez accéder à la longueur.

4
DarthFennec 20 nov. 2018 à 23:03

Option 1

Utilisez TeeReader et cela est évolutif. Il divise le lecteur en deux et l'un d'eux calcule la taille en utilisant la mémoire allouée. Aussi, dans le premier cas

  maxmem := 4096
  var buf bytes.Buffer
  // comment this line out if you want to disable gathering metrics
  resp.Body = io.TeeReader(resp.Body, &buf) 

  readsize := func(r io.Reader) int {
    bytes := make([]byte, maxmem)
    var size int
      for {
        read, err := r.Read(bytes)
        if err == io.EOF {
        break
      }
      size += read
    }
    return size
  }

  log.Printf("Size is %d", readsize(&buf))

Option 2 manière non évolutive (réponse originale)

Vous pouvez simplement lire le corps, calculer la taille, puis démarquer en struct, pour qu'il devienne:

    b, _ := ioutil.ReadAll(r.Body)

    size := len(b) // can be nil so check err in your app

    if err := json.Unmarshal(b, &input); err != nil {
        s.BadReq(w, errors.New("error reading body"))
        return
    }

2
tsuz 5 sept. 2019 à 04:19