http - golang - go request body
Cuerpo de solicitud de lectura de Golang (2)
Inspección y burla del cuerpo de solicitud
Cuando lees el cuerpo por primera vez, debes almacenarlo, así que una vez que hayas terminado con él, puedes establecer un nuevo
io.ReadCloser
como el cuerpo de la solicitud construido a partir de los datos originales.
Entonces, cuando avanzas en la cadena, el siguiente controlador puede leer el mismo cuerpo.
Una opción es leer todo el cuerpo usando
ioutil.ReadAll()
, que le da el cuerpo como un segmento de bytes.
Puede usar
bytes.NewBuffer()
para obtener un
io.Reader
de un segmento de bytes.
La última pieza que falta es convertir el
io.Reader
un
io.ReadCloser
, porque
bytes.Buffer
no tiene un método
Close()
.
Para esto, puede usar
ioutil.NopCloser()
que envuelve un
io.Reader
y devuelve un
io.ReadCloser
, cuyo método
Close()
agregado será un no-op (no hace nada).
Tenga en cuenta que incluso puede modificar el contenido del segmento de bytes que utiliza para crear el "nuevo" cuerpo. Tienes control total sobre eso.
Sin embargo, se debe tener cuidado, ya que puede haber otros campos HTTP como la longitud del contenido y las sumas de verificación que pueden volverse inválidas si modifica solo los datos. Si los controladores posteriores los verifican, ¡también necesitarás modificarlos!
Inspección / modificación del cuerpo de respuesta
Si también desea leer el cuerpo de la respuesta, debe envolver el
http.ResponseWriter
que obtiene y pasar el contenedor a la cadena.
Este contenedor puede almacenar en caché los datos enviados, que puede inspeccionar después, sobre la marcha (como los controladores posteriores le escriben).
Aquí hay un contenedor simple
ResponseWriter
, que simplemente almacena en caché los datos, por lo que estará disponible después de que regrese el controlador posterior:
type MyResponseWriter struct {
http.ResponseWriter
buf *bytes.Buffer
}
func (mrw *MyResponseWriter) Write(p []byte) (int, error) {
return mrw.buf.Write(p)
}
Tenga en cuenta que
MyResponseWriter.Write()
simplemente escribe los datos en un búfer.
También puede optar por inspeccionarlo sobre la marcha (en el método
Write()
) y escribir los datos inmediatamente en el
ResponseWriter
envuelto / incrustado.
Incluso puede modificar los datos.
Tienes el control total.
Sin embargo, se debe tener cuidado nuevamente, ya que los controladores posteriores también pueden enviar encabezados de respuesta HTTP relacionados con los datos de respuesta, como la longitud o las sumas de verificación, que también pueden invalidar si modifica los datos de respuesta.
Ejemplo completo
Poniendo las piezas juntas, aquí hay un ejemplo de trabajo completo:
func loginmw(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Printf("Error reading body: %v", err)
http.Error(w, "can''t read body", http.StatusBadRequest)
return
}
// Work / inspect body. You may even modify it!
// And now set a new body, which will simulate the same data we read:
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
// Create a response wrapper:
mrw := &MyResponseWriter{
ResponseWriter: w,
buf: &bytes.Buffer{},
}
// Call next handler, passing the response wrapper:
handler.ServeHTTP(mrw, r)
// Now inspect response, and finally send it out:
// (You can also modify it before sending it out!)
if _, err := io.Copy(w, mrw.buf); err != nil {
log.Printf("Failed to send out response: %v", err)
}
})
}
Estoy escribiendo mi propio logginMiddleware. Básicamente, necesito registrar el cuerpo de la solicitud y la respuesta. El problema que enfrenté es que cuando leo el cuerpo, se vacía y no puedo leerlo dos veces. Entiendo que sucede porque es de tipo ReadCloser. ¿Hay alguna manera de rebobinar el cuerpo hasta el principio?
Podría usar el paquete
GetBody
from Request.
Mire este comentario en el código fuente de request.go en net / http:
GetBody define una función opcional para devolver una nueva copia de Body. Se utiliza para solicitudes de clientes cuando una redirección requiere leer el cuerpo más de una vez. El uso de GetBody todavía requiere configurar el cuerpo. Para las solicitudes del servidor no se utiliza ".
GetBody func() (io.ReadCloser, error)
De esta manera, puede obtener la solicitud del cuerpo sin dejarla vacía.
Muestra:
getBody := request.GetBody
copyBody, err := getBody()
if err != nil {
// Do something return err
}
http.DefaultClient.Do(request)