fix http decode (#742)

* fix http decoder
pull/746/head
Tony Chen 4 years ago committed by GitHub
parent f8eeb9f388
commit 946e9ca814
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      .github/workflows/go.yml
  2. 2
      cmd/protoc-gen-go-http/go.mod
  3. 43
      cmd/protoc-gen-go-http/internal/testproto/echo_service.pb.go
  4. 9
      cmd/protoc-gen-go-http/internal/testproto/echo_service.proto
  5. 38
      cmd/protoc-gen-go-http/internal/testproto/echo_service_grpc.pb.go
  6. 93
      cmd/protoc-gen-go-http/internal/testproto/echo_service_http.pb.go
  7. 212
      cmd/protoc-gen-go-http/internal/testproto/echo_service_test.go
  8. 11
      cmd/protoc-gen-go-http/template.go
  9. 12
      encoding/json/json.go
  10. 22
      transport/http/client.go
  11. 24
      transport/http/handle.go

@ -23,3 +23,21 @@ jobs:
- name: Test
run: go test -v ./...
- name: Kratos
run: |
cd cmd/kratos
go build ./...
go test ./...
- name: HTTP
run: |
cd cmd/protoc-gen-go-http
go build ./...
go test ./...
- name: Errors
run: |
cd cmd/protoc-gen-go-errors
go build ./...
go test ./...

@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2
go 1.15
require (
github.com/go-kratos/kratos/v2 v2.0.0-alpha5
github.com/go-kratos/kratos/v2 v2.0.0-20210305104106-278210c4755a
github.com/golang/protobuf v1.4.3
github.com/gorilla/mux v1.8.0
google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119

@ -491,7 +491,7 @@ var file_echo_service_proto_rawDesc = []byte{
0x4d, 0x41, 0x47, 0x45, 0x53, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f, 0x43, 0x41, 0x4c,
0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x45, 0x57, 0x53, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08,
0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x53, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x56, 0x49,
0x44, 0x45, 0x4f, 0x10, 0x06, 0x32, 0xbc, 0x04, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x53, 0x65,
0x44, 0x45, 0x4f, 0x10, 0x06, 0x32, 0xcc, 0x05, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xf2, 0x01, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x18,
0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c,
0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70,
@ -513,13 +513,22 @@ var file_echo_service_proto_rawDesc = []byte{
0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d,
0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f,
0x65, 0x63, 0x68, 0x6f, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x61, 0x0a, 0x0a,
0x45, 0x63, 0x68, 0x6f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x74, 0x65, 0x73,
0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x1f,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d,
0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12,
0x65, 0x63, 0x68, 0x6f, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a,
0x10, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x6f, 0x64,
0x79, 0x12, 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79,
0x6e, 0x61, 0x6d, 0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44,
0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x22, 0x1e, 0x2f, 0x76, 0x31,
0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x72, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x62, 0x04, 0x62, 0x6f, 0x64,
0x79, 0x12, 0x6c, 0x0a, 0x0a, 0x45, 0x63, 0x68, 0x6f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12,
0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70,
0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x2a, 0x22, 0x2f, 0x76, 0x31,
0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x64, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x6e, 0x75, 0x6d, 0x7d, 0x12,
0x73, 0x0a, 0x09, 0x45, 0x63, 0x68, 0x6f, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x74,
0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x1f, 0x2e,
@ -569,14 +578,16 @@ var file_echo_service_proto_depIdxs = []int32{
7, // 6: testproto.DynamicMessageUpdate.update_mask:type_name -> google.protobuf.FieldMask
2, // 7: testproto.EchoService.Echo:input_type -> testproto.SimpleMessage
2, // 8: testproto.EchoService.EchoBody:input_type -> testproto.SimpleMessage
2, // 9: testproto.EchoService.EchoDelete:input_type -> testproto.SimpleMessage
4, // 10: testproto.EchoService.EchoPatch:input_type -> testproto.DynamicMessageUpdate
2, // 11: testproto.EchoService.Echo:output_type -> testproto.SimpleMessage
2, // 12: testproto.EchoService.EchoBody:output_type -> testproto.SimpleMessage
2, // 13: testproto.EchoService.EchoDelete:output_type -> testproto.SimpleMessage
4, // 14: testproto.EchoService.EchoPatch:output_type -> testproto.DynamicMessageUpdate
11, // [11:15] is the sub-list for method output_type
7, // [7:11] is the sub-list for method input_type
4, // 9: testproto.EchoService.EchoResponseBody:input_type -> testproto.DynamicMessageUpdate
2, // 10: testproto.EchoService.EchoDelete:input_type -> testproto.SimpleMessage
4, // 11: testproto.EchoService.EchoPatch:input_type -> testproto.DynamicMessageUpdate
2, // 12: testproto.EchoService.Echo:output_type -> testproto.SimpleMessage
2, // 13: testproto.EchoService.EchoBody:output_type -> testproto.SimpleMessage
4, // 14: testproto.EchoService.EchoResponseBody:output_type -> testproto.DynamicMessageUpdate
2, // 15: testproto.EchoService.EchoDelete:output_type -> testproto.SimpleMessage
4, // 16: testproto.EchoService.EchoPatch:output_type -> testproto.DynamicMessageUpdate
12, // [12:17] is the sub-list for method output_type
7, // [7:12] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name

@ -85,10 +85,17 @@ service EchoService {
body: "*"
};
}
// EchoResponseBody method receives a simple message and returns it.
rpc EchoResponseBody(DynamicMessageUpdate) returns (DynamicMessageUpdate) {
option (google.api.http) = {
post: "/v1/example/echo_response_body"
response_body: "body"
};
}
// EchoDelete method receives a simple message and returns it.
rpc EchoDelete(SimpleMessage) returns (SimpleMessage) {
option (google.api.http) = {
delete: "/v1/example/echo_delete"
delete: "/v1/example/echo_delete/{id}/{num}"
};
}
// EchoPatch method receives a NonStandardUpdateRequest and returns it.

@ -25,6 +25,8 @@ type EchoServiceClient interface {
Echo(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error)
// EchoBody method receives a simple message and returns it.
EchoBody(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error)
// EchoResponseBody method receives a simple message and returns it.
EchoResponseBody(ctx context.Context, in *DynamicMessageUpdate, opts ...grpc.CallOption) (*DynamicMessageUpdate, error)
// EchoDelete method receives a simple message and returns it.
EchoDelete(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error)
// EchoPatch method receives a NonStandardUpdateRequest and returns it.
@ -57,6 +59,15 @@ func (c *echoServiceClient) EchoBody(ctx context.Context, in *SimpleMessage, opt
return out, nil
}
func (c *echoServiceClient) EchoResponseBody(ctx context.Context, in *DynamicMessageUpdate, opts ...grpc.CallOption) (*DynamicMessageUpdate, error) {
out := new(DynamicMessageUpdate)
err := c.cc.Invoke(ctx, "/testproto.EchoService/EchoResponseBody", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *echoServiceClient) EchoDelete(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error) {
out := new(SimpleMessage)
err := c.cc.Invoke(ctx, "/testproto.EchoService/EchoDelete", in, out, opts...)
@ -86,6 +97,8 @@ type EchoServiceServer interface {
Echo(context.Context, *SimpleMessage) (*SimpleMessage, error)
// EchoBody method receives a simple message and returns it.
EchoBody(context.Context, *SimpleMessage) (*SimpleMessage, error)
// EchoResponseBody method receives a simple message and returns it.
EchoResponseBody(context.Context, *DynamicMessageUpdate) (*DynamicMessageUpdate, error)
// EchoDelete method receives a simple message and returns it.
EchoDelete(context.Context, *SimpleMessage) (*SimpleMessage, error)
// EchoPatch method receives a NonStandardUpdateRequest and returns it.
@ -103,6 +116,9 @@ func (UnimplementedEchoServiceServer) Echo(context.Context, *SimpleMessage) (*Si
func (UnimplementedEchoServiceServer) EchoBody(context.Context, *SimpleMessage) (*SimpleMessage, error) {
return nil, status.Errorf(codes.Unimplemented, "method EchoBody not implemented")
}
func (UnimplementedEchoServiceServer) EchoResponseBody(context.Context, *DynamicMessageUpdate) (*DynamicMessageUpdate, error) {
return nil, status.Errorf(codes.Unimplemented, "method EchoResponseBody not implemented")
}
func (UnimplementedEchoServiceServer) EchoDelete(context.Context, *SimpleMessage) (*SimpleMessage, error) {
return nil, status.Errorf(codes.Unimplemented, "method EchoDelete not implemented")
}
@ -158,6 +174,24 @@ func _EchoService_EchoBody_Handler(srv interface{}, ctx context.Context, dec fun
return interceptor(ctx, in, info, handler)
}
func _EchoService_EchoResponseBody_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DynamicMessageUpdate)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).EchoResponseBody(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/testproto.EchoService/EchoResponseBody",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).EchoResponseBody(ctx, req.(*DynamicMessageUpdate))
}
return interceptor(ctx, in, info, handler)
}
func _EchoService_EchoDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SimpleMessage)
if err := dec(in); err != nil {
@ -209,6 +243,10 @@ var EchoService_ServiceDesc = grpc.ServiceDesc{
MethodName: "EchoBody",
Handler: _EchoService_EchoBody_Handler,
},
{
MethodName: "EchoResponseBody",
Handler: _EchoService_EchoResponseBody_Handler,
},
{
MethodName: "EchoDelete",
Handler: _EchoService_EchoDelete_Handler,

@ -27,6 +27,8 @@ type EchoServiceHandler interface {
EchoDelete(context.Context, *SimpleMessage) (*SimpleMessage, error)
EchoPatch(context.Context, *DynamicMessageUpdate) (*DynamicMessageUpdate, error)
EchoResponseBody(context.Context, *DynamicMessageUpdate) (*DynamicMessageUpdate, error)
}
func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) http.Handler {
@ -38,16 +40,16 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
r.HandleFunc("/v1/example/echo/{id}/{num}", func(w http.ResponseWriter, r *http.Request) {
var in SimpleMessage
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
if err := h.Decode(r, &in); err != nil {
h.Error(w, r, err)
return
}
if err := h.Decode(r, &in); err != nil {
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.Echo(ctx, req.(*SimpleMessage))
}
@ -59,23 +61,24 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out); err != nil {
reply := out.(*SimpleMessage)
if err := h.Encode(w, r, reply); err != nil {
h.Error(w, r, err)
}
}).Methods("GET")
r.HandleFunc("/v1/example/echo/{id}/{num}/{lang}", func(w http.ResponseWriter, r *http.Request) {
var in SimpleMessage
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
if err := h.Decode(r, &in); err != nil {
h.Error(w, r, err)
return
}
if err := h.Decode(r, &in); err != nil {
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.Echo(ctx, req.(*SimpleMessage))
}
@ -87,23 +90,24 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out); err != nil {
reply := out.(*SimpleMessage)
if err := h.Encode(w, r, reply); err != nil {
h.Error(w, r, err)
}
}).Methods("GET")
r.HandleFunc("/v1/example/echo1/{id}/{line_num}/{status.note}", func(w http.ResponseWriter, r *http.Request) {
var in SimpleMessage
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
if err := h.Decode(r, &in); err != nil {
h.Error(w, r, err)
return
}
if err := h.Decode(r, &in); err != nil {
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.Echo(ctx, req.(*SimpleMessage))
}
@ -115,23 +119,24 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out); err != nil {
reply := out.(*SimpleMessage)
if err := h.Encode(w, r, reply); err != nil {
h.Error(w, r, err)
}
}).Methods("GET")
r.HandleFunc("/v1/example/echo2/{no.note}", func(w http.ResponseWriter, r *http.Request) {
var in SimpleMessage
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
if err := h.Decode(r, &in); err != nil {
h.Error(w, r, err)
return
}
if err := h.Decode(r, &in); err != nil {
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.Echo(ctx, req.(*SimpleMessage))
}
@ -143,23 +148,24 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out); err != nil {
reply := out.(*SimpleMessage)
if err := h.Encode(w, r, reply); err != nil {
h.Error(w, r, err)
}
}).Methods("GET")
r.HandleFunc("/v1/example/echo/{id}", func(w http.ResponseWriter, r *http.Request) {
var in SimpleMessage
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
if err := h.Decode(r, &in); err != nil {
h.Error(w, r, err)
return
}
if err := h.Decode(r, &in); err != nil {
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.Echo(ctx, req.(*SimpleMessage))
}
@ -171,18 +177,19 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out); err != nil {
reply := out.(*SimpleMessage)
if err := h.Encode(w, r, reply); err != nil {
h.Error(w, r, err)
}
}).Methods("POST")
r.HandleFunc("/v1/example/echo_body", func(w http.ResponseWriter, r *http.Request) {
var in SimpleMessage
if err := h.Decode(r, &in); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.EchoBody(ctx, req.(*SimpleMessage))
}
@ -194,18 +201,48 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out); err != nil {
reply := out.(*SimpleMessage)
if err := h.Encode(w, r, reply); err != nil {
h.Error(w, r, err)
}
}).Methods("POST")
r.HandleFunc("/v1/example/echo_delete", func(w http.ResponseWriter, r *http.Request) {
var in SimpleMessage
r.HandleFunc("/v1/example/echo_response_body", func(w http.ResponseWriter, r *http.Request) {
var in DynamicMessageUpdate
if err := h.Decode(r, &in); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.EchoResponseBody(ctx, req.(*DynamicMessageUpdate))
}
if h.Middleware != nil {
next = h.Middleware(next)
}
out, err := next(r.Context(), &in)
if err != nil {
h.Error(w, r, err)
return
}
reply := out.(*DynamicMessageUpdate)
if err := h.Encode(w, r, reply.Body); err != nil {
h.Error(w, r, err)
}
}).Methods("POST")
r.HandleFunc("/v1/example/echo_delete/{id}/{num}", func(w http.ResponseWriter, r *http.Request) {
var in SimpleMessage
if err := h.Decode(r, &in); err != nil {
h.Error(w, r, err)
return
}
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.EchoDelete(ctx, req.(*SimpleMessage))
}
@ -217,18 +254,19 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out); err != nil {
reply := out.(*SimpleMessage)
if err := h.Encode(w, r, reply); err != nil {
h.Error(w, r, err)
}
}).Methods("DELETE")
r.HandleFunc("/v1/example/echo_patch", func(w http.ResponseWriter, r *http.Request) {
var in DynamicMessageUpdate
if err := h.Decode(r, &in.Body); err != nil {
h.Error(w, r, err)
return
}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.EchoPatch(ctx, req.(*DynamicMessageUpdate))
}
@ -240,7 +278,8 @@ func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) h
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out); err != nil {
reply := out.(*DynamicMessageUpdate)
if err := h.Encode(w, r, reply); err != nil {
h.Error(w, r, err)
}
}).Methods("PATCH")

@ -0,0 +1,212 @@
package testproto
import (
"bytes"
context "context"
"fmt"
"net"
http "net/http"
"testing"
"time"
"github.com/go-kratos/kratos/v2/encoding"
tr "github.com/go-kratos/kratos/v2/transport/http"
_struct "github.com/golang/protobuf/ptypes/struct"
)
type echoService struct {
}
func (s *echoService) Echo(ctx context.Context, m *SimpleMessage) (*SimpleMessage, error) {
return m, nil
}
func (s *echoService) EchoBody(ctx context.Context, m *SimpleMessage) (*SimpleMessage, error) {
return m, nil
}
func (s *echoService) EchoDelete(ctx context.Context, m *SimpleMessage) (*SimpleMessage, error) {
return m, nil
}
func (s *echoService) EchoPatch(ctx context.Context, m *DynamicMessageUpdate) (*DynamicMessageUpdate, error) {
return m, nil
}
func (s *echoService) EchoResponseBody(ctx context.Context, m *DynamicMessageUpdate) (*DynamicMessageUpdate, error) {
return m, nil
}
type echoClient struct {
baseURL string
client *http.Client
}
// post: /v1/example/echo/{id}
func (c *echoClient) Echo(ctx context.Context, in *SimpleMessage) (out *SimpleMessage, err error) {
codec := encoding.GetCodec("json")
data, err := codec.Marshal(in)
if err != nil {
return
}
req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/example/echo/%s", c.baseURL, in.Id), bytes.NewReader(data))
if err != nil {
return
}
req.Header.Set("content-type", "application/json")
out = new(SimpleMessage)
if err = tr.Do(c.client, req, out); err != nil {
return
}
return
}
// post: /v1/example/echo_body
func (c *echoClient) EchoBody(ctx context.Context, in *SimpleMessage) (out *SimpleMessage, err error) {
codec := encoding.GetCodec("json")
data, err := codec.Marshal(in)
if err != nil {
return
}
req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/example/echo_body", c.baseURL), bytes.NewReader(data))
if err != nil {
return
}
req.Header.Set("content-type", "application/json")
out = new(SimpleMessage)
if err = tr.Do(c.client, req, out); err != nil {
return
}
return
}
// delete: /v1/example/echo_delete/{id}/{num}
func (c *echoClient) EchoDelete(ctx context.Context, in *SimpleMessage) (out *SimpleMessage, err error) {
req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/example/echo_delete/%s/%d", c.baseURL, in.Id, in.Num), nil)
if err != nil {
return
}
out = new(SimpleMessage)
if err = tr.Do(c.client, req, out); err != nil {
return
}
return
}
// patch: /v1/example/echo_patch
func (c *echoClient) EchoPatch(ctx context.Context, in *DynamicMessageUpdate) (out *DynamicMessageUpdate, err error) {
codec := encoding.GetCodec("json")
data, err := codec.Marshal(in.Body)
if err != nil {
return
}
req, err := http.NewRequest("PATCH", fmt.Sprintf("%s/v1/example/echo_patch", c.baseURL), bytes.NewReader(data))
if err != nil {
return
}
req.Header.Set("content-type", "application/json")
out = new(DynamicMessageUpdate)
if err = tr.Do(c.client, req, out); err != nil {
return
}
return
}
// post: /v1/example/echo_response_body
func (c *echoClient) EchoResponseBody(ctx context.Context, in *DynamicMessageUpdate) (out *DynamicMessageUpdate, err error) {
codec := encoding.GetCodec("json")
data, err := codec.Marshal(in)
if err != nil {
return
}
req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/example/echo_response_body", c.baseURL), bytes.NewReader(data))
if err != nil {
return
}
req.Header.Set("content-type", "application/json")
out = new(DynamicMessageUpdate)
if err = tr.Do(c.client, req, out.Body); err != nil {
return
}
return
}
func TestEchoService(t *testing.T) {
s := &echoService{}
h := NewEchoServiceHandler(s)
srv := &http.Server{Addr: ":0", Handler: h}
lis, err := net.Listen("tcp", srv.Addr)
if err != nil {
t.Fatal(err)
}
addr := lis.Addr().(*net.TCPAddr)
time.AfterFunc(time.Second, func() {
defer srv.Shutdown(context.Background())
testEchoClient(t, fmt.Sprintf("http://127.0.0.1:%d", addr.Port))
})
if err := srv.Serve(lis); err != nil && err != http.ErrServerClosed {
t.Fatal(err)
}
}
func testEchoClient(t *testing.T, baseURL string) {
var (
err error
in = &SimpleMessage{Id: "test_id", Num: 100}
out = &SimpleMessage{}
)
check := func(in, out *SimpleMessage) {
if in.Id != out.Id || in.Num != out.Num {
t.Errorf("expected %s got %s", in, out)
}
}
cli := &echoClient{baseURL: baseURL, client: http.DefaultClient}
if out, err = cli.Echo(context.Background(), in); err != nil {
t.Fatal(err)
}
check(in, out)
if out, err = cli.EchoBody(context.Background(), in); err != nil {
t.Fatal(err)
}
check(in, out)
if out, err = cli.EchoDelete(context.Background(), in); err != nil {
t.Fatal(err)
}
check(in, out)
var (
din = &DynamicMessageUpdate{Body: &DynamicMessage{
ValueField: &_struct.Value{Kind: &_struct.Value_StringValue{StringValue: "test"}},
}}
dout *DynamicMessageUpdate
)
if dout, err = cli.EchoPatch(context.Background(), din); err != nil {
t.Fatal(err)
}
if din.Body.ValueField.GetStringValue() != dout.Body.ValueField.GetStringValue() {
t.Errorf("EchoPatch expected %s got %s", din, dout)
}
if dout, err = cli.EchoResponseBody(context.Background(), din); err != nil {
t.Fatal(err)
}
if din.Body.ValueField.GetStringValue() != dout.Body.ValueField.GetStringValue() {
t.Errorf("EchoResponseBody expected %s got %s", din, dout)
}
}
func TestJSON(t *testing.T) {
in := &SimpleMessage{Id: "test_id", Num: 100}
out := &SimpleMessage{}
codec := encoding.GetCodec("json")
data, err := codec.Marshal(in)
if err != nil {
t.Fatal(err)
}
if err := codec.Unmarshal(data, out); err != nil {
t.Fatal(err)
}
}

@ -22,16 +22,16 @@ func New{{.ServiceType}}Handler(srv {{.ServiceType}}Handler, opts ...http1.Handl
{{range .Methods}}
r.HandleFunc("{{.Path}}", func(w http.ResponseWriter, r *http.Request) {
var in {{.Request}}
{{if ne (len .Vars) 0}}
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
if err := h.Decode(r, &in{{.Body}}); err != nil {
h.Error(w, r, err)
return
}
{{end}}
if err := h.Decode(r, &in{{.Body}}); err != nil {
{{if ne (len .Vars) 0}}
if err := binding.MapProto(&in, mux.Vars(r)); err != nil {
h.Error(w, r, err)
return
}
{{end}}
next := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.{{.Name}}(ctx, req.(*{{.Request}}))
}
@ -43,7 +43,8 @@ func New{{.ServiceType}}Handler(srv {{.ServiceType}}Handler, opts ...http1.Handl
h.Error(w, r, err)
return
}
if err := h.Encode(w, r, out{{.ResponseBody}}); err != nil {
reply := out.(*{{.Reply}})
if err := h.Encode(w, r, reply{{.ResponseBody}}); err != nil {
h.Error(w, r, err)
}
}).Methods("{{.Method}}")

@ -2,6 +2,7 @@ package json
import (
"encoding/json"
"reflect"
"github.com/go-kratos/kratos/v2/encoding"
@ -21,6 +22,8 @@ var (
UnmarshalOptions = protojson.UnmarshalOptions{
DiscardUnknown: true,
}
typeProtoMessage = reflect.TypeOf((*proto.Message)(nil)).Elem()
)
func init() {
@ -38,6 +41,15 @@ func (codec) Marshal(v interface{}) ([]byte, error) {
}
func (codec) Unmarshal(data []byte, v interface{}) error {
rv := reflect.ValueOf(v)
for rv.Kind() == reflect.Ptr {
if rv.IsNil() {
rv.Set(reflect.New(rv.Type().Elem()))
v = rv.Interface()
break
}
rv = rv.Elem()
}
if m, ok := v.(proto.Message); ok {
return UnmarshalOptions.Unmarshal(data, m)
}

@ -2,10 +2,11 @@ package http
import (
"context"
"encoding/json"
"io/ioutil"
"net/http"
"time"
"github.com/go-kratos/kratos/v2/encoding"
"github.com/go-kratos/kratos/v2/errors"
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/transport"
@ -116,11 +117,24 @@ func Do(client *http.Client, req *http.Request, target interface{}) error {
}
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode > 299 {
se := &errors.StatusError{}
if err := json.NewDecoder(req.Body).Decode(se); err != nil {
se := &errors.StatusError{Code: 2}
if err := decodeResponse(res, se); err != nil {
return err
}
return se
}
return json.NewDecoder(req.Body).Decode(target)
return decodeResponse(res, target)
}
func decodeResponse(res *http.Response, target interface{}) error {
subtype := contentSubtype(res.Header.Get(contentTypeHeader))
codec := encoding.GetCodec(subtype)
if codec == nil {
codec = encoding.GetCodec("json")
}
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
return codec.Unmarshal(data, target)
}

@ -1,7 +1,6 @@
package http
import (
"encoding/json"
"io/ioutil"
"net/http"
"strings"
@ -96,18 +95,22 @@ func decodeRequest(req *http.Request, v interface{}) error {
// encodeResponse encodes the object to the HTTP response.
func encodeResponse(w http.ResponseWriter, r *http.Request, v interface{}) error {
var codec encoding.Codec
for _, accept := range r.Header[acceptHeader] {
if codec := encoding.GetCodec(contentSubtype(accept)); codec != nil {
data, err := codec.Marshal(v)
if err != nil {
return err
}
w.Header().Set(contentTypeHeader, contentType(codec.Name()))
w.Write(data)
return nil
if codec = encoding.GetCodec(contentSubtype(accept)); codec != nil {
break
}
}
return json.NewEncoder(w).Encode(v)
if codec == nil {
codec = encoding.GetCodec("json")
}
data, err := codec.Marshal(v)
if err != nil {
return err
}
w.Header().Set(contentTypeHeader, contentType(codec.Name()))
w.Write(data)
return nil
}
// encodeError encodes the erorr to the HTTP response.
@ -116,7 +119,6 @@ func encodeError(w http.ResponseWriter, r *http.Request, err error) {
if !ok {
se = &errors.StatusError{
Code: 2,
Reason: "",
Message: err.Error(),
}
}

Loading…
Cancel
Save