在 Golang 中使用正则表达式从 URL 中提取子域

Extract subdomain from URL using regexp in Golang

在下面的代码示例中,我使用正则表达式从给定的 URL 中提取子域名。这个示例有效,但我认为我在编译正则表达式时没有正确完成,主要是在我插入 'virtualHost' 变量的地方。有什么建议吗?

package main

import (
    "fmt"
    "regexp"
)

var (
    virtualHost string
    domainRegex *regexp.Regexp
)

func extractSubdomain(host string) string {
    matches := domainRegex.FindStringSubmatch(host)
    if matches != nil && len(matches) > 1 {
        return matches[1]
    }
    return ""
}

func init() {
    // virtualHost = os.GetEnv("VIRTUAL_HOST")
    virtualHost = "login.localhost:3000"

    domainRegex = regexp.MustCompile(`^(?:https?://)?([-a-z0-9]+)(?:\.` + virtualHost + `)*$`)
}

func main() {
    // host := req.host
    host := "http://acme.login.localhost:3000"

    if result := extractSubdomain(host); result != "" {
        fmt.Printf("Subdomain detected: %s\n", result)
        return
    }

    fmt.Println("No subdomain detected")
}

url package has a function parse that allows you to parse an URL. The parsed URL instance has a method Hostname 将 return 你的主机名。

package main

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

func main() {
    u, err := url.Parse("http://login.localhost:3000")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(u.Hostname())
}

输出:

login.localhost

https://play.golang.com/p/3R1TPyk8qck

更新:

我之前的回答只涉及解析主机名。从那时起,我一直在使用以下库从主机名中解析域后缀。一旦你有了它,剥离域并只留下子域前缀就很简单了。

https://pkg.go.dev/golang.org/x/net/publicsuffix

我发现要准确识别子域和主机之间的区别可能有点棘手,如果没有这个可以识别常见后缀的包的帮助。例如,在内部我们可能有一个来自 kubernetes ingress 的域:

foo.bar.host.kube.domain.com.au

主机为“主机”,子域名为“foo.bar”。即使在 publicsuffix 库的帮助下,它也不知道“kube”是内部域组件的一部分。所以你必须添加更多你自己的提示来匹配。

这是我用过的


func getSubdomain(r *http.Request) string {
    //The Host that the user queried.
    host := r.URL.Host
    host = strings.TrimSpace(host)
    //Figure out if a subdomain exists in the host given.
    hostParts := strings.Split(host, ".")

    fmt.Println("host parts",hostParts)

    lengthOfHostParts := len(hostParts)

    // scenarios
    // A. site.com  -> length : 2
    // B. www.site.com -> length : 3
    // C. www.hello.site.com -> length : 4

    if lengthOfHostParts == 4 {
        return strings.Join([]string{hostParts[1]},"") // scenario C
    }
    
    if lengthOfHostParts == 3 { // scenario B with a check
        subdomain := strings.Join([]string{hostParts[0]},"")
        
        if subdomain == "www" {
            return ""
        } else {
            return subdomain
        }
    }

    return "" // scenario A
}