
当web应用发起包含自定义http头的跨域请求时,浏览器会首先发送一个“预检(preflight)”options请求。本文将深入探讨这一机制,并通过go语言服务器端的具体案例,演示如何正确处理这些预检请求,以确保带有自定义`authorization`等头的cors get请求能够顺利执行,避免常见的404或cors错误。
跨域资源共享(CORS)是一种W3C标准,它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了同源策略的限制。然而,并非所有跨域请求都会直接发送。对于某些“非简单请求”,浏览器会先自动发送一个HTTP OPTIONS请求到目标资源,以确定实际请求是否安全可发送。这个OPTIONS请求被称为“预检请求”(Preflight Request)。
非简单请求的条件通常包括:
当一个AngularJS应用(或其他前端框架)发起一个带有Authorization自定义头的GET请求时,即使GET本身是简单请求方法,但由于Authorization头的存在,该请求会被视为非简单请求,从而触发浏览器发送预检OPTIONS请求。
考虑以下前端AngularJS代码,它尝试向/banks接口发送一个带有Authorization头的GET请求:
$http.get(env.apiURL()+'/banks', {
headers: {
'Authorization': 'Bearer '+localStorageService.get('access_token')
}
})当这个请求被触发时,浏览器实际会先发送一个OPTIONS请求,其结构大致如下:
OPTIONS /banks HTTP/1.1 Host: localhost:8080 Connection: keep-alive Access-Control-Request-Method: GET Origin: http://localhost:8081 Access-Control-Request-Headers: accept, authorization // 注意这里包含了 'authorization' Accept: */* // ... 其他头部
如果服务器端没有明确处理这个OPTIONS请求,或者路由配置中没有对应的OPTIONS处理器,服务器可能会返回一个404 Not Found错误,因为默认情况下它只知道如何处理GET请求。例如,一个Go语言服务器可能只配置了GET路由:
r := mux.NewRouter()
r.HandleFunc("/banks", RetrieveAllBank).Methods("GET")
http.ListenAndServe(":8080", r)在这种情况下,OPTIONS /banks请求将不会匹配到任何路由,导致服务器返回404 Not Found。即使服务器在后续的GET请求中设置了正确的CORS响应头(如Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers),由于预检请求的失败,浏览器会阻止实际的GET请求发出,并在控制台报告CORS错误。
服务器返回的404响应可能看起来像这样,即使其中包含了CORS相关的响应头,也无济于事,因为请求方法不匹配:
HTTP/1.1 404 Not Found Access-Control-Allow-Headers: Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE Access-Control-Allow-Origin: http://localhost:8081 Content-Type: text/plain; charset=utf-8 Date: Mon, 17 Mar 2014 11:05:20 GMT Content-Length: 19
解决此问题的关键在于服务器端必须明确地处理OPTIONS请求。当服务器收到OPTIONS请求时,它应该检查请求头中的Access-Control-Request-Method和Access-Control-Request-Headers,并用适当的CORS响应头进行回复,告知浏览器允许哪些方法和头部。
以下是Go语言服务器的修改示例,通过实现http.Handler接口来拦截并处理OPTIONS请求:
package main
import (
"fmt"
"github.com/gorilla/mux"
"log"
"net/http"
)
// RetrieveAllBank 模拟实际的GET请求处理器
func RetrieveAllBank(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the banks API!")
}
// MyServer 结构体,用于包装mux.Router并实现http.Handler接口
type MyServer struct {
r *mux.Router
}
// ServeHTTP 方法实现http.Handler接口
func (s *MyServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// 设置CORS响应头,允许指定来源、方法和头部
if origin := req.Header.Get("Origin"); origin == "http://localhost:8081" {
rw.Header().Set("Access-Control-Allow-Origin", origin)
rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
rw.Header().Set("Access-Control-Allow-Headers",
"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") // 确保包含所有自定义头
rw.Header().Set("Access-Control-Max-Age", "86400") // 预检请求的缓存时间,单位秒
}
// 如果是预检OPTIONS请求,则直接返回,不继续处理路由
if req.Method == "OPTIONS" {
rw.WriteHeader(http.StatusOK) // 返回200 OK
return
}
// 如果不是OPTIONS请求,则交给Gorilla Mux路由器处理
s.r.ServeHTTP(rw, req)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/banks", RetrieveAllBank).Methods("GET") // 注册GET方法路由
fmt.Println("Server listening on :8080")
// 将自定义的MyServer作为http.ListenAndServe的处理器
log.Fatal(http.ListenAndServe(":8080", &MyServer{r}))
}
代码解析:
if req.Method == "OPTIONS" {
rw.WriteHeader(http.StatusOK) // 返回200 OK
return
}当请求方法是OPTIONS时,我们设置了必要的CORS头后,直接返回http.StatusOK(200 OK),并且不再将请求传递给底层的mux.Router处理。这告诉浏览器,预检成功,实际请求可以安全发送。
通过以上修改,前端AngularJS应用发出的带有自定义Authorization头的GET请求,在经过预检OPTIONS请求成功后,将能够顺利到达服务器并获取数据。理解并正确处理CORS预检请求是构建健壮跨域Web应用的关键一步。
以上就是深度解析CORS预检请求:解决自定义Header导致的OPTIONS请求失败的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号