获取当前请求URL的方案

时间:2016-11-27 07:21:39

标签: go

在Ruby / Rack中,我能够get the scheme of the current request URL from scheme#request。但是,在Go中,http.Request.URL.Scheme返回一个空字符串:

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "%#v\n", r.URL.Scheme) // Always shows empty string
}

如何获取当前请求网址的方案?

6 个答案:

答案 0 :(得分:2)

快速grep表示r.URL.Scheme永远不会设置为net/http中任何位置的空字符串以外的任何内容。我个人认为应该尽可能地,但显然我有少数意见。

如果您自己使用http.ListenAndServeTLS()打开了TLS监听器,那么可能您知道该方案已经是https。在这种情况下,您可以使用填充r.URL.Scheme的简单中间件处理程序。

func AlwaysHTTPS(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        r.URL.Scheme = "https"

        next.ServeHTTP(w, r)
    })
}

如果您在Web服务器后面运行,那么它可以在X-Forwarded-Proto之类的标头中传递请求协议。在这种情况下,您可以使用像大猩猩的handlersProxyHeaders()这样的处理程序来填充缺少的字段。

使用gorilla mux的示例:

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    r.Use(handlers.ProxyHeaders)

    http.Handle("/", r)
    log.Fatal(http.ListenAndServe("[::]:8009", nil))
}

来自其评论:

  

ProxyHeaders检查常见的反向代理头并设置HTTP请求结构中的相应字段。这些是用于远程(客户端)IP地址的X-Forwarded-For和X-Real-IP,用于该方案的X-Forwarded-Proto或X-Forwarded-Scheme(http | https)和RFC7239 Forwarded报头,其中可能包括客户端IP和方案。

     

注意:此中间件只能在nginx,HAProxy或Apache等反向代理后面使用。没有(或被配置为不)从客户端请求剥离这些头的反向代理,或者从远程客户端“按原样”接受这些头的情况(例如,当Go不在代理后面时),可以表现为漏洞如果您的应用程序使用这些标头来验证请求的“可信度”。

答案 1 :(得分:1)

localhost是URL形成的特例。如果你的客户是localhost,那么它将是空的。

net.http package doc

  

作为特例,如果req.URL.Host是" localhost" (有或没有端口号),然后返回一个nil URL和nil错误。

获取所需url / uri信息的方法是直接从http.Request获取。例如:

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "%s\n", r.Host)                    
}                                                     

答案 2 :(得分:1)

要提供 http https ,您需要同时调用这两个服务功能
http.ListenAndServe()http.ListenAndServeTLS()使用相同的处理程序 因为如果您仅使用其中的一种(如问题示例),那么您只会在 http http.ListenAndServe()协议和 https http.ListenAndServeTLS()协议中列出>,如果您尝试使用其他协议与服务器联系,那么它将无法通过

,并且由于 https HTTP over TLS,因此*http.Request具有TLS属性,该属性将为您提供*tls.ConnectionState的TLS信息是用于此请求的,那么,如果您想知道客户端如何与您的服务器联系,可以检查请求的TLS属性, 如果请求是通过 https 发出的,则不会为零, 如果请求是通过 http 发出的,则TLS属性将为nil, 因为使用TLS进行请求的唯一方法是使用 https 协议

func handler(w http.ResponseWriter, r *http.Request) {
   if r.TLS == nil {
       // the scheme was http
   } else {
       // the scheme was https
   }                 
}  

func main() {
    http.HandleFunc("/", handler)
    go func(){ 
        log.Fatal(http.ListenAndServeTLS(":8443","localhost.crt", "localhost.key", nil)) 
    }()
    log.Fatal(http.ListenAndServe(":8080", nil))
}                                                   

答案 3 :(得分:0)

由于您使用ListenAndServe而不是ListenAndServeTLS,因此可以将您的方案安全地假定为http。如果同时使用tls和非tls版本,则可以使用r.TLS并将其检查为null以了解是否已建立TLS。如果您的go应用程序在反向代理后面运行,则实际上无法知道该方案是什么。

答案 4 :(得分:0)

http标头X-Forwarded-Proto将具有http或https

答案 5 :(得分:-2)

这是因为您正在访问HTTP服务器,所以:

GET / HTTP/1.1
Host: localhost:8080

在这种情况下,基于解析你得到的是来自Go的http.Request.URL的原始URL。为什么要这样做是因为您从相对路径访问URL,因此URL对象中缺少Host或Scheme。

如果您确实想要获取HTTP主机,则可能必须访问http.Request结构的Host属性。见http://golang.org/pkg/http/#Request

因为它不是直接可用的,但你仍然可以组装它:

u := r.URL

// The scheme can be http/https because that's depends on protocol your server handles.
u.Scheme = "http"