在 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
}
在下面的代码示例中,我使用正则表达式从给定的 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
}