mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 12:32:21 +00:00
ch4-06: 增加rest内容
This commit is contained in:
parent
00a6238fb1
commit
903e87b09b
@ -148,11 +148,122 @@ func (this *Message) Validate() error {
|
|||||||
|
|
||||||
通过生成的验证函数,并结合GRPC的截取器,我们可以很容易为每个方法的输入参数和返回值进行验证。
|
通过生成的验证函数,并结合GRPC的截取器,我们可以很容易为每个方法的输入参数和返回值进行验证。
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
## REST接口
|
## REST接口
|
||||||
|
|
||||||
TODO
|
GRPC服务一般用于集群内部通信,如果需要对外暴露服务一般会提供等价的REST接口。通过REST接口比较方便前端JavaScript和后端交互。开源社区中的grac-gateway项目就实现了将GRPC服务转为REST服务的能力。
|
||||||
|
|
||||||
|
grpc-gateway的工作原理如下图:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
通过在Protobuf文件中添加路由相关的元信息,通过自定义的代码插件生成路由相关的处理代码,最终将Rest请求转给更后端的Grpc服务处理。
|
||||||
|
|
||||||
|
路由扩展元信息也是通过Protobuf的元数据扩展用法提供:
|
||||||
|
|
||||||
|
```protobuf
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
|
||||||
|
message StringMessage {
|
||||||
|
string value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service RestService {
|
||||||
|
rpc Get(StringMessage) returns (StringMessage) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/get/{value}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
rpc Post(StringMessage) returns (StringMessage) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/post"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
我们首先为GRPC定义了Get和Post方法,然后通过元扩展语法在对应的方法后添加路由信息。其中“/get/{value}”路径对应的是Get方法,`{value}`部分对应参数中的value成员,结果通过json格式返回。Post方法对应“/post”路径,body中包含json格式的请求信息。
|
||||||
|
|
||||||
|
然后通过以下命令安装protoc-gen-grpc-gateway插件:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
再通过插件生成grpc-gateway必须的路由处理代码:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ protoc -I/usr/local/include -I. \
|
||||||
|
-I$GOPATH/src \
|
||||||
|
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||||
|
--grpc-gateway_out=. \
|
||||||
|
hello.proto
|
||||||
|
```
|
||||||
|
|
||||||
|
插件会为RestService服务生成对应的RegisterRestServiceHandlerFromEndpoint函数:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RegisterRestServiceHandlerFromEndpoint(
|
||||||
|
ctx context.Context, mux *runtime.ServeMux, endpoint string,
|
||||||
|
opts []grpc.DialOption,
|
||||||
|
) (err error) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
RegisterRestServiceHandlerFromEndpoint函数用于将定义了Rest接口的请求转发到真正的GRPC服务。注册路由处理函数之后就可以启动Web服务了:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mux := runtime.NewServeMux()
|
||||||
|
|
||||||
|
err := RegisterRestServiceHandlerFromEndpoint(
|
||||||
|
ctx, mux, "localhost:5000",
|
||||||
|
grpc.WithInsecure(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ListenAndServe(":8080", mux)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
首先通过runtime.NewServeMux()函数创建路由处理器,然后通过RegisterRestServiceHandlerFromEndpoint函数将RestService服务相关的REST接口导到后面的GRPC服务。grpc-gateway提供runtime.ServeMux类似同时也实现了http.Handler接口,因此可以标准库中的相关函数配置使用。
|
||||||
|
|
||||||
|
档GRPC和REST服务全部启动之后,就可以用curl请求REST服务了:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl localhost:8080/get/gopher
|
||||||
|
{"value":"Get: gopher"}
|
||||||
|
|
||||||
|
$ curl localhost:8080/post -X POST --data '{"value":"grpc"}'
|
||||||
|
{"value":"Post: grpc"}
|
||||||
|
```
|
||||||
|
|
||||||
|
在对外公布REST接口时,我们一般还会提供一个Swagger格式的文件用于描述这个接口规范。
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
|
||||||
|
|
||||||
|
$ protoc \
|
||||||
|
-I=. -I=../../../github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||||
|
--swagger_out=. \
|
||||||
|
hello.proto
|
||||||
|
```
|
||||||
|
|
||||||
|
然后会生成一个hello.swagger.json文件。这样的话就可以通过swagger-ui这个项目,在网页中提供REST接口的文档和测试等功能。
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
|
||||||
## Nginx代理
|
## Nginx代理
|
||||||
|
|
||||||
|
21
vendor/gobook.examples/ch4-06-grpc-ext/rest/Makefile
vendored
Normal file
21
vendor/gobook.examples/ch4-06-grpc-ext/rest/Makefile
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
run:
|
||||||
|
@go build -o a.out && ./a.out
|
||||||
|
-@rm ./a.out
|
||||||
|
|
||||||
|
gen:
|
||||||
|
protoc \
|
||||||
|
-I=. -I=../../../github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||||
|
--go_out=plugins=grpc:. \
|
||||||
|
helloworld.proto
|
||||||
|
protoc \
|
||||||
|
-I=. -I=../../../github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||||
|
--grpc-gateway_out=. \
|
||||||
|
helloworld.proto
|
||||||
|
protoc \
|
||||||
|
-I=. -I=../../../github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||||
|
--swagger_out=. \
|
||||||
|
helloworld.proto
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm *.pb.go
|
190
vendor/gobook.examples/ch4-06-grpc-ext/rest/helloworld.pb.go
vendored
Normal file
190
vendor/gobook.examples/ch4-06-grpc-ext/rest/helloworld.pb.go
vendored
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: helloworld.proto
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
import _ "google.golang.org/genproto/googleapis/api/annotations"
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "golang.org/x/net/context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type StringMessage struct {
|
||||||
|
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StringMessage) Reset() { *m = StringMessage{} }
|
||||||
|
func (m *StringMessage) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*StringMessage) ProtoMessage() {}
|
||||||
|
func (*StringMessage) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_helloworld_b65656966bdaa93a, []int{0}
|
||||||
|
}
|
||||||
|
func (m *StringMessage) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_StringMessage.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *StringMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_StringMessage.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *StringMessage) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_StringMessage.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *StringMessage) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_StringMessage.Size(m)
|
||||||
|
}
|
||||||
|
func (m *StringMessage) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_StringMessage.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_StringMessage proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *StringMessage) GetValue() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*StringMessage)(nil), "main.StringMessage")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ grpc.ClientConn
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
|
// RestServiceClient is the client API for RestService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||||
|
type RestServiceClient interface {
|
||||||
|
Get(ctx context.Context, in *StringMessage, opts ...grpc.CallOption) (*StringMessage, error)
|
||||||
|
Post(ctx context.Context, in *StringMessage, opts ...grpc.CallOption) (*StringMessage, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type restServiceClient struct {
|
||||||
|
cc *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRestServiceClient(cc *grpc.ClientConn) RestServiceClient {
|
||||||
|
return &restServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *restServiceClient) Get(ctx context.Context, in *StringMessage, opts ...grpc.CallOption) (*StringMessage, error) {
|
||||||
|
out := new(StringMessage)
|
||||||
|
err := c.cc.Invoke(ctx, "/main.RestService/Get", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *restServiceClient) Post(ctx context.Context, in *StringMessage, opts ...grpc.CallOption) (*StringMessage, error) {
|
||||||
|
out := new(StringMessage)
|
||||||
|
err := c.cc.Invoke(ctx, "/main.RestService/Post", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestServiceServer is the server API for RestService service.
|
||||||
|
type RestServiceServer interface {
|
||||||
|
Get(context.Context, *StringMessage) (*StringMessage, error)
|
||||||
|
Post(context.Context, *StringMessage) (*StringMessage, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterRestServiceServer(s *grpc.Server, srv RestServiceServer) {
|
||||||
|
s.RegisterService(&_RestService_serviceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _RestService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(StringMessage)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RestServiceServer).Get(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/main.RestService/Get",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RestServiceServer).Get(ctx, req.(*StringMessage))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _RestService_Post_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(StringMessage)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RestServiceServer).Post(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/main.RestService/Post",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RestServiceServer).Post(ctx, req.(*StringMessage))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _RestService_serviceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "main.RestService",
|
||||||
|
HandlerType: (*RestServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "Get",
|
||||||
|
Handler: _RestService_Get_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "Post",
|
||||||
|
Handler: _RestService_Post_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "helloworld.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_helloworld_b65656966bdaa93a) }
|
||||||
|
|
||||||
|
var fileDescriptor_helloworld_b65656966bdaa93a = []byte{
|
||||||
|
// 192 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
|
||||||
|
0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d,
|
||||||
|
0xcc, 0xcc, 0x93, 0x92, 0x49, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f,
|
||||||
|
0xcc, 0xcb, 0xcb, 0x2f, 0x49, 0x2c, 0xc9, 0xcc, 0xcf, 0x2b, 0x86, 0xa8, 0x51, 0x52, 0xe5, 0xe2,
|
||||||
|
0x0d, 0x2e, 0x29, 0xca, 0xcc, 0x4b, 0xf7, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f, 0x15, 0x12, 0xe1,
|
||||||
|
0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x70, 0x8c,
|
||||||
|
0x66, 0x30, 0x72, 0x71, 0x07, 0xa5, 0x16, 0x97, 0x04, 0xa7, 0x16, 0x95, 0x65, 0x26, 0xa7, 0x0a,
|
||||||
|
0xb9, 0x72, 0x31, 0xbb, 0xa7, 0x96, 0x08, 0x09, 0xeb, 0x81, 0xac, 0xd0, 0x43, 0x31, 0x41, 0x0a,
|
||||||
|
0x9b, 0xa0, 0x92, 0x48, 0xd3, 0xe5, 0x27, 0x93, 0x99, 0xf8, 0x84, 0x78, 0xf4, 0xd3, 0x53, 0x4b,
|
||||||
|
0xf4, 0xab, 0xc1, 0xa6, 0xd6, 0x0a, 0x39, 0x71, 0xb1, 0x04, 0xe4, 0x17, 0x93, 0x62, 0x8e, 0x00,
|
||||||
|
0xd8, 0x1c, 0x2e, 0x25, 0x56, 0xfd, 0x82, 0xfc, 0xe2, 0x12, 0x2b, 0x46, 0xad, 0x24, 0x36, 0xb0,
|
||||||
|
0x47, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x75, 0x88, 0x64, 0x8b, 0x00, 0x01, 0x00, 0x00,
|
||||||
|
}
|
180
vendor/gobook.examples/ch4-06-grpc-ext/rest/helloworld.pb.gw.go
vendored
Normal file
180
vendor/gobook.examples/ch4-06-grpc-ext/rest/helloworld.pb.gw.go
vendored
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
||||||
|
// source: helloworld.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package main is a reverse proxy.
|
||||||
|
|
||||||
|
It translates gRPC into RESTful JSON APIs.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ codes.Code
|
||||||
|
var _ io.Reader
|
||||||
|
var _ status.Status
|
||||||
|
var _ = runtime.String
|
||||||
|
var _ = utilities.NewDoubleArray
|
||||||
|
|
||||||
|
func request_RestService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client RestServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq StringMessage
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
var (
|
||||||
|
val string
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
_ = err
|
||||||
|
)
|
||||||
|
|
||||||
|
val, ok = pathParams["value"]
|
||||||
|
if !ok {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "value")
|
||||||
|
}
|
||||||
|
|
||||||
|
protoReq.Value, err = runtime.String(val)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "value", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func request_RestService_Post_0(ctx context.Context, marshaler runtime.Marshaler, client RestServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq StringMessage
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := client.Post(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterRestServiceHandlerFromEndpoint is same as RegisterRestServiceHandler but
|
||||||
|
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||||
|
func RegisterRestServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||||
|
conn, err := grpc.Dial(endpoint, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if cerr := conn.Close(); cerr != nil {
|
||||||
|
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
if cerr := conn.Close(); cerr != nil {
|
||||||
|
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return RegisterRestServiceHandler(ctx, mux, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterRestServiceHandler registers the http handlers for service RestService to "mux".
|
||||||
|
// The handlers forward requests to the grpc endpoint over "conn".
|
||||||
|
func RegisterRestServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
||||||
|
return RegisterRestServiceHandlerClient(ctx, mux, NewRestServiceClient(conn))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterRestServiceHandler registers the http handlers for service RestService to "mux".
|
||||||
|
// The handlers forward requests to the grpc endpoint over the given implementation of "RestServiceClient".
|
||||||
|
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "RestServiceClient"
|
||||||
|
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
||||||
|
// "RestServiceClient" to call the correct interceptors.
|
||||||
|
func RegisterRestServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client RestServiceClient) error {
|
||||||
|
|
||||||
|
mux.Handle("GET", pattern_RestService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
if cn, ok := w.(http.CloseNotifier); ok {
|
||||||
|
go func(done <-chan struct{}, closed <-chan bool) {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-closed:
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}(ctx.Done(), cn.CloseNotify())
|
||||||
|
}
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_RestService_Get_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_RestService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_RestService_Post_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
if cn, ok := w.(http.CloseNotifier); ok {
|
||||||
|
go func(done <-chan struct{}, closed <-chan bool) {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-closed:
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}(ctx.Done(), cn.CloseNotify())
|
||||||
|
}
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_RestService_Post_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_RestService_Post_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
pattern_RestService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"get", "value"}, ""))
|
||||||
|
|
||||||
|
pattern_RestService_Post_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"post"}, ""))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
forward_RestService_Get_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
|
forward_RestService_Post_0 = runtime.ForwardResponseMessage
|
||||||
|
)
|
23
vendor/gobook.examples/ch4-06-grpc-ext/rest/helloworld.proto
vendored
Normal file
23
vendor/gobook.examples/ch4-06-grpc-ext/rest/helloworld.proto
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
|
||||||
|
message StringMessage {
|
||||||
|
string value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service RestService {
|
||||||
|
rpc Get(StringMessage) returns (StringMessage) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/get/{value}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
rpc Post(StringMessage) returns (StringMessage) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/post"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
79
vendor/gobook.examples/ch4-06-grpc-ext/rest/helloworld.swagger.json
vendored
Normal file
79
vendor/gobook.examples/ch4-06-grpc-ext/rest/helloworld.swagger.json
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"swagger": "2.0",
|
||||||
|
"info": {
|
||||||
|
"title": "helloworld.proto",
|
||||||
|
"version": "version not set"
|
||||||
|
},
|
||||||
|
"schemes": [
|
||||||
|
"http",
|
||||||
|
"https"
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/get/{value}": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "Get",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/mainStringMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"RestService"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/post": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "Post",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/mainStringMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/mainStringMessage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"RestService"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"mainStringMessage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
vendor/gobook.examples/ch4-06-grpc-ext/rest/main.go
vendored
Normal file
73
vendor/gobook.examples/ch4-06-grpc-ext/rest/main.go
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
port = ":5000"
|
||||||
|
echoEndpoint = flag.String("echo_endpoint", "localhost"+port, "endpoint of YourService")
|
||||||
|
)
|
||||||
|
|
||||||
|
type myGrpcServer struct{}
|
||||||
|
|
||||||
|
func (s *myGrpcServer) Get(ctx context.Context, in *StringMessage) (*StringMessage, error) {
|
||||||
|
return &StringMessage{Value: "Get: " + in.Value}, nil
|
||||||
|
}
|
||||||
|
func (s *myGrpcServer) Post(ctx context.Context, in *StringMessage) (*StringMessage, error) {
|
||||||
|
return &StringMessage{Value: "Post: " + in.Value}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mux := runtime.NewServeMux()
|
||||||
|
opts := []grpc.DialOption{grpc.WithInsecure()}
|
||||||
|
err := RegisterRestServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.ListenAndServe(":8080", mux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// $ curl localhost:8080/get/gopher
|
||||||
|
// {"value":"Get: gopher"}
|
||||||
|
|
||||||
|
// $ curl localhost:8080/post -X POST --data '{"value":"grpc"}'
|
||||||
|
// {"value":"Post: grpc"}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
defer glog.Flush()
|
||||||
|
|
||||||
|
go startGrpcServer()
|
||||||
|
|
||||||
|
if err := run(); err != nil {
|
||||||
|
glog.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startGrpcServer() {
|
||||||
|
server := grpc.NewServer()
|
||||||
|
RegisterRestServiceServer(server, new(myGrpcServer))
|
||||||
|
|
||||||
|
lis, err := net.Listen("tcp", port)
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("could not list on %s: %s", port, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := server.Serve(lis); err != nil {
|
||||||
|
log.Panicf("grpc serve error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user