Golang中如何全局启用OPTIONS请求

Golang中如何全局启用OPTIONS请求 我正在使用 Go 1.22 中的 HTTP 处理器,但是 OPTIONS 请求会被自动拒绝,并向客户端返回 405 状态码。因此,我无法从我的单页应用(SPA)中调用任何 POST 端点。有没有办法在不使用第三方工具或库的情况下,全局自动允许 OPTIONS 请求?

4 回复

Ester:

有没有办法自动允许OPTIONS请求

我是这样处理的:

	switch r.Method {
	case "GET":
		Read(w, r)
	case "DELETE":
		Delete(w, r)
	case "POST":
		Create(w, r)
	case "PUT":
		Update(w, r)
	case "OPTIONS":
		// bugfix (handshake)
	default:
		Read(w, r)
	}

更多关于Golang中如何全局启用OPTIONS请求的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你可以直接使用类似这样的代码:

func newAppMux() *http.ServeMux {
	router := http.NewServeMux()
	// 全局 OPTIONS 请求处理器
	router.HandleFunc("OPTIONS /", handleOptions)
	return router
}

func handleOptions(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "https://myapp.com")
	w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
	w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization")
	w.WriteHeader(http.StatusOK)
}

关于模式中的“/”需要注意。根据文档

特殊的通配符 {$} 仅匹配 URL 的结尾。例如,模式“/{$}”仅匹配路径“/”,而模式“/”匹配所有路径。

因此,该模式会匹配该HTTP方法(“OPTIONS”)下的所有路径。如需进一步了解CORS以及可能需要设置哪些请求头,请查阅此文档

不过,没有必要为 CORS 声明一个全局的 OPTIONS 处理器;而且,与其手动实现 CORS(这容易出错),我建议使用专门的库。下面是一个示例:

package main

import (
  "io"
  "log"
  "net/http"

  "github.com/jub0bs/cors"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("GET /hello", handleHello) // 注意:未配置 CORS

  // 创建 CORS 中间件
  corsMw, err := cors.NewMiddleware(cors.Config{
    Origins:        []string{"https://example.com"},
    Methods:        []string{http.MethodGet, http.MethodPost},
    RequestHeaders: []string{"Authorization"},
  })
  if err != nil {
    log.Fatal(err)
  }
  corsMw.SetDebug(true) // 开启调试模式(可选)

  api := http.NewServeMux()
  api.HandleFunc("GET /users", handleUsersGet)
  api.HandleFunc("POST /users", handleUsersPost)
  mux.Handle("/api/", http.StripPrefix("/api", corsMw.Wrap(api))) // 注意:此处使用无方法模式

  log.Fatal(http.ListenAndServe(":8080", mux))
}

func handleHello(w http.ResponseWriter, _ *http.Request) {
  io.WriteString(w, "Hello, World!")
}

func handleUsersGet(w http.ResponseWriter, _ *http.Request) {
  // 省略
}

func handleUsersPost(w http.ResponseWriter, _ *http.Request) {
  // 省略
}

在Go中,HTTP处理器默认会拒绝OPTIONS请求并返回405状态码。要全局启用OPTIONS请求,可以通过自定义处理器包装器来实现。以下是一个示例,展示如何创建一个中间件来自动处理OPTIONS请求:

package main

import (
    "net/http"
)

// 允许OPTIONS请求的中间件
func allowOptions(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 如果是OPTIONS请求,直接返回200状态码并设置CORS头部
        if r.Method == http.MethodOptions {
            w.Header().Set("Access-Control-Allow-Origin", "*")
            w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
            w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
            w.WriteHeader(http.StatusOK)
            return
        }
        // 否则调用下一个处理器
        next.ServeHTTP(w, r)
    })
}

func main() {
    // 创建路由器
    mux := http.NewServeMux()
    
    // 注册路由
    mux.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
        if r.Method == http.MethodPost {
            w.Write([]byte("POST请求成功"))
        } else {
            http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
        }
    })
    
    // 使用中间件包装路由器
    handler := allowOptions(mux)
    
    // 启动服务器
    http.ListenAndServe(":8080", handler)
}

这个中间件会拦截所有请求,如果是OPTIONS请求,则直接返回200状态码并设置必要的CORS头部。对于其他请求,会正常传递给后续的处理器。

如果需要更细粒度的控制,可以在每个路由处理器中显式处理OPTIONS请求:

func apiHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodOptions:
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        w.WriteHeader(http.StatusOK)
    case http.MethodPost:
        w.Write([]byte("POST请求成功"))
    default:
        http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
    }
}

这两种方法都可以在不使用第三方库的情况下全局处理OPTIONS请求。第一种方法通过中间件实现更简洁的全局处理,第二种方法提供了更细粒度的控制。

回到顶部