如何在Golang Echo中的next()之后添加/设置Header?

huangapple go评论81阅读模式
英文:

How to Add/Set Header after next() in Golang Echo Middleware?

问题

主要目的:
我想简单地计算任何API/路由的执行时间,并将其添加到响应头中,键为ExecutionTime。

我非常愿意尝试其他方法,因为我对Go-Echo框架还不熟悉,我的方法可能是错误的。

我一直在做什么:
这是我的中间件代码:

func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		c.Response().Header().Set("ExecutionStartedAt", time.Now().String())
		if err := next(c); err != nil { //执行主要过程
			c.Error(err)
		}
		c.Response().Header().Set("ExecutionDoneAt", time.Now().String())
		return nil
	}
}

当前结果:

< HTTP/1.1 200 OK
< Content-Type: application/json; charset=UTF-8
< Executionstartedat: 2022-09-11 16:32:15.612045 +0700 WIB m=+3.619364986
< Server: Echo/3.0
< Date: Sun, 11 Sep 2022 09:32:21 GMT
< Content-Length: 856

请注意:已添加了Executionstartedat。但不幸的是,没有像预期的那样添加ExecutionDoneAt。

期望的结果:

< HTTP/1.1 200 OK
< Content-Type: application/json; charset=UTF-8
< Executionstartedat: 2022-09-11 16:32:15.612045 +0700 WIB m=+3.619364986
< Executiondoneat: 2022-09-11 16:32:18.612045 +0700 WIB m=+3.619364986
< Server: Echo/3.0
< Date: Sun, 11 Sep 2022 09:32:21 GMT
< Content-Length: 856

我正在遵循这个文档:https://echo.labstack.com/cookbook/middleware/#response

非常感谢任何帮助、任何想法、任何实现相同结果的替代方法。非常感谢。

编辑 #1:

我还尝试了什么:

我尝试使用另一个中间件,计时是正确的,就像我预期的那样。但不幸的是,头部仍然为空。

e.Use(middleware.BodyDump(func(c echo.Context, reqBody, resBody []byte) {
	fmt.Println("this is from BodyDump 7: ", time.Now())
	c.Response().Header().Set("ExecutionTime7", "Exec Time: " + time.Now().String())
}))

结果与上面完全相同,没有任何变化。

编辑 #2

这是一个可工作的代码示例:

// ExecTime是中间件函数。
func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		before := time.Now()
		c.Response().Header().Set("ExecutionStartedAt", before.String())

		c.Response().Before(func() {
			after := time.Now()
			elapsed := time.Since(before)

			c.Response().Header().Set("ExecutionDoneAt", after.String())
			c.Response().Header().Set("ExecutionTime", elapsed.String())
		})

		if err := next(c); err != nil { //执行主要过程
			c.Error(err)
		}
		return nil
	}
}
英文:

Main Purpose:
I want to simply count the execution time for any API / route, and add it into the Response Header with key: ExecutionTime.

I am very open to another alternative, since my method may wrong as I am still new in Go-Echo framework.

What I have been doing:
So this is my middleware code:

func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		c.Response().Header().Set(&quot;ExecutionStartedAt&quot;, time.Now().String())
		if err := next(c); err != nil { //exec main process
			c.Error(err)
		}
		c.Response().Header().Set(&quot;ExecutionDoneAt&quot;, time.Now().String())
		return nil
	}
}

Current Result:

&lt; HTTP/1.1 200 OK
&lt; Content-Type: application/json; charset=UTF-8
&lt; Executionstartedat: 2022-09-11 16:32:15.612045 +0700 WIB m=+3.619364986
&lt; Server: Echo/3.0
&lt; Date: Sun, 11 Sep 2022 09:32:21 GMT
&lt; Content-Length: 856

Please note: There is an Executionstartedat added. But unfortunatelly there is no ExecutionDoneAt as expected.

Expected Result:

&lt; HTTP/1.1 200 OK
&lt; Content-Type: application/json; charset=UTF-8
&lt; Executionstartedat: 2022-09-11 16:32:15.612045 +0700 WIB m=+3.619364986
&lt; Executiondoneat: 2022-09-11 16:32:18.612045 +0700 WIB m=+3.619364986
&lt; Server: Echo/3.0
&lt; Date: Sun, 11 Sep 2022 09:32:21 GMT
&lt; Content-Length: 856

I am following this documentation: <https://echo.labstack.com/cookbook/middleware/#response>

Any help, any idea, any alternative ways to accomplish same result would be very very appreciated. Thank you very much.

Edited #1:

What else I have tried:

I have tried to use another middleware, the timing is good, just as I expected. But unfortunately the header is still empty.

	e.Use(middleware.BodyDump(func(c echo.Context, reqBody, resBody []byte) {
		fmt.Println(&quot;this is from BodyDump 7: &quot;, time.Now())
		c.Response().Header().Set(&quot;ExecutionTime7&quot;, &quot;Exec Time: &quot; + time.Now().String())
	}))

The result is exactly like above, no change at all.

Edited #2

here is the working code example:

// ExecTime is the middleware function.
func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		before := time.Now()
		c.Response().Header().Set(&quot;ExecutionStartedAt&quot;, before.String())

		c.Response().Before(func() {
			after := time.Now()
			elapsed := time.Since(before)

			c.Response().Header().Set(&quot;ExecutionDoneAt&quot;, after.String())
			c.Response().Header().Set(&quot;ExecutionTime&quot;, elapsed.String())
		})

		if err := next(c); err != nil { //exec main process
			c.Error(err)
		}
		return nil
	}
}

答案1

得分: 3

在HTTP中,头部在正文之前,正如“header”这个名字所暗示的那样。因此,一旦你写了正文,就不能再写头部了。你可以尝试使用HTTP/2的Trailers。或者在执行过程中,在将其输出写入正文之前,计算执行时间,添加头部,然后再写入正文。

我不使用echo,所以我不知道正确的方法是什么,但我觉得如果你在使用响应之前使用响应的before hook可能会更好。

以下是@JackySupit提供的示例:

// ExecTime是中间件函数。
func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		before := time.Now()
		c.Response().Header().Set("ExecutionStartedAt", before.String())

		c.Response().Before(func() {
			after := time.Now()
			elapsed := time.Since(before)

			c.Response().Header().Set("ExecutionDoneAt", after.String())
			c.Response().Header().Set("ExecutionTime", elapsed.String())
		})

		if err := next(c); err != nil { //执行主要过程
			c.Error(err)
		}
		return nil
	}
}
英文:

In HTTP the headers go before the body, as the name "header" implies. So once you write the body you cannot write the header. You may try using HTTP/2 Trailers. Or execute the process, but before writing its output to the body, calculate the execution time, add the header, and then write the body.

I don't use echo so I don't know what the correct approach is, but it seems to me like it'd be better if you use the response before hook.

Example by @JackySupit:

// ExecTime is the middleware function.
func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		before := time.Now()
		c.Response().Header().Set(&quot;ExecutionStartedAt&quot;, before.String())

		c.Response().Before(func() {
			after := time.Now()
			elapsed := time.Since(before)

			c.Response().Header().Set(&quot;ExecutionDoneAt&quot;, after.String())
			c.Response().Header().Set(&quot;ExecutionTime&quot;, elapsed.String())
		})

		if err := next(c); err != nil { //exec main process
			c.Error(err)
		}
		return nil
	}
}

huangapple
  • 本文由 发表于 2022年9月11日 17:39:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/73678238.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定