英文:
CORS preflight mysteriously failing with gorilla/handlers
问题
我正在通过Heroku发布我的应用程序的Golang API。无法使我的Web应用程序(Flutter/Dart堆栈)从我的API获得成功响应。但是,我可以使用本地的curl命令获得成功的响应。我已经阅读了几篇关于修改Go mux服务器并添加正确标头的帖子,但对我没有起作用。我甚至在我的curl请求中看到了这些标头返回。希望能得到一些帮助,因为这让我进展缓慢。
这是创建服务器的主要类:
import (
"api/preventative_care"
"api/user"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"log"
"net/http"
"os"
)
func main() {
log.SetFlags(log.LstdFlags | log.Llongfile)
router := mux.NewRouter()
// Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure)
headersOk := handlers.AllowedHeaders([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
originsOk := handlers.AllowedOrigins([]string{"*"})
router.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
log.Println("Up and running!")
})
router.HandleFunc("/api/login", user.LoginHandler).Methods("GET")
router.HandleFunc("/api/recommendations", preventative_care.RecommendationHandler).Methods("GET")
var port = os.Getenv("PORT")
log.Printf("Starting application on port %s\n", port)
//log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), router))
log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), handlers.CORS(originsOk, headersOk, methodsOk)(router)))
}
调用此API的Dart代码如下:
Map<String, String> headers = {
"content-type": "application/json",
"username": username,
"password": password
};
final response = await http.get(Uri.parse(uri), headers: headers);
我将Web应用程序和API托管在两个单独的Heroku Dynos中。当我使用curl从本地访问API时,我看到以下内容:
$ > curl -iXGET https://my-app-api.herokuapp.com/api/login -H "username:hello" -H "password:pizza"
HTTP/1.1 200 OK
Server: Cowboy
Connection: keep-alive
Content-Type: application/json
Date: Thu, 18 Nov 2021 23:39:56 GMT
Content-Length: 160
Via: 1.1 vegur
我以为我应该看到添加了Access-Control-Allow-Origin: *
标头,但实际上没有,但我仍然得到了200的成功响应。然而,当我尝试使用我的Web应用程序在Google Chrome中的登录界面访问API时,我看到以下错误:
访问'https://my-app-api.herokuapp.com/api/login'的XMLHttpRequest被来自'https://my-app-staging.herokuapp.com'的源阻止,因为CORS策略:预检请求的响应未通过访问控制检查:所请求的资源上不存在'Access-Control-Allow-Origin'标头。
不知道为什么-就好像Chrome删除了标头或其他什么东西?
编辑:我还尝试使用CURL发送预检请求,我看到了正确的标头-但仍然显示4XX错误。
$ > curl -H "Access-Control-Request-Method: GET" -H "Origin: https://my-app-staging.herokuapp.com" --head https://my-app-api.herokuapp.com/api/login
HTTP/1.1 405 Method Not Allowed
Server: Cowboy
Connection: keep-alive
Access-Control-Allow-Origin: *
Date: Fri, 19 Nov 2021 00:51:52 GMT
Via: 1.1 vegur
所以现在我真的不确定了。
英文:
I am publishing a Golang API for my application through Heroku. Not able to get my webapp (Flutter/Dart stack) to actually get a successful response from my api. However I am able to get a successful response using curl commands from local. I've read through several posts about altering the Go mux server and adding the correct headers but this has not worked for me. I even see these headers returned back during my curl requests. Could really use some help as this is slowing me down.
essentially this is my main class which creates the server
import (
"api/preventative_care"
"api/user"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"log"
"net/http"
"os"
)
func main() {
log.SetFlags(log.LstdFlags | log.Llongfile)
router := mux.NewRouter()
// Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure)
headersOk := handlers.AllowedHeaders([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
originsOk := handlers.AllowedOrigins([]string{"*"})
router.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
log.Println("Up and running!")
})
router.HandleFunc("/api/login", user.LoginHandler).Methods("GET")
router.HandleFunc("/api/recommendations", preventative_care.RecommendationHandler).Methods("GET")
var port = os.Getenv("PORT")
log.Printf("Starting application on port %s\n", port)
//log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), router))
log.Fatal(http.ListenAndServe(":" + os.Getenv("PORT"), handlers.CORS(originsOk, headersOk, methodsOk)(router)))
}
And the dart code which calls this API looks like this:
Map<String, String> headers = {
"content-type": "application/json",
"username": username,
"password": password
};
final response = await http.get(Uri.parse(uri), headers: headers);
I'm hosting both the webapp and the API in 2 separate Heroku Dynos. When I hit the API from my local using curl I see below:
$ > curl -iXGET https://my-app-api.herokuapp.com/api/login -H "username:hello" -H "password:pizza"
HTTP/1.1 200 OK
Server: Cowboy
Connection: keep-alive
Content-Type: application/json
Date: Thu, 18 Nov 2021 23:39:56 GMT
Content-Length: 160
Via: 1.1 vegur
I thought I was supposed to see see the Header Access-Control-Allow-Origin: *
added there but it's not yet I still get 200 success back. However when I try to use my Webapp to hit the API from a login screen using Google Chrome I see this error:
> Access to XMLHttpRequest at 'https://my-app-api.herokuapp.com/api/login' from origin 'https://my-app-staging.herokuapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
No idea - it's like the header is being removed by Chrome or something maybe?
EDIT: I've also tried sending a preflight request using CURL and I am seeing the correct headers - however it's still showing me 4XX error.
$ > curl -H "Access-Control-Request-Method: GET" -H "Origin: https://my-app-staging.herokuapp.com" --head https://my-app-api.herokuapp.com/api/login
HTTP/1.1 405 Method Not Allowed
Server: Cowboy
Connection: keep-alive
Access-Control-Allow-Origin: *
Date: Fri, 19 Nov 2021 00:51:52 GMT
Via: 1.1 vegur
So now I'm REALLY not sure
答案1
得分: 4
TL;DR
gorilla/handlers目前不支持通配符Access-Control-Allow-Headers
。您必须明确指定所有允许的标头。在您的情况下,您应该使用以下代码:
handlers.AllowedHeaders([]string{"content-type", "username", "password"})
更多细节
Fetch标准在2016年(对于非凭证请求)添加了对Access-Control-Allow-Headers
标头中通配符的支持。现代大多数浏览器现在都支持此功能。
然而,gorilla/handlers似乎还没有跟上规范。如果您检查handlers.AllowedHeaders函数和*cors.ServeHTTP方法的源代码,您会发现没有对"*"
值进行特殊处理:它被当作字面值处理。因此,CORS中间件会在您的预检请求提供的请求标头(content-type
、username
和password
)与您允许的标头(*
,字面意思)之间检测到不匹配,并且响应一个403
,甚至没有设置Access-Control-Allow-Origin
标头,从而导致访问控制检查失败。
英文:
TL;DR
gorilla/handlers doesn't (yet?) support the wildcard for Access-Control-Allow-Headers
. You must specify all the allowed headers explicitly. In your case, instead of
handlers.AllowedHeaders([]string{"*"})
you should have
handlers.AllowedHeaders([]string{"content-type", "username", "password"})
More details
The Fetch standard added support (in the case of non-credentialed requests) for the wildcard in the Access-Control-Allow-Headers
header back in 2016. Most modern browsers now support this feature.
However, gorilla/handlers doesn't seem to have caught up with the spec yet. If you inspect the source code for the handlers.AllowedHeaders
function and for the *cors.ServeHTTP
method, you'll see that there's no special handling of the "*"
value: it's treated literally. As a result, the CORS middleware detects a mismatch between the request headers supplied by your preflight request (content-type
, username
, and password
) and your allowed headers (*
, taken literally) and responds with a 403
without even setting the Access-Control-Allow-Origin
header, thereby causing the access-control check to fail.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论