Skip to content

Custom ResponseWriter not used by echo.WrapMiddleware #1257

Closed
@Daniel-Houston

Description

@Daniel-Houston

Issue Description

I want to wrap all response writers for my handlers with a custom response writer that also records the status code.

I realize that echo's Response struct records the status code, but I want to write my middleware to target the standard http library so it is usable by multiple muxers, as such I do not want to use the echo.Context or echo.Context.Response, or the Response.Before method in my middleware.

My custom writer looks like this:

type statusRecorder struct {                                                                                             
    http.ResponseWriter                                                                                       
    statusCode int                                                                                                       
}                                                                                                                        
                                                                                                                         
func (rec *statusRecorder) WriteHeader(code int) {
    rec.statusCode = code
    rec.WriteHeader(code)
}

I am using it in some middleware as follows:

func Middleware(handler http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        writer := statusRecorder{w, 0}
        handler.ServeHTTP(&writer, req)
        if writer.statusCode == 0 {
            log.Errorf("Expected status code to be set but it wasn't")
        }
    })
}

and then wrapping that middleware with echo.WrapMiddleware:

func main() {
    e := echo.New()
    e.GET("/endpoint", handler, echo.WrapMiddleware(Middleware))
}

func handler(c echo.Context) error {
    return c.String(http.StatusOK, "Hello, World!")
}

However, when I make a request to "/endpoint", I get the error message "Expected status code to be set but it wasn't".

I believe this is because echo's WrapMiddleware function does not use the response writer provided by the middleware. See lines 728-731 of echo.go:

echo/echo.go

Lines 728 to 731 in 7867fce

m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.SetRequest(r)
err = next(c)
})).ServeHTTP(c.Response(), c.Request())

c.SetRequest() is called but there is no call to set the response.

Would something like this work?

c.response = echo.NewResponse(w, c.Echo())

I would be happy to submit a patch that also sets the response writer if that is the desired solution.

If this is not at viable solution, please help me know how to best go about doing this

Checklist

  • [ x] Dependencies installed
  • [x ] No typos
  • [ x] Searched existing issues and docs

Expected behaviour

I would expect echo to use the wrapped ResponseWriter when writing http responses through something like c.String(). I would expect the statusCode field in my response writer to be set.

Actual behaviour

the statusCode field is not set.

Steps to reproduce

Run the code below, make an http request to http://localhost:8080/endpoint and check the log message.

Working code to debug

package main
                                                                                                                           
import (                                                                                                                           
    "net/http"                                                                                                                     
                                                                                                                                   
    "github.com/labstack/echo"                                                                                                     
    "github.com/labstack/gommon/log"                                                                                               
)                                                                                                                                  
                                                                                                                                   
type statusRecorder struct {                                                                                                       
    http.ResponseWriter                                                                                                            
    statusCode int                                                                                                                 
}                                                                                                                                  
                                                                                                                                   
func (rec *statusRecorder) WriteHeader(code int) {                                                                                 
    rec.statusCode = code                                                                                                          
    rec.WriteHeader(code)                                                                                                          
}                                                                                                                                  
                                                                                                                                   
func main() {                                                                                                                      
    e := echo.New()                                                                                                                
    e.GET("/endpoint", handler, echo.WrapMiddleware(Middleware))                                                                   
    e.Logger.Fatal(e.Start(":8080"))                                                                                               
}                                                                                                                                  
                                                                                                                                   
func handler(c echo.Context) error {                                                                                               
    return c.String(http.StatusOK, "Hello, World!")                                                                                
}                                                                                                                                  
                                                                                                                                   
func Middleware(handler http.Handler) http.Handler {                                                                               
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {                                                       
        writer := statusRecorder{w, 0}                                                                                             
        handler.ServeHTTP(&writer, req)                                                                                            
        if writer.statusCode == 0 {                                                                                                
            log.Errorf("Expected status code to be set but it wasn't")                                                             
        }                                                                                                                          
    })                                                                                                                             
}  

Version/commit

master branch latest commit

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions