I have a vue.js 3 frontend, and I am calling a Golang backend via grpc-gateway
. I have been at this for a while but I see light at the end of the tunnel.
I am currently facing a CORS issue. However, I am reading conflicting information on how to handle it. Therefore, I want to post and hopefully it helps someone.
Here is the code on how I init my mux server for GRPC (gateway)
func RunHttpServer(server *http.Server, httpEndpoint, grpcEndpoint, swaggerPath string) (err error) {
server.Addr = httpEndpoint
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Register gROC server endpoint
mux := runtime.NewServeMux(
runtime.WithErrorHandler(func(ctx context.Context,
mux *runtime.ServeMux,
marshaler runtime.Marshaler,
w http.ResponseWriter, r *http.Request,
err error,
) {
s, ok := status.FromError(err)
if ok {
if s.Code() == codes.Unavailable {
err = status.Error(codes.Unavailable, ErrUnavailable)
}
}
runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, err)
}),
)
opts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithChainUnaryInterceptor(),
}
if err = api.RegisterApiServiceHandlerFromEndpoint(ctx, mux, grpcEndpoint, opts); err != nil {
return
}
swMux := http.NewServeMux()
swMux.Handle("/", mux)
serveSwagger(swMux, swaggerPath)
server.Handler = swMux
return server.ListenAndServe()
}
Here is where I believe I should add the cors config, but I am not sure this is how I set it up in the server.go file..
var httpServer http.Server
// Run Http Server with gRPC gateway
g.Go(func() error {
fmt.Println("Starting Http sever (port {}) and gRPC gateway (port {})",
strconv.Itoa(cfg.Server.HTTPPort),
strconv.Itoa(cfg.Server.GRPCPort),
)
return rest.RunHttpServer(
&httpServer,
":"+strconv.Itoa(cfg.Server.HTTPPort),
":"+strconv.Itoa(cfg.Server.GRPCPort),
"/webapi",
)
})
error in console:
Access to XMLHttpRequest at 'http://localhost:8080/v1/test' from origin 'http://localhost:9000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin'
I am not sure where to add something like
func enableCors(w *http.ResponseWriter) {
(*w).Header().Set("Access-Control-Allow-Origin", "*")
}
and I feel the golang GRPC gateway should have something built in but I cannot find anything?
Any advice would be greatly appreciated.
----- update 1 -----
I have tried
func enableCors(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:9000")
w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, HEAD, OPTIONS")
h.ServeHTTP(w, r)
})
}
and
func enableCors(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, HEAD, OPTIONS")
h.ServeHTTP(w, r)
})
}
and
func enableCors(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "http://localhost")
w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, HEAD, OPTIONS")
h.ServeHTTP(w, r)
})
}
in conjuction with
func serveSwagger(mux *http.ServeMux, swaggerPath string) {
fileServer := http.FileServer(http.Dir(swaggerPath))
prefix := "/swagger-ui"
mux.Handle(prefix, http.StripPrefix(prefix, fileServer))
}
and still have the same issue.. Very frustrating
Based on the latest error you've provided in a comment:
Access to XMLHttpRequest at 'localhost:8080/v1/test' from origin 'localhost:9000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Your browser is sending a preflight request (OPTIONS
HTTP method) to ascertain whether the desired cross origin request can be made.
And the server is responding with a non-2xx response.
I suspect this is because your enableCors
functions are propagating the request to the grpc-gateway handler, which is unhappy with the OPTIONS
HTTP method and returning an error status, probably:
< HTTP/1.1 501 Not Implemented
< Content-Type: application/json
< Vary: Origin
< Date: Fri, 25 Nov 2022 11:17:52 GMT
< Content-Length: 55
<
{"code":12,"message":"Method Not Allowed","details":[]}
So to avoid that, you'd want to not propagate the request further in the case of a preflight request being made, e.g.
func enableCors(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:9000")
w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, HEAD, OPTIONS")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
h.ServeHTTP(w, r)
})
}
However, the above is likely still not a reasonable implementation of CORS handling. You should look to use an existing package for this, e.g. github.com/rs/cors, which will handle this in a reasonable fashion, and also deal with any potential gotchas, etc.
So importing github.com/rs/cors
and then doing something like:
server.Handler = cors.AllowAll().Handler(swMux)
should get you started by allowing everything through. The library will allow you to tailor to specific origins, HTTP methods, etc, as required.