dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/rest/rest_invoker.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package rest
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"net/http"
    24  )
    25  
    26  import (
    27  	perrors "github.com/pkg/errors"
    28  )
    29  
    30  import (
    31  	"dubbo.apache.org/dubbo-go/v3/common"
    32  	"dubbo.apache.org/dubbo-go/v3/protocol"
    33  	invocation_impl "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
    34  	"dubbo.apache.org/dubbo-go/v3/protocol/rest/client"
    35  	"dubbo.apache.org/dubbo-go/v3/protocol/rest/config"
    36  )
    37  
    38  // nolint
    39  type RestInvoker struct {
    40  	protocol.BaseInvoker
    41  	client              client.RestClient
    42  	restMethodConfigMap map[string]*config.RestMethodConfig
    43  }
    44  
    45  // NewRestInvoker returns a RestInvoker
    46  func NewRestInvoker(url *common.URL, client *client.RestClient, restMethodConfig map[string]*config.RestMethodConfig) *RestInvoker {
    47  	return &RestInvoker{
    48  		BaseInvoker:         *protocol.NewBaseInvoker(url),
    49  		client:              *client,
    50  		restMethodConfigMap: restMethodConfig,
    51  	}
    52  }
    53  
    54  // Invoke is used to call service method by invocation
    55  func (ri *RestInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
    56  	inv := invocation.(*invocation_impl.RPCInvocation)
    57  	methodConfig := ri.restMethodConfigMap[inv.MethodName()]
    58  	var (
    59  		result      protocol.RPCResult
    60  		body        interface{}
    61  		pathParams  map[string]string
    62  		queryParams map[string]string
    63  		header      http.Header
    64  		err         error
    65  	)
    66  	if methodConfig == nil {
    67  		result.Err = perrors.Errorf("[RestInvoker] Rest methodConfig:%s is nil", inv.MethodName())
    68  		return &result
    69  	}
    70  	if pathParams, err = restStringMapTransform(methodConfig.PathParamsMap, inv.Arguments()); err != nil {
    71  		result.Err = err
    72  		return &result
    73  	}
    74  	if queryParams, err = restStringMapTransform(methodConfig.QueryParamsMap, inv.Arguments()); err != nil {
    75  		result.Err = err
    76  		return &result
    77  	}
    78  	if header, err = getRestHttpHeader(methodConfig, inv.Arguments()); err != nil {
    79  		result.Err = err
    80  		return &result
    81  	}
    82  	if len(inv.Arguments()) > methodConfig.Body && methodConfig.Body >= 0 {
    83  		body = inv.Arguments()[methodConfig.Body]
    84  	}
    85  	req := &client.RestClientRequest{
    86  		Location:    ri.GetURL().Location,
    87  		Method:      methodConfig.MethodType,
    88  		Path:        methodConfig.Path,
    89  		PathParams:  pathParams,
    90  		QueryParams: queryParams,
    91  		Body:        body,
    92  		Header:      header,
    93  	}
    94  	result.Err = ri.client.Do(req, inv.Reply())
    95  	if result.Err == nil {
    96  		result.Rest = inv.Reply()
    97  	}
    98  	return &result
    99  }
   100  
   101  // restStringMapTransform is used to transform rest map
   102  func restStringMapTransform(paramsMap map[int]string, args []interface{}) (map[string]string, error) {
   103  	resMap := make(map[string]string, len(paramsMap))
   104  	for k, v := range paramsMap {
   105  		if k >= len(args) || k < 0 {
   106  			return nil, perrors.Errorf("[Rest Invoke] Index %v is out of bundle", k)
   107  		}
   108  		resMap[v] = fmt.Sprint(args[k])
   109  	}
   110  	return resMap, nil
   111  }
   112  
   113  // nolint
   114  func getRestHttpHeader(methodConfig *config.RestMethodConfig, args []interface{}) (http.Header, error) {
   115  	header := http.Header{}
   116  	headersMap := methodConfig.HeadersMap
   117  	header.Set("Content-Type", methodConfig.Consumes)
   118  	header.Set("Accept", methodConfig.Produces)
   119  	for k, v := range headersMap {
   120  		if k >= len(args) || k < 0 {
   121  			return nil, perrors.Errorf("[Rest Invoke] Index %v is out of bundle", k)
   122  		}
   123  		header.Set(v, fmt.Sprint(args[k]))
   124  	}
   125  	return header, nil
   126  }