CORS grpc Gateway GoLang 的中文翻译是:跨域资源共享(CORS)gRPC 网关 GoLang。

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

CORS grpc Gateway GoLang

问题

我有一个使用Vue.js 3的前端,并通过grpc-gateway调用Golang后端。我已经在这个问题上花了一段时间,但我看到了曙光。

我目前面临的问题是CORS(跨域资源共享)问题。然而,我在处理它的方法上看到了矛盾的信息。因此,我想发布一下,希望能帮助到其他人。

这是我初始化GRPC(gateway)的mux服务器的代码:

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()

}

这是我认为应该添加CORS配置的位置,但我不确定这是否是我在server.go文件中设置它的方式。

var httpServer http.Server

// Run Http Server with gRPC gateway
g.Go(func() error {
    fmt.Println("Starting Http sever (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",
    )
})

控制台报错信息:

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'.

我不确定在哪里添加类似于以下代码的内容:

func enableCors(w *http.ResponseWriter) {
    (*w).Header().Set("Access-Control-Allow-Origin", "*")
}

我觉得Golang的GRPC gateway应该内置了一些东西,但我找不到任何相关信息。

非常感谢任何建议。

----- 更新 1 -----

我尝试了以下代码:

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)
    })
}

以及

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)
    })
}

还有

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)
    })
}

结合以下代码:

func serveSwagger(mux *http.ServeMux, swaggerPath string) {
    fileServer := http.FileServer(http.Dir(swaggerPath))
    prefix := "/swagger-ui"
    mux.Handle(prefix, http.StripPrefix(prefix, fileServer))
}

但仍然遇到相同的问题...非常令人沮丧。

英文:

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

答案1

得分: 1

一种方法是通过为swMux添加额外的处理逻辑来实现。

func enableCors(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")

        h.ServeHTTP(w, r)
    })
}

然后,在你的RunHttpServer函数中调用它:

swMux := http.NewServeMux()
swMux.Handle("/", mux)
serveSwagger(swMux, swaggerPath)

server.Handler = enableCors(swMux)

这里是另一个启用了更多选项的示例。
这里grpc-gateway存储库中的官方示例,展示了他们如何处理CORS。

英文:

One way to do it is to enrich your swMux with additional handler logic.

func enableCors(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")

        h.ServeHTTP(w, r)
    })
}

Then, call this in your RunHttpServer function:

swMux := http.NewServeMux()
swMux.Handle("/", mux)
serveSwagger(swMux, swaggerPath)

server.Handler = enableCors(swMux)

Here is another example with more options enabled.
Here is an official example from grpc-gateway repo on how they handle CORS.

答案2

得分: 1

根据您在评论中提供的最新错误信息:

> 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.

您的浏览器正在发送一个预检请求OPTIONS HTTP 方法),以确定是否可以进行所需的跨域请求。

而服务器则返回了一个非 2xx 的响应。

我怀疑这是因为您的 enableCors 函数将请求传递给了 grpc-gateway 处理程序,而该处理程序对 OPTIONS HTTP 方法不满意,并返回了一个错误状态,可能是:

< 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":[]}

因此,为了避免这种情况,您希望在进行预检请求时 不要 进一步传递请求,例如:

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)
	})
}

然而,上述代码可能仍然不是一个合理的 CORS 处理实现。您应该考虑使用现有的包来处理这个问题,例如 github.com/rs/cors,它会以合理的方式处理这个问题,并处理任何潜在的陷阱等。

因此,导入 github.com/rs/cors,然后执行以下操作:

server.Handler = cors.AllowAll().Handler(swMux)

这样做将允许所有请求通过。该库还可以根据需要允许特定的来源、HTTP 方法等进行定制。

英文:

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.

答案3

得分: 0

如果您正在本地调试,您应该在前端添加一个代理来代理到后端服务。应该有一个vue.config.js文件。

英文:

If you are debugging locally, you should add a proxy to the front end to proxy to the back end service. There should be a vue.config.js file

huangapple
  • 本文由 发表于 2022年11月21日 02:31:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/74510810.html
匿名

发表评论

匿名网友

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

确定