这个filter给服务增加CORS支持:
- 使用正则表达式匹配HTTP请求头中的
Origin是否被允许 - 如果允许,添加CORS相关响应头
- 如果不允许,则无操作。浏览器因为看不到CORS相关响应头而拒绝请求获取响应。
// TODO: use restful.CrossOriginResourceSharing
// See github.com/emicklei/go-restful/blob/master/examples/cors/restful-CORS-filter.go, and
// github.com/emicklei/go-restful/blob/master/examples/basicauth/restful-basic-authentication.go
// Or, for a more detailed implementation use https://github.com/martini-contrib/cors
// or implement CORS at your proxy layer.
// WithCORS is a simple CORS implementation that wraps an http Handler.
// Pass nil for allowedMethods and allowedHeaders to use the defaults. If allowedOriginPatterns
// is empty or nil, no CORS support is installed.
func WithCORS(handler http.Handler, allowedOriginPatterns []string, allowedMethods []string, allowedHeaders []string, exposedHeaders []string, allowCredentials string) http.Handler {
// 如果无Origin限制,默认拒绝
if len(allowedOriginPatterns) == 0 {
return handler
}
// + 生成允许的orgin的正则表达式
allowedOriginPatternsREs := allowedOriginRegexps(allowedOriginPatterns)
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// 获取HTTP请求头 Origin
origin := req.Header.Get("Origin")
if origin != "" {
allowed := false
// 如果Origin配置其中一个正则,则allowed=true;
for _, re := range allowedOriginPatternsREs {
if allowed = re.MatchString(origin); allowed {
break
}
}
// 添加相关CORS响应头,请求被允许; 否则不添加,请求会被浏览器拒绝
if allowed {
w.Header().Set("Access-Control-Allow-Origin", origin)
// Set defaults for methods and headers if nothing was passed
if allowedMethods == nil {
allowedMethods = []string{"POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"}
}
if allowedHeaders == nil {
allowedHeaders = []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "X-Requested-With", "If-Modified-Since"}
}
if exposedHeaders == nil {
exposedHeaders = []string{"Date"}
}
w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowedMethods, ", "))
w.Header().Set("Access-Control-Allow-Headers", strings.Join(allowedHeaders, ", "))
w.Header().Set("Access-Control-Expose-Headers", strings.Join(exposedHeaders, ", "))
w.Header().Set("Access-Control-Allow-Credentials", allowCredentials)
// Stop here if its a preflight OPTIONS request
if req.Method == "OPTIONS" {
w.WriteHeader(http.StatusNoContent)
return
}
}
}
// Dispatch to the next handler
handler.ServeHTTP(w, req)
})
}
allowedOriginPatternsREs := allowedOriginRegexps(allowedOriginPatterns)
根据string数组生成正则表达式regexp.Regexp数组
func allowedOriginRegexps(allowedOrigins []string) []*regexp.Regexp {
// + 编译string => regexp.Regexp
res, err := compileRegexps(allowedOrigins)
if err != nil {
klog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(allowedOrigins, ","), err)
}
return res
}
res, err := compileRegexps(allowedOrigins)
// Takes a list of strings and compiles them into a list of regular expressions
func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
regexps := []*regexp.Regexp{}
for _, regexpStr := range regexpStrings {
r, err := regexp.Compile(regexpStr)
if err != nil {
return []*regexp.Regexp{}, err
}
regexps = append(regexps, r)
}
return regexps, nil
}
相关API
正则表达式
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(gopher){2}`)
fmt.Println(re.MatchString("gopher"))
fmt.Println(re.MatchString("gophergopher"))
fmt.Println(re.MatchString("gophergophergopher"))
//Output:
//
//false
//true
//true
}