How to handle preflight CORS requests on a Go server

huangapple go评论90阅读模式
英文:

How to handle preflight CORS requests on a Go server

问题

所以我正在用Go编写这个RESTful后端,它将被跨站HTTP请求调用,也就是来自另一个站点提供的内容(实际上只是另一个端口,但同源策略会生效,所以我们在这里)。

在这种情况下,用户代理有时会发送预检OPTIONS请求,以检查实际请求是否安全。

我的问题是如何在Go上下文中最好地处理并适当地响应这些预检请求。我构思的方式并不太优雅,我想知道是否有其他方法我没有考虑到。

使用标准的net/http包,我可以在处理程序函数中检查请求方法,可能像这样:

func AddResourceHandler(rw http.ResponseWriter, r *http.Request) {
  switch r.Method {
  case "OPTIONS":
    // 处理预检请求
  case "PUT":
    // 响应实际请求
  }
}

我还可以使用Gorillamux包,并为每个相关的URL路径注册一个预检的"OPTIONS"处理程序。

r := mux.NewRouter()
r.HandleFunc("/someresource/item", AddResourceHandler).Methods("PUT")
r.HandleFunc("/someresource/item", PreflightAddResourceHandler).Methods("OPTIONS")

也许对这个问题的回答很简单:是的,这些是你的基本选择。但我想知道是否有一些我不知道的最佳实践。

英文:

So I'm writing this RESTful backend in Go, which will be called with cross-site HTTP requests, i.e. from content served by another site (actually, just another port, but the same-origin policy kicks in, so here we are).

In this scenario, the user agent will, in some cases, send preflight OPTIONS requests to check if the actual request is safe to send.

My question is how to best deal with, and adequately respond to, these preflight requests in a Go context. The ways I have conceived don't feel very elegant, and I'm wondering if there's some other approach to this that I haven't thought of.

Using the standard net/http package, I can check the request method in the handler func, perhaps like this:

func AddResourceHandler(rw http.ResponseWriter, r *http.Request) {
  switch r.Method {
  case "OPTIONS":
    // handle preflight
  case "PUT":
    // respond to actual request
  }
}

I can also use Gorilla's mux package, and register a preflight "OPTIONS" handler for each relevant URL path.

r := mux.NewRouter()
r.HandleFunc("/someresource/item", AddResourceHandler).Methods("PUT")
r.HandleFunc("/someresource/item", PreflightAddResourceHandler).Methods("OPTIONS")

Maybe the response to this question is simply: Yup, those are your basic options. But I thought there might be some best practice around this that I'm unaware of.

答案1

得分: 35

一种简单的方法是将逻辑分离并重用你定义的CORS处理程序,可以通过包装你的REST处理程序来实现。例如,如果你正在使用net/http和Handle方法,你可以这样做:

func corsHandler(h http.Handler) http.HandlerFunc {
  return func(w http.ResponseWriter, r *http.Request) {
    if (r.Method == "OPTIONS") {
      // 在这里处理预检请求
    } else {
      h.ServeHTTP(w, r)
    }
  }
}

你可以这样包装

http.Handle("/endpoint/", corsHandler(restHandler))
英文:

One simple way to separate out your logic and re-use the CORS handler you define would be to wrap your REST handler. For example, if you're using net/http and the Handle method you could always do something like:

func corsHandler(h http.Handler) http.HandlerFunc {
  return func(w http.ResponseWriter, r *http.Request) {
    if (r.Method == "OPTIONS") {
      //handle preflight in here
    } else {
      h.ServeHTTP(w,r)
    }
  }
}

You can wrap like this:

http.Handle("/endpoint/", corsHandler(restHandler))

答案2

得分: 10

这是一个对我有效的代码片段:

addCorsHeader(res)
if req.Method == "OPTIONS" {
    res.WriteHeader(http.StatusOK)
    return
} else {
    h.APIHandler.ServeHTTP(res, req)
}

func addCorsHeader(res http.ResponseWriter) {
    headers := res.Header()
    headers.Add("Access-Control-Allow-Origin", "*")
    headers.Add("Vary", "Origin")
    headers.Add("Vary", "Access-Control-Request-Method")
    headers.Add("Vary", "Access-Control-Request-Headers")
    headers.Add("Access-Control-Allow-Headers", "Content-Type, Origin, Accept, token")
    headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
}

请注意,这只是一个代码片段,可能需要根据你的具体需求进行适当的修改。

英文:

Here's a snippet that worked for me:

addCorsHeader(res)
if req.Method == "OPTIONS" {
	res.WriteHeader(http.StatusOK)
	return
} else {
	h.APIHandler.ServeHTTP(res, req)
}


func addCorsHeader(res http.ResponseWriter) {
	headers := res.Header()
	headers.Add("Access-Control-Allow-Origin", "*")
	headers.Add("Vary", "Origin")
	headers.Add("Vary", "Access-Control-Request-Method")
	headers.Add("Vary", "Access-Control-Request-Headers")
	headers.Add("Access-Control-Allow-Headers", "Content-Type, Origin, Accept, token")
	headers.Add("Access-Control-Allow-Methods", "GET, POST,OPTIONS")
}

答案3

得分: 9

我个人觉得为每个可能收到OPTIONS请求的路径添加预检路由很繁琐,所以我选择将处理程序添加到请求多路复用器(在这种情况下是Gorilla)处理的任何OPTIONS方法中,方法如下:

router.Methods("OPTIONS").HandlerFunc(
	func(w http.ResponseWriter, r *http.Request){
		myHttpLib.OptionsForBrowserPreflight(w, r)
	})

需要注意的是,这应该在映射其他路由之前进行,因为如果你首先注册了一个像"/foo"这样的路径,而没有为该路由指定任何方法,那么对"/foo"的OPTIONS请求将会运行而不是你的预检代码,因为它是第一个匹配项。

通过这种方式,你可以:(1)只需为所有预检请求进行一次路由注册,(2)只需在一个地方重用代码并应用逻辑/规则来处理OPTIONS请求。

英文:

I personally find it tedious to add preflight routes for every path that will get an OPTIONS request, so instead I simply add my handler to any OPTIONS method that the request multiplexer (Gorilla in this case) handles as follows:

router.Methods("OPTIONS").HandlerFunc(
	func(w http.ResponseWriter, r *http.Request){
	myHttpLib.OptionsForBrowserPreflight(w, r)
})

Note though, that this should come before mapping other routes because if, for example, you have a path like "/foo" and you register that first without specifying any methods for that route, then an OPTIONS request to "/foo" would run instead of your pre-flight code because its the first match.

This way you can: (1) have just one routing registration for all pre-flights, and (2) have one handler to reuse code and apply logic/rules in a single place for OPTIONS requests.

答案4

得分: 6

gorilla/handlers 还有一个很好的CORS处理程序:cors.go

示例用法:

import (
    "net/http"

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

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/users", UserEndpoint)
    r.HandleFunc("/projects", ProjectEndpoint)

    // 将CORS中间件应用于我们的顶级路由器,并使用默认值。
    http.ListenAndServe(":8000", handlers.CORS()(r))
}
英文:

gorilla/handlers also has a nice CORS handler: cors.go

Example usage:

import (
    "net/http"

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

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/users", UserEndpoint)
    r.HandleFunc("/projects", ProjectEndpoint)

    // Apply the CORS middleware to our top-level router, with the defaults.
    http.ListenAndServe(":8000", handlers.CORS()(r))
}

答案5

得分: 1

好的,以下是翻译好的内容:

嗯,我的Vue.js应用程序中没有任何东西起作用,所以我做了以下操作。

cors := cors.New(cors.Options{
		AllowedOrigins:   []string{"*"}, //viper.GetString("ORIGIN_ALLOWED")
		AllowedHeaders:   []string{"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token", "Authorization"},
		AllowedMethods:   []string{"GET", "PATCH", "POST", "PUT", "OPTIONS", "DELETE"},
		Debug:            true,
		AllowCredentials: true,
	})

cors.Handler(corsMiddle())
func corsMiddle() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
		if request.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
		}
	})
}
英文:

Well, nothing worked from me from my Vue.js application so I did this.

cors := cors.New(cors.Options{
		AllowedOrigins:   []string{"*"}, //viper.GetString("ORIGIN_ALLOWED")
		AllowedHeaders:   []string{"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token", "Authorization"},
		AllowedMethods:   []string{"GET", "PATCH", "POST", "PUT", "OPTIONS", "DELETE"},
		Debug:            true,
		AllowCredentials: true,
	})

cors.Handler(corsMiddle())
func corsMiddle() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
		if request.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
		}
	})
}

huangapple
  • 本文由 发表于 2014年4月10日 03:20:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/22972066.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定