当前位置: 首页 > 面试题库 >

从外部Go中间件控制HTTP标头

刘奇
2023-03-14
问题内容

假设我在Go中有一个中间件,我想Server用自己的值覆盖任何现有的标头。

// Server attaches a Server header to the response.
func Server(h http.Handler, serverName string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Server", serverName)
        h.ServeHTTP(w, r)
    })
}

然后我将其添加到这样的响应链中

http.Handle("/", authHandler)
http.ListenAndServe(":8000", Server(http.DefaultServeMux, "myapp"))

不幸的是,如果authHandler或由DefaultServeMux调用处理的任何事情w.Header().Add("Server", "foo")(例如,httputil.ReverseProxy.ServeHTTP),我最终在响应中会有两个Server标头。

$ http localhost:5962/v1/hello_world
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain
Date: Tue, 12 Jul 2016 04:54:04 GMT
Server: inner-middleware
Server: myapp

我真正想要的是这样的:

// Server attaches a Server header to the response.
func Server(h http.Handler, serverName string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
        w.Header().Set("Server", serverName)
    })
}

但是,ServeHTTP的语义不允许这样做:

ServeHTTP应该将回复标头和数据写入ResponseWriter,然后返回。返回信号表明请求已完成;在ServeHTTP调用完成之后或与之同时使用ResponseWriter或从Request.Body中读取是无效的。

我已经看到一些与此有关的丑陋骇客,例如httputil.ReverseProxy.ServeHTTP中的代码,该代码复制标头并重写响应代码,或者使用httptest.ResponseRecorder将整个正文读入字节缓冲区。

我也可以颠倒传统的中间件顺序,并以某种方式将Server中间件放在最后,或者使其成为最内部的中间件。

我缺少任何简单的方法吗?


问题答案:

您可以定义一个自定义类型,该类型将包装ResponseWriter并在写入所有标头之前插入Server标头,但要付出额外的间接层费用。这是一个例子:

type serverWriter struct {
    w           http.ResponseWriter
    name        string
    wroteHeader bool
}

func (s serverWriter) WriteHeader(code int) {
    if s.wroteHeader == false {
        s.w.Header().Set("Server", s.name)
        s.wroteHeader = true
    }
    s.w.WriteHeader(code)
}

func (s serverWriter) Write(b []byte) (int, error) {
    return s.w.Write(b)
}

func (s serverWriter) Header() http.Header {
    return s.w.Header()
}

// Server attaches a Server header to the response.
func Server(h http.Handler, serverName string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        sw := serverWriter{
            w:           w,
            name:        serverName,
            wroteHeader: false,
        }
        h.ServeHTTP(sw, r)
    })
}

我在这里写了更多有关此的内容。https://kev.inburke.com/kevin/how-to-write-go-
middleware/



 类似资料:
  • 问题内容: 如何从网页的任何位置(控制器组件外部)调用控制器下定义的函数? 当我按下“获取”按钮时,它可以完美运行。但是我需要从div控制器外部调用它。逻辑是:默认情况下,我的div是隐藏的。在导航菜单中的某个位置,我按了一个按钮,它应该显示()我的div并执行“获取”功能。我该如何实现? 我的网页是: 我的js: 问题答案: 这是从外部调用控制器功能的一种方法: 您的控制器的功能在哪里。 你可以

  • 问题内容: 我有上面的控制器,它设置了一个我只能从内部访问值的控制器。 但我看到的地方,使用下面我将能够访问,但是当我在它不存在。 我无法弄清楚如何访问$ scope并使用变量更新它。 问题答案: 没有看到标记,我想MenuSideController的作用域是您选择的作用域的子作用域。 虽然可以像这样遍历树(假设我们想要的作用域是第一个孩子): 仅选择连接了特定控制器的元素会更简单。 假设您正在

  • 控制器中间件 V5.1.17+版本开始,支持为控制器定义中间件。首先你的控制器需要继承系统的think\Controller类,然后在控制器中定义middleware属性,例如: <?php namespace app\index\controller; use think\Controller; class Index extends Controller { protected $

  • 注意: 机外控制非常危险。如果在真机上操作,请确保可以在出错的时候切回手动控制。 下面的教程是一个基础的机外控制例子,通过MAVROS在Gazebo中应用于Iris四旋翼上。在教程最后,你应该会得到与下面视频相同的结果,即无人机缓慢起飞到高度2米。 代码 在ROS包中创建offb_node.cpp文件,并粘贴下面内容: /** * @file offb_node.cpp * @brief of

  • Main.java Controller1.java Controller2.java 谢谢你的忠告! 编辑:语法

  • 问题内容: 我正在使用LiteIDE for Go。我的Go文件位于此处: 当我使用以下文件添加文件时: 导入“ ../Helper” 我收到此错误: 无法加载软件包:/Users/username/go/src/src/projectA/main.go:4:8:非本地软件包中的本地导入“ ../Helper” 有什么想法我做错了吗? 问题答案: 您通过导入路径导入软件包。对于位于中的packag