I am looking at this type
type GzipResponseWriter struct {
gw *gzip.Writer
http.ResponseWriter
}
And functions that will implement it
func (w GzipResponseWriter) Write(b []byte) (int, error) {
if _, ok := w.Header()["Content-Type"]; !ok {
// If content type is not set, infer it from the uncompressed body.
w.Header().Set("Content-Type", http.DetectContentType(b))
}
return w.gw.Write(b)
}
func (w GzipResponseWriter) Flush() {
w.gw.Flush()
if fw, ok := w.ResponseWriter.(http.Flusher); ok {
fw.Flush()
}
}
Does the http.ResponseWriter relate to the second field?
Why not
gw1 http.ResponseWriter?
Related
Premise: I've found a similar issue but not working in my case, so please do not mark this as a duplicate.
I've a HTTP server in Go and I've created a middleware to log the request, the response time and I would like to log the response too.
I've used httputil.DumpRequest in a function called HTTPRequest under the package log.
How can I correctly get the response body and status and headers from the w http.ResponseWriter and log them together with the other data?
My ISSUE is: I would like to intercept the Response Headers, Status and Body and to log the together with the Request and Response Time
Here's the code:
log "core/logger"
...
func RequestLoggerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer func() {
log.Info(
fmt.Sprintf(
"[Request: %s] [Execution time: %v] [Response: %s]",
log.HTTPRequest(r),
time.Since(start),
// RESPONSE DATA HERE !!!!!!!
))
}()
next.ServeHTTP(w, r)
})
}
Thanks, #Sivachandran for the response. It was almost perfect, only it didn't implement the http.ResponseWriter because of the pointers.
For the sake of completeness, I post here the correct solution code, because it's not easy to find any documentation on it, even if this question has been given a negative score.
Stackoverflow is a good place to exchange questions and this, in my opinion, was a very good and difficult question, either for a middle lever Golang programmer, so it didn't deserve a negative score at all!
That's the solution, enjoy:
// RequestLoggerMiddleware is the middleware layer to log all the HTTP requests
func RequestLoggerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rww := NewResponseWriterWrapper(w)
w.Header()
defer func() {
log.Info(
fmt.Sprintf(
"[Request: %s] [Execution time: %v] [Response: %s]",
log.HTTPRequest(r),
time.Since(start),
rww.String(),
))
}()
next.ServeHTTP(rww, r)
})
}
// ResponseWriterWrapper struct is used to log the response
type ResponseWriterWrapper struct {
w *http.ResponseWriter
body *bytes.Buffer
statusCode *int
}
// NewResponseWriterWrapper static function creates a wrapper for the http.ResponseWriter
func NewResponseWriterWrapper(w http.ResponseWriter) ResponseWriterWrapper {
var buf bytes.Buffer
var statusCode int = 200
return ResponseWriterWrapper{
w: &w,
body: &buf,
statusCode: &statusCode,
}
}
func (rww ResponseWriterWrapper) Write(buf []byte) (int, error) {
rww.body.Write(buf)
return (*rww.w).Write(buf)
}
// Header function overwrites the http.ResponseWriter Header() function
func (rww ResponseWriterWrapper) Header() http.Header {
return (*rww.w).Header()
}
// WriteHeader function overwrites the http.ResponseWriter WriteHeader() function
func (rww ResponseWriterWrapper) WriteHeader(statusCode int) {
(*rww.statusCode) = statusCode
(*rww.w).WriteHeader(statusCode)
}
func (rww ResponseWriterWrapper) String() string {
var buf bytes.Buffer
buf.WriteString("Response:")
buf.WriteString("Headers:")
for k, v := range (*rww.w).Header() {
buf.WriteString(fmt.Sprintf("%s: %v", k, v))
}
buf.WriteString(fmt.Sprintf(" Status Code: %d", *(rww.statusCode)))
buf.WriteString("Body")
buf.WriteString(rww.body.String())
return buf.String()
}
You need to wrap the ResponseWriter to capture the response data.
type ResponseWriterWrapper struct {
w http.ResponseWriter
body bytes.Buffer
statusCode int
}
func (i *ResponseWriterWrapper) Write(buf []byte) (int, error) {
i.body.Write(buf)
return i.w.Write(buf)
}
func (i *ResponseWriterWrapper) WriteHeader(statusCode int) {
i.statusCode = statusCode
i.w.WriteHeader(statusCode)
}
func (i *ResponseWriterWrapper) String() {
var buf bytes.Buffer
buf.WriteString("Response:")
buf.WriteString("Headers:")
for k, v := range i.w.Header() {
buf.WriteString(fmt.Sprintf("%s: %v", k, v))
}
buf.WriteString(fmt.Sprintf("Status Code: %d", i.statusCode))
buf.WriteString("Body")
buf.WriteString(i.body.String())
}
Pass the wrapper to ServeHTTP and log captured response data.
func RequestLoggerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rww := ResponseWriterWrapper{ w: w }
defer func() {
log.Info(
fmt.Sprintf(
"[Request: %s] [Execution time: %v] [Response: %s]",
log.HTTPRequest(r),
time.Since(start),
log.Info(rww.String())
))
}()
next.ServeHTTP(rww, r)
})
}
Here is simple realisation of Handler interface:
type someApi struct {
mu *sync.RWMutex
}
func (api *someApi) ServeHTTP(w http.ResponseWriter, r *http.Request) {}
func NewSomeApi(mu *sync.RWMutex) *someApi {
return &someApi{
mu: mu,
}
}
func (srv *someApi) Create() {
// some realisation
}
func Create() {
// some realisation
}
I want parse file with go/ast and create decorator for someApi.Create function.
It simple to get func name with *ast.FuncDecl.Name, but how can I find which of Create funcs attached to someApi?
Sloved this by iterating through *ast.FuncDecl.Recv.List:
for _, l := range fn.Recv.List { // fn is *ast.FuncDecl
star, ok := l.Type.(*ast.StarExpr)
if !ok {
continue
}
fmt.Println(star.X) // someApi
}
gorm unsupported data type interface.
How do I save different structures to one field in json (gorm)? Type checking needed, not just serialization in Json.
Example code with error: unsupported data type: models.Fields
// table questions (GORM)
type Questions struct {
ID
Fields `json:"fields"`
// .....
}
type Fields interface{
// ...
}
// radio ----------
type Radio struct {
Text string
Img string
// ...
}
func (d Radio) Value() (driver.Value, error) {
return json.Marshal(d)
}
func (d *Radio) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
return errors.New("type assertion to []byte failed")
}
return json.Unmarshal(b, &d)
}
// Checkbox ----------
type Checkbox struct {
Text string
MaxCheckbox uint16
// ...
}
func (d Checkbox) Value() (driver.Value, error) {
return json.Marshal(d)
}
func (d *Checkbox) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
return errors.New("type assertion to []byte failed")
}
return json.Unmarshal(b, &d)
}
You might have already solved this, but you need to use polymorphism.
I am looking at this example
var name string
type helloWorldResponse struct {
Message string `json:"message"`
}
type helloWorldRequest struct {
Name string `json:"name"`
}
func main() {
port := 8080
handler := newValidationHandler(newHelloWorldHandler())
http.Handle("/helloworld", handler)
log.Printf("Server starting on port %v\n", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}
type validationHandler struct {
next http.Handler
}
func newValidationHandler(next http.Handler) http.Handler {
return validationHandler{next: next}
}
func (h validationHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
var request helloWorldRequest
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&request)
if err != nil {
http.Error(rw, "Bad request", http.StatusBadRequest)
return
}
name = request.Name
h.next.ServeHTTP(rw, r)
}
type helloWorldHandler struct{}
func newHelloWorldHandler() http.Handler {
return helloWorldHandler{}
}
func (h helloWorldHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
response := helloWorldResponse{Message: "Hello " + name}
encoder := json.NewEncoder(rw)
encoder.Encode(response)
}
The author of the code explained that we are
going to be chaining handlers together, the first handler, which is our validation handler,
needs to have a reference to the next in the chain as it has the responsibility for calling
ServeHTTP or returning a response. I am newbe to Go and I do not understand this line
return validationHandler{next: next}
Which data structure next:next represents?
type validationHandler struct {
next http.Handler // 1
}
func newValidationHandler(next /* 2 */ http.Handler) http.Handler {
return validationHandler{next: next}
// 1 2
}
next number 1 is a field from validationHandler struct (a few lines above). And the other next is method's parameter (from the signature). All in all, this simply sets a field in a struct. No magic.
Which data structure next:next represents?
Not a data structure. It is struct initialization syntax. See more examples here: https://gobyexample.com/structs
Have following struct:
package dto
type CapacityResponse struct{
Val int
Err error
TransactionID string
}
func (r *CapacityResponse) GetError() (error) {
return r.Err
}
func (r *CapacityResponse) SetError(err error) {
r.Err = err
}
func (r *CapacityResponse) Read() interface{} {
return r.Val
}
func (r *CapacityResponse) GetTransactionId() string {
return r.TransactionID
}
It's interface:
package dto
type Responder interface {
Read() (interface{})
GetError() (error)
SetError(error)
GetTransactionId() (string)
}
And following logic:
func handler(w http.ResponseWriter, r *http.Request) {
cr := request2CacheRequest(r)
responseChan := make(chan dto.Responder)
go func() {
responder := processReq(cr)
responseChan <- responder
}()
go func() {
for r := range responseChan {
if (r.GetTransactionId() == cr.TransactionID) {
switch r.(type) {
//case dto.KeysResponse:
//case dto.GetResponse:
//case dto.RemoveResponse:
//case dto.SetResponse:
case dto.CapacityResponse:
if i, ok := r.Read().(int); ok {
fmt.Fprintf(w, fmt.Sprintf("{'capasity': %d, 'err': %s}", i, r.GetError()))
}
}
}
}
}()
}
I am getting exception:
impossible type switch case: r (type dto.Responder) cannot have dynamic type dto.CapacityResponse (missing GetError method)
Could you, please, help me to understand what is wrong here?
The error message is saying that a dto.Responder value cannot contain a dto.CapacityResponse because dto.CapacityResponse is missing one of the dto.Responder methods (GetError).
The pointer type implements the interface. Change the case to:
case *dto.CapacityResponse:
You have this error because dto.CapacityResponse type is different from *dto.CapacityResponse type.
Because you are using local variable r of interface type dto.Responder the only concrete types you can use in case statements are those that implement this interface and dto.CapacityResponse isn't one of them, because it is not a pointer type and you have declared receivers as pointers for dto.CapacityResponse. Please take a look on playground example