Repository: labstack/echo
Pull request #2040 (2021-12-07 11:19:00)

Add support for configurable target header for the request_id middleware
This PR adds the ability to choose what header to target for the X-Request-ID. Before this PR, the header used by the middleware was hardcoded for `X-Request-ID`, and exposing it via configuration will allow, among others, to set for `X-Correlation-ID`.

Setting a different header key is helpful for distributed traceability since `X-Request-ID` is used to trace single requests. In contrast, `X-Correlation-ID`(or others) is [commonly used](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields) to trace multiple servers' transactions.

The default is set as the previous hardcoded header (`X-Request-ID`), which is a backward-compatible change.

Example for the new configuration:

```Golang
(...)

rid = RequestIDWithConfig(RequestIDConfig{
		TargetHeader: echo.HeaderXCorrelationID,
})

(...)
```

This way they can be combined and the same middleware can be used for both use-cases. 

Also updated documentation: https://github.com/labstack/echox/pull/239


codecov[bot]: # [Codecov](https://codecov.io/gh/labstack/echo/pull/2040?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack) Report
> Merging [#2040](https://codecov.io/gh/labstack/echo/pull/2040?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack) (6dbf4d1) into [master](https://codecov.io/gh/labstack/echo/commit/b437ee3879f21e973be85ff827754343a34fbe35?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack) (b437ee3) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/labstack/echo/pull/2040/graphs/tree.svg?width=650&height=150&src=pr&token=vqnT76l79i&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack)](https://codecov.io/gh/labstack/echo/pull/2040?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack)

```diff
@@           Coverage Diff           @@
##           master    #2040   +/-   ##
=======================================
  Coverage   91.32%   91.33%           
=======================================
  Files          33       33           
  Lines        2871     2873    +2     
=======================================
+ Hits         2622     2624    +2     
  Misses        159      159           
  Partials       90       90           
```


| [Impacted Files](https://codecov.io/gh/labstack/echo/pull/2040?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack) | Coverage Δ | |
|---|---|---|
| [echo.go](https://codecov.io/gh/labstack/echo/pull/2040/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack#diff-ZWNoby5nbw==) | `94.15% <ø> (ø)` | |
| [middleware/request\_id.go](https://codecov.io/gh/labstack/echo/pull/2040/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack#diff-bWlkZGxld2FyZS9yZXF1ZXN0X2lkLmdv) | `85.71% <100.00%> (+1.50%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/labstack/echo/pull/2040?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/labstack/echo/pull/2040?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack). Last update [b437ee3...6dbf4d1](https://codecov.io/gh/labstack/echo/pull/2040?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack).

aldas: LGTM.

sitenote:  I would assume that if you are adding `X-Correlation-ID` you want to add `X-Request-ID` also. These headers are complementary to each other. Anyway this is ok, as you can always add other header with 
```go
	e.Use(RequestIDWithConfig(RequestIDConfig{
		RequestIDHandler: func(c echo.Context, s string) {
			corrID := req.Header.Get(echo.HeaderXCorrelationID)
			if corrID != "" {
				c.Response().Header().Set(echo.HeaderXCorrelationID, corrID)
			}
		},
	}))
```
and there are probably cases when you have different header than `X-Request-ID` (whatever legacy or 3rdpary reasons) and would like to use middleware with your own custom header.

Patch:
diff --git a/echo.go b/echo.go
index df5d358..ad03dd5 100644
--- a/echo.go
+++ b/echo.go
@@ -214,6 +214,7 @@ const (
 	HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
 	HeaderXRealIP             = "X-Real-IP"
 	HeaderXRequestID          = "X-Request-ID"
+	HeaderXCorrelationID      = "X-Correlation-ID"
 	HeaderXRequestedWith      = "X-Requested-With"
 	HeaderServer              = "Server"
 	HeaderOrigin              = "Origin"
diff --git a/middleware/request_id.go b/middleware/request_id.go
index b0baeeb..8c5ff66 100644
--- a/middleware/request_id.go
+++ b/middleware/request_id.go
@@ -17,14 +17,18 @@ type (
 
 		// RequestIDHandler defines a function which is executed for a request id.
 		RequestIDHandler func(echo.Context, string)
+
+		// TargetHeader defines what header to look for to populate the id
+		TargetHeader string
 	}
 )
 
 var (
 	// DefaultRequestIDConfig is the default RequestID middleware config.
 	DefaultRequestIDConfig = RequestIDConfig{
-		Skipper:   DefaultSkipper,
-		Generator: generator,
+		Skipper:      DefaultSkipper,
+		Generator:    generator,
+		TargetHeader: echo.HeaderXRequestID,
 	}
 )
 
@@ -42,6 +46,9 @@ func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc {
 	if config.Generator == nil {
 		config.Generator = generator
 	}
+	if config.TargetHeader == "" {
+		config.TargetHeader = echo.HeaderXRequestID
+	}
 
 	return func(next echo.HandlerFunc) echo.HandlerFunc {
 		return func(c echo.Context) error {
@@ -51,11 +58,11 @@ func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc {
 
 			req := c.Request()
 			res := c.Response()
-			rid := req.Header.Get(echo.HeaderXRequestID)
+			rid := req.Header.Get(config.TargetHeader)
 			if rid == "" {
 				rid = config.Generator()
 			}
-			res.Header().Set(echo.HeaderXRequestID, rid)
+			res.Header().Set(config.TargetHeader, rid)
 			if config.RequestIDHandler != nil {
 				config.RequestIDHandler(c, rid)
 			}


Test patch:
diff --git a/middleware/request_id_test.go b/middleware/request_id_test.go
index 944b3b4..21b7778 100644
--- a/middleware/request_id_test.go
+++ b/middleware/request_id_test.go
@@ -55,3 +55,34 @@ func TestRequestID_IDNotAltered(t *testing.T) {
 	_ = h(c)
 	assert.Equal(t, rec.Header().Get(echo.HeaderXRequestID), "<sample-request-id>")
 }
+
+func TestRequestIDConfigDifferentHeader(t *testing.T) {
+	e := echo.New()
+	req := httptest.NewRequest(http.MethodGet, "/", nil)
+	rec := httptest.NewRecorder()
+	c := e.NewContext(req, rec)
+	handler := func(c echo.Context) error {
+		return c.String(http.StatusOK, "test")
+	}
+
+	rid := RequestIDWithConfig(RequestIDConfig{TargetHeader: echo.HeaderXCorrelationID})
+	h := rid(handler)
+	h(c)
+	assert.Len(t, rec.Header().Get(echo.HeaderXCorrelationID), 32)
+
+	// Custom generator and handler
+	customID := "customGenerator"
+	calledHandler := false
+	rid = RequestIDWithConfig(RequestIDConfig{
+		Generator:    func() string { return customID },
+		TargetHeader: echo.HeaderXCorrelationID,
+		RequestIDHandler: func(_ echo.Context, id string) {
+			calledHandler = true
+			assert.Equal(t, customID, id)
+		},
+	})
+	h = rid(handler)
+	h(c)
+	assert.Equal(t, rec.Header().Get(echo.HeaderXCorrelationID), "customGenerator")
+	assert.True(t, calledHandler)
+}