func getJSONField(buf *bytes.Buffer, key string) string {
	if buf == nil {
		return ""
	}
	value := gjson.Get(buf.String(), key[len(reqBodyReplPrefix):])
	return value.String()
}
getJSONField gets the value of the given field from the JSON body,
which is buffered in buf.
package requestbodyvar
import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
	"github.com/tidwall/gjson"
	"go.uber.org/zap"
)
const (
	reqBodyReplPrefix = "http.request.body."
	jsonBufCtxKey caddy.CtxKey = "json_buf"
)
func init() {
	caddy.RegisterModule(RequestBodyVar{})
	httpcaddyfile.RegisterHandlerDirective("request_body_var", parseCaddyfile)
}
type RequestBodyVar struct {
	logger *zap.Logger
}
func (RequestBodyVar) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "http.handlers.request_body_var",
		New: func() caddy.Module { return new(RequestBodyVar) },
	}
}
func (rbv *RequestBodyVar) Provision(ctx caddy.Context) (err error) {
	rbv.logger = ctx.Logger(rbv)
	return nil
}
func (rbv RequestBodyVar) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
	bodyVars := func(key string) (interface{}, bool) {
		if !strings.HasPrefix(key, reqBodyReplPrefix) {
			return nil, false
		}
		buf, ok := r.Context().Value(jsonBufCtxKey).(*bytes.Buffer)
		if ok {
			rbv.logger.Debug("got from the buffer", zap.String("key", key))
			return getJSONField(buf, key), true
		}
		rbv.logger.Debug("got from the body", zap.String("key", key))
		if r == nil || r.Body == nil {
			return "", true
		}
		defer r.Body.Close()
		buf = new(bytes.Buffer)
		_, err := io.Copy(buf, r.Body)
		if err != nil {
			return "", true
		}
		r.Body = ioutil.NopCloser(buf)
		ctx := context.WithValue(r.Context(), jsonBufCtxKey, buf)
		r = r.WithContext(ctx)
		return getJSONField(buf, key), true
	}
	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
	repl.Map(bodyVars)
	return next.ServeHTTP(w, r)
}