使用自定义的http.ResponseWriter基于代理请求的响应编写cookie? [英] Using a custom http.ResponseWriter to write cookies based on the response from a proxied request?

查看:79
本文介绍了使用自定义的http.ResponseWriter基于代理请求的响应编写cookie?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的原始问题此副本问题.我没有实现它的运气,并且怀疑我的问题被误解了,所以在我的问题结束之时,我从一个更具体的问题着手.

My original question here was flagged as a duplicate of this question. I had no luck implementing it and suspect my problem is misunderstood, so with my question closed, I'm starting fresh with a more specific question.

我正在尝试基于来自反向请求的中间件中响应头的cookie设置.

I'm trying to set a cookie based on a response header from within middleware in request that is reverse proxied.

这是工作流程:

  • 用户请求 http://example.com/foo/bar
  • Go应用程序使用ReverseProxy将该请求代理到 http://baz.com
  • baz.com设置响应标头 X-FOO
  • Go应用通过使用 X-FOO 响应标头的值设置 MYAPPFOO cookie来修改响应,
  • cookie被写入用户的浏览器
  • User requests http://example.com/foo/bar
  • Go app uses ReverseProxy to proxy that request to http://baz.com
  • baz.com sets a response header X-FOO
  • Go app modifies response by setting a MYAPPFOO cookie with the value of the X-FOO response header
  • The cookie is written to the user's browser

建议使用自定义的 http.ResponseWriter ,但尝试并搜索更多信息后,尚不清楚如何解决此问题.

It was suggested that a custom http.ResponseWriter will work, but after trying and searching for more information, it is not clear how to approach this.

由于我无法掌握用例的自定义ResponseWriter的概念,因此我将发布代码,以更精确地演示在遇到困难时我想做的事情:

Since I'm failing to grasp the concept of a custom ResponseWriter for my use case, I'll post code that demonstrates more precisely what I was trying to do at the point I got stuck:

package main

import (

    "github.com/gorilla/mux"
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
)

func setCookie(w http.ResponseWriter, name string, value string) {
    ...
    http.SetCookie(w, &cookie)
}

func handler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        // setCookie() works here
        // but I cannot access w.Header().Get("X-FOO")

        next.ServeHTTP(w, r)

        // I can access w.Header().Get("X-FOO") here
        // but setCookie() does not cookie the user's browser

        // If I could do it all in one place, this is what I would do:
        if r.Method == "POST" && r.URL.String() == "/login" {
            foo := w.Header().Get("X-FOO")
            setCookie(w, "MYAPPFOO", foo)

        }
    })
}

func main() {

    r := mux.NewRouter()
    r.Use(handler)
    proxy := httputil.NewSingleHostReverseProxy("https://baz.example.com/")
    r.PathPrefix("/").Handler(proxy)
    log.Fatal(http.ListenAndServe(":9001", r))
}

作为旁注,我能够按照上一个问题的注释中的建议使用 ReverseProxy.ModifyResponse 进行此操作,但我真的很想使用中间件来实现此目的,以保持代码从config clean动态创建代理.(不在示例代码中)

As a side note, I was able to make this work with ReverseProxy.ModifyResponse as recommended in the comments of my last question but I'd really like to achieve this with middleware to keep the code that dynamically creates proxies from config clean. (not in the example code)

推荐答案

摘录自 http.ResponseWriter 方法:(添加了重点)

  • Header()http.Header :

在调用WriteHeader(或Write)之后更改标题映射没有效果,除非修改后的标题是预告片.

Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.

  • WriteHeader(statusCode int):

    WriteHeader 发送具有所提供状态的HTTP响应标头代码.

  • 写入([] byte)(int,错误):

    如果尚未调用WriteHeader,则写入个调用写入数据之前,先使用WriteHeader(http.StatusOK).

  • 这应该突出显示为什么您无法在 next.ServeHTTP(w,r)调用之后设置cookie的原因,这是该代码执行的中间件链中的处理程序之一呼叫是直接或间接呼叫 WriteHeader Write .

    This should highlight the reason why, you can't set a cookie after the next.ServeHTTP(w, r) call, which is that one of the handlers in the middleware chain executed by that call is calling either WriteHeader or Write directly or indirectly.

    因此要在 next.ServeHTTP(w,r)调用之后的 之后设置cookie,您需要确保中间件链调用中没有任何处理程序原始 http.ResponseWriter 实例上的 WriteHeader Write .一种方法是将原始实例包装在自定义的 http.ResponseWriter 实现中,该实现将推迟编写响应,直到完成cookie设置为止

    So to be able set the cookie after the next.ServeHTTP(w, r) call you need to make sure that none of the handlers in the middleware chain calls WriteHeader or Write on the original http.ResponseWriter instance. One way to do this is to wrap the original instance in a custom http.ResponseWriter implementation that will postpone the writing of the response until after you're done with setting the cookie.

    例如这样的东西:

    type responsewriter struct {
        w    http.ResponseWriter
        buf  bytes.Buffer
        code int
    }
    
    func (rw *responsewriter) Header() http.Header {
        return rw.w.Header()
    }
    
    func (rw *responsewriter) WriteHeader(statusCode int) {
        rw.code = statusCode
    }
    
    func (rw *responsewriter) Write(data []byte) (int, error) {
        return rw.buf.Write(data)
    }
    
    func (rw *responsewriter) Done() (int64, error) {
        if rw.code > 0 {
            rw.w.WriteHeader(rw.code)
        }
        return io.Copy(rw.w, &rw.buf)
    }
    

    您将在中间件中这样使用它:

    And you would use it like this in your middleware:

    func handler(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            rw := &responsewriter{w: w}
            next.ServeHTTP(rw, r)
    
            if r.Method == "POST" && r.URL.String() == "/login" {
                foo := rw.Header().Get("X-FOO")
                setCookie(rw, "MYAPPFOO", foo)
            }
    
            if _, err := rw.Done(); err != nil {
                log.Println(err)
            }
        })
    }
    

    这篇关于使用自定义的http.ResponseWriter基于代理请求的响应编写cookie?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆