Alanxtl commented on code in PR #3174: URL: https://github.com/apache/dubbo-go/pull/3174#discussion_r2724949130
########## filter/generic/service.go: ########## @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package generic + +import ( + "context" + "reflect" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/filter/generic/generalizer" +) + +// GenericService uses for generic invoke for service call +type GenericService struct { + Invoke func(ctx context.Context, methodName string, types []string, args []hessian.Object) (any, error) `dubbo:"$invoke"` + referenceStr string +} + +// NewGenericService returns a GenericService instance +func NewGenericService(referenceStr string) *GenericService { + return &GenericService{referenceStr: referenceStr} +} + +// Reference gets referenceStr from GenericService +func (s *GenericService) Reference() string { + return s.referenceStr +} + +// InvokeWithType invokes the remote method and deserializes the result into the reply struct. +// The reply parameter must be a non-nil pointer to the target type. +// +// Note: This method uses MapGeneralizer for deserialization, which means it only supports +// the default map-based generic serialization (generic=true). If you are using other +// serialization types like Gson or Protobuf-JSON, use the Invoke method directly and +// handle deserialization manually. +// +// Example usage: +// +// var user User +// err := genericService.InvokeWithType(ctx, "getUser", []string{"java.lang.String"}, []hessian.Object{"123"}, &user) +// if err != nil { +// return err +// } +// fmt.Println(user.Name, user.Age) +func (s *GenericService) InvokeWithType(ctx context.Context, methodName string, types []string, args []hessian.Object, reply any) error { + // Validate the reply pointer + replyValue, err := validateReplyPointer(reply) + if err != nil { + return err + } + + // Call the underlying Invoke method + result, err := s.Invoke(ctx, methodName, types, args) + if err != nil { + return err + } + + if result == nil { + return nil + } + + // Get the element type that the pointer points to + replyType := replyValue.Elem().Type() + + // Use MapGeneralizer to realize the map result to the target struct + g := generalizer.GetMapGeneralizer() + realized, err := realizeResult(result, replyType, g) + if err != nil { + return err + } + + // Set the realized value to reply + replyValue.Elem().Set(reflect.ValueOf(realized)) + return nil Review Comment: ditto ########## filter/generic/filter.go: ########## @@ -152,8 +153,65 @@ func (f *genericFilter) Invoke(ctx context.Context, invoker base.Invoker, inv ba return invoker.Invoke(ctx, inv) } -// OnResponse dummy process, returns the result directly -func (f *genericFilter) OnResponse(_ context.Context, result result.Result, _ base.Invoker, - _ base.Invocation) result.Result { - return result +// OnResponse deserializes the map result to the target struct if reply is provided. +// If inv.Reply() is a non-nil pointer to a struct, the map result will be automatically +// deserialized into it using the appropriate generalizer. +func (f *genericFilter) OnResponse(_ context.Context, res result.Result, invoker base.Invoker, + inv base.Invocation) result.Result { + // Only process if this is a generic call and there's no error + if res.Error() != nil { + return res + } + + // Check if this is a generic invocation + if !isGeneric(invoker.GetURL().GetParam(constant.GenericKey, "")) { + return res + } + + // Get the reply from invocation + reply := inv.Reply() + if reply == nil { + return res + } + + // Check if reply is a valid pointer + replyValue := reflect.ValueOf(reply) + if replyValue.Kind() != reflect.Ptr || replyValue.IsNil() { + return res + } + + // Get the result data + data := res.Result() + if data == nil { + return res + } + + // Check if data is a map type that needs to be deserialized + dataValue := reflect.ValueOf(data) + if dataValue.Kind() != reflect.Map && dataValue.Kind() != reflect.Slice { + // If data is not a map or slice, it's already a primitive type, no need to deserialize + return res + } + + // Get the element type that the pointer points to + replyElemType := replyValue.Elem().Type() + + // Get the generalizer based on the generic serialization type + generic := invoker.GetURL().GetParam(constant.GenericKey, constant.GenericSerializationDefault) + g := getGeneralizer(generic) + + // Realize the map/slice to the target struct using shared helper + realized, err := realizeResult(data, replyElemType, g) + if err != nil { + logger.Warnf("failed to deserialize generic result: %v", err) + return res + } + + // Set the realized value to reply + replyValue.Elem().Set(reflect.ValueOf(realized)) Review Comment: 这里,输入`realizeResult`的`data`如果是`nil`,`realizeResult`会返回nil,nil,后面又把nil 赋值了`replyValue.Elem().Set(reflect.ValueOf(realized))`,这里下游会不会panic啊 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
