为什么不做网站做公众号,公司装修效果图办公室,浙江政务服务网,什么是电子商务网站的建设http header 转发到 grpc上下文
grpc网关可以将请求体内容转发到grpc对应消息中。那如何获取http header头中的信息#xff0c;本文将介绍如何将http header转发到grpc上下文并采用拦截器#xff0c;获取http header中的内容。 有些http header中的内置字段是会转发的比如Au…http header 转发到 grpc上下文
grpc网关可以将请求体内容转发到grpc对应消息中。那如何获取http header头中的信息本文将介绍如何将http header转发到grpc上下文并采用拦截器获取http header中的内容。 有些http header中的内置字段是会转发的比如Authorization但是狠多自定义字段是转发不了的。
本文实现http header中自定义字段转发到grpc上下文并采用拦截器做个简单鉴权
代码可以参考前面几篇grpc-gateway博客
grpc-gateway入门,环境简单案例
grpc-gateway proto定义http路由
grpc-gateway定义http路由
网关代码修改
如果要转发http header中的自定义内容生成的网关代码需要进行修改增加一些网关服务器选项
runtime.WithIncomingHeaderMatcher 请求http header 设置转发哪些到grpc上下文runtime.WithOutgoingHeaderMatcher: 响应后,grpc上下文转发到http头部
gateway.go
package gatewayimport (contextflagfmtnet/httpgithub.com/grpc-ecosystem/grpc-gateway/v2/runtimegoogle.golang.org/grpcgoogle.golang.org/grpc/credentials/insecure_ google.golang.org/grpc/grpcloggw user/proto // Update
)var (// command-line options:// gRPC server endpointgrpcServerEndpoint flag.String(grpc-server-endpoint, localhost:50051, gRPC server endpoint)
)func Run() error {ctx : context.Background()ctx, cancel : context.WithCancel(ctx)defer cancel()// 请求时,将http header中某些字段转发到grpc上下文inComingOpt : runtime.WithIncomingHeaderMatcher(func(s string) (string, bool) {fmt.Println(header: s)switch s {case Service-Authorization:fmt.Println(Service-Authorization hit)return Service-Authorization, truedefault:return , false}})// 响应后,grpc上下文转发到http头部outGoingOpt : runtime.WithOutgoingHeaderMatcher(func(s string) (string, bool) {return , false})// Register gRPC server endpoint// Note: Make sure the gRPC server is running properly and accessiblemux : runtime.NewServeMux(inComingOpt, outGoingOpt)//添加文件上传处理函数mux.HandlePath(POST, /upload, uploadHandler)opts : []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}err : gw.RegisterUserHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)if err ! nil {return err}// Start HTTP server (and proxy calls to gRPC server endpoint)return http.ListenAndServe(:8081, mux)
}
文件上传接口修改因为这是自定义的网关路由接口需要自己将Header中的字段转发到grpc中
upload.go
package gatewayimport (contextfmtgithub.com/golang/protobuf/jsonpbgoogle.golang.org/grpcgoogle.golang.org/grpc/credentials/insecuregoogle.golang.org/grpc/metadataionet/httpuser/proto
)func uploadHandler(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {// 先从request解析文件err : r.ParseForm()if err ! nil {http.Error(w, fmt.Sprintf(上传失败:%s, err.Error()), http.StatusInternalServerError)}f, header, err :r.FormFile(attachment)if err ! nil {http.Error(w, fmt.Sprintf(上传失败:%s, err.Error()), http.StatusInternalServerError)}defer f.Close()// 访问grpc server端, 实际生产用连接池conn, err : grpc.Dial(*grpcServerEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))if err ! nil {http.Error(w, fmt.Sprintf(上传失败:%s, err.Error()), http.StatusInternalServerError)}defer conn.Close()c : proto.NewUserClient(conn)ctx : context.Background()ctx metadata.NewOutgoingContext(ctx, metadata.New(map[string]string{file_name:header.Filename,service-authorization:r.Header.Get(Service-Authorization),}))stream, err : c.Upload(ctx)if err ! nil {http.Error(w, fmt.Sprintf(上传失败:%s, err.Error()), http.StatusInternalServerError)}// 读文件流 转发给grpcbuf : make([]byte, 512)for {n, err : f.Read(buf)if err ! nil err ! io.EOF{http.Error(w, fmt.Sprintf(上传失败:%s, err.Error()), http.StatusInternalServerError)}if n 0 {break}stream.Send(proto.UploadRequest{Content: buf[:n],Size: int64(n),})}res, err : stream.CloseAndRecv()if err ! nil {http.Error(w, fmt.Sprintf(上传失败:%s, err.Error()), http.StatusInternalServerError)}m : jsonpb.Marshaler{}str, _ : m.MarshalToString(res)if err ! nil {http.Error(w, fmt.Sprintf(上传失败:%s, err.Error()), http.StatusInternalServerError)}w.Header().Add(Content-Type, application/json)fmt.Fprintf(w, str)}grpc服务代码修改
拦截器从上下文中获取元数据进行业务操作即可
interceptor.go
package serverimport (contexterrorsfmtgoogle.golang.org/grpcgoogle.golang.org/grpc/metadatastrings
)func UnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {err auth(ctx)if err ! nil {return nil, err}return handler(ctx, req)
}func StreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {err : auth(ss.Context())if err ! nil {return err}return handler(srv, ss)
}func auth(ctx context.Context) error {md, ok : metadata.FromIncomingContext(ctx)fmt.Println(meta:, md)// 实际应用中返回前端提示需模糊化,详细错误可以打印日志if !ok {return errors.New(获取元数据失败身份校验失败)}// 转发过来都是小写authorization : md[service-authorization]if len(authorization) 1 {return errors.New(获取身份令牌失败身份校验失败)}token : strings.TrimPrefix(authorization[0], Bearer )if token ! bearerToken {return errors.New(身份令牌对比失败身份校验失败)}return nil
}// 测试用
var bearerToken sdfdlsdhgeiasdxzasqqqy2ybfhhu2gyvb并将拦截器注册到grpc服务中
s : grpc.NewServer(grpc.UnaryInterceptor(server.UnaryInterceptor), grpc.StreamInterceptor(server.StreamInterceptor))重点还是网关代码修改增加转发header的逻辑。