dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/rest/server/rest_server.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 server
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"net/http"
    24  	"reflect"
    25  	"strconv"
    26  )
    27  
    28  import (
    29  	"github.com/dubbogo/gost/log/logger"
    30  
    31  	perrors "github.com/pkg/errors"
    32  )
    33  
    34  import (
    35  	"dubbo.apache.org/dubbo-go/v3/common"
    36  	"dubbo.apache.org/dubbo-go/v3/protocol"
    37  	"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
    38  	rest_config "dubbo.apache.org/dubbo-go/v3/protocol/rest/config"
    39  )
    40  
    41  const parseParameterErrorStr = "An error occurred while parsing parameters on the server"
    42  
    43  // RestServer user can implement this server interface
    44  type RestServer interface {
    45  	// Start rest server
    46  	Start(url *common.URL)
    47  	// Deploy a http api
    48  	Deploy(restMethodConfig *rest_config.RestMethodConfig, routeFunc func(request RestServerRequest, response RestServerResponse))
    49  	// UnDeploy a http api
    50  	UnDeploy(restMethodConfig *rest_config.RestMethodConfig)
    51  	// Destroy rest server
    52  	Destroy()
    53  }
    54  
    55  // RestServerRequest interface
    56  type RestServerRequest interface {
    57  	// RawRequest get the Ptr of http.Request
    58  	RawRequest() *http.Request
    59  	// PathParameter get the path parameter by name
    60  	PathParameter(name string) string
    61  	// PathParameters get the map of the path parameters
    62  	PathParameters() map[string]string
    63  	// QueryParameter get the query parameter by name
    64  	QueryParameter(name string) string
    65  	// QueryParameters get the map of query parameters
    66  	QueryParameters(name string) []string
    67  	// BodyParameter get the body parameter of name
    68  	BodyParameter(name string) (string, error)
    69  	// HeaderParameter get the header parameter of name
    70  	HeaderParameter(name string) string
    71  	// ReadEntity checks the Accept header and reads the content into the entityPointer.
    72  	ReadEntity(entityPointer interface{}) error
    73  }
    74  
    75  // RestServerResponse interface
    76  type RestServerResponse interface {
    77  	http.ResponseWriter
    78  	// WriteError writes the http status and the error string on the response. err can be nil.
    79  	// Return an error if writing was not successful.
    80  	WriteError(httpStatus int, err error) (writeErr error)
    81  	// WriteEntity marshals the value using the representation denoted by the Accept Header.
    82  	WriteEntity(value interface{}) error
    83  }
    84  
    85  // GetRouteFunc is a route function will be invoked by http server
    86  func GetRouteFunc(invoker protocol.Invoker, methodConfig *rest_config.RestMethodConfig) func(req RestServerRequest, resp RestServerResponse) {
    87  	return func(req RestServerRequest, resp RestServerResponse) {
    88  		var (
    89  			err  error
    90  			args []interface{}
    91  		)
    92  		svc := common.ServiceMap.GetServiceByServiceKey(invoker.GetURL().Protocol, invoker.GetURL().ServiceKey())
    93  		// get method
    94  		method := svc.Method()[methodConfig.MethodName]
    95  		argsTypes := method.ArgsType()
    96  		replyType := method.ReplyType()
    97  		// two ways to prepare arguments
    98  		// if method like this 'func1(req []interface{}, rsp *User) error'
    99  		// we don't have arguments type
   100  		if (len(argsTypes) == 1 || len(argsTypes) == 2 && replyType == nil) &&
   101  			argsTypes[0].String() == "[]interface {}" {
   102  			args, err = getArgsInterfaceFromRequest(req, methodConfig)
   103  		} else {
   104  			args, err = getArgsFromRequest(req, argsTypes, methodConfig)
   105  		}
   106  		if err != nil {
   107  			logger.Errorf("[Go Restful] parsing http parameters error:%v", err)
   108  			err = resp.WriteError(http.StatusInternalServerError, errors.New(parseParameterErrorStr))
   109  			if err != nil {
   110  				logger.Errorf("[Go Restful] WriteErrorString error:%v", err)
   111  			}
   112  		}
   113  		result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]interface{})))
   114  		if result.Error() != nil {
   115  			err = resp.WriteError(http.StatusInternalServerError, result.Error())
   116  			if err != nil {
   117  				logger.Errorf("[Go Restful] WriteError error:%v", err)
   118  			}
   119  			return
   120  		}
   121  		err = resp.WriteEntity(result.Result())
   122  		if err != nil {
   123  			logger.Errorf("[Go Restful] WriteEntity error:%v", err)
   124  		}
   125  	}
   126  }
   127  
   128  // getArgsInterfaceFromRequest when service function like GetUser(req []interface{}, rsp *User) error
   129  // use this method to get arguments
   130  func getArgsInterfaceFromRequest(req RestServerRequest, methodConfig *rest_config.RestMethodConfig) ([]interface{}, error) {
   131  	argsMap := make(map[int]interface{}, 8)
   132  	maxKey := 0
   133  	for k, v := range methodConfig.PathParamsMap {
   134  		if maxKey < k {
   135  			maxKey = k
   136  		}
   137  		argsMap[k] = req.PathParameter(v)
   138  	}
   139  	for k, v := range methodConfig.QueryParamsMap {
   140  		if maxKey < k {
   141  			maxKey = k
   142  		}
   143  		params := req.QueryParameters(v)
   144  		if len(params) == 1 {
   145  			argsMap[k] = params[0]
   146  		} else {
   147  			argsMap[k] = params
   148  		}
   149  	}
   150  	for k, v := range methodConfig.HeadersMap {
   151  		if maxKey < k {
   152  			maxKey = k
   153  		}
   154  		argsMap[k] = req.HeaderParameter(v)
   155  	}
   156  	if methodConfig.Body >= 0 {
   157  		if maxKey < methodConfig.Body {
   158  			maxKey = methodConfig.Body
   159  		}
   160  		m := make(map[string]interface{})
   161  		// TODO read as a slice
   162  		if err := req.ReadEntity(&m); err != nil {
   163  			return nil, perrors.Errorf("[Go restful] Read body entity as map[string]interface{} error:%v", err)
   164  		}
   165  		argsMap[methodConfig.Body] = m
   166  	}
   167  	args := make([]interface{}, maxKey+1)
   168  	for k, v := range argsMap {
   169  		if k >= 0 {
   170  			args[k] = v
   171  		}
   172  	}
   173  	return args, nil
   174  }
   175  
   176  // getArgsFromRequest get arguments from server.RestServerRequest
   177  func getArgsFromRequest(req RestServerRequest, argsTypes []reflect.Type, methodConfig *rest_config.RestMethodConfig) ([]interface{}, error) {
   178  	argsLength := len(argsTypes)
   179  	args := make([]interface{}, argsLength)
   180  	for i, t := range argsTypes {
   181  		args[i] = reflect.Zero(t).Interface()
   182  	}
   183  	if err := assembleArgsFromPathParams(methodConfig, argsLength, argsTypes, req, args); err != nil {
   184  		return nil, err
   185  	}
   186  	if err := assembleArgsFromQueryParams(methodConfig, argsLength, argsTypes, req, args); err != nil {
   187  		return nil, err
   188  	}
   189  	if err := assembleArgsFromBody(methodConfig, argsTypes, req, args); err != nil {
   190  		return nil, err
   191  	}
   192  	if err := assembleArgsFromHeaders(methodConfig, req, argsLength, argsTypes, args); err != nil {
   193  		return nil, err
   194  	}
   195  	return args, nil
   196  }
   197  
   198  // assembleArgsFromHeaders assemble arguments from headers
   199  func assembleArgsFromHeaders(methodConfig *rest_config.RestMethodConfig, req RestServerRequest, argsLength int, argsTypes []reflect.Type, args []interface{}) error {
   200  	for k, v := range methodConfig.HeadersMap {
   201  		param := req.HeaderParameter(v)
   202  		if k < 0 || k >= argsLength {
   203  			return perrors.Errorf("[Go restful] Header param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName)
   204  		}
   205  		t := argsTypes[k]
   206  		if t.Kind() == reflect.Ptr {
   207  			t = t.Elem()
   208  		}
   209  		if t.Kind() == reflect.String {
   210  			args[k] = param
   211  		} else {
   212  			return perrors.Errorf("[Go restful] Header param parse error, the index %v args's type isn't string", k)
   213  		}
   214  	}
   215  	return nil
   216  }
   217  
   218  // assembleArgsFromBody assemble arguments from body
   219  func assembleArgsFromBody(methodConfig *rest_config.RestMethodConfig, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error {
   220  	if methodConfig.Body >= 0 && methodConfig.Body < len(argsTypes) {
   221  		t := argsTypes[methodConfig.Body]
   222  		kind := t.Kind()
   223  		if kind == reflect.Ptr {
   224  			t = t.Elem()
   225  		}
   226  		var ni interface{}
   227  		if t.String() == "[]interface {}" {
   228  			ni = make([]map[string]interface{}, 0)
   229  		} else if t.String() == "interface {}" {
   230  			ni = make(map[string]interface{})
   231  		} else {
   232  			n := reflect.New(t)
   233  			if n.CanInterface() {
   234  				ni = n.Interface()
   235  			}
   236  		}
   237  		if err := req.ReadEntity(&ni); err != nil {
   238  			return perrors.Errorf("[Go restful] Read body entity error, error is %v", perrors.WithStack(err))
   239  		}
   240  		args[methodConfig.Body] = ni
   241  	}
   242  	return nil
   243  }
   244  
   245  // assembleArgsFromQueryParams assemble arguments from query params
   246  func assembleArgsFromQueryParams(methodConfig *rest_config.RestMethodConfig, argsLength int, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error {
   247  	var (
   248  		err   error
   249  		param interface{}
   250  		i64   int64
   251  	)
   252  	for k, v := range methodConfig.QueryParamsMap {
   253  		if k < 0 || k >= argsLength {
   254  			return perrors.Errorf("[Go restful] Query param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName)
   255  		}
   256  		t := argsTypes[k]
   257  		kind := t.Kind()
   258  		if kind == reflect.Ptr {
   259  			t = t.Elem()
   260  			kind = t.Kind()
   261  		}
   262  		if kind == reflect.Slice {
   263  			param = req.QueryParameters(v)
   264  		} else if kind == reflect.String {
   265  			param = req.QueryParameter(v)
   266  		} else if kind == reflect.Int {
   267  			param, err = strconv.Atoi(req.QueryParameter(v))
   268  		} else if kind == reflect.Int32 {
   269  			i64, err = strconv.ParseInt(req.QueryParameter(v), 10, 32)
   270  			if err == nil {
   271  				param = int32(i64)
   272  			}
   273  		} else if kind == reflect.Int64 {
   274  			param, err = strconv.ParseInt(req.QueryParameter(v), 10, 64)
   275  		} else {
   276  			return perrors.Errorf("[Go restful] Query param parse error, the index %v args's type isn't int or string or slice", k)
   277  		}
   278  		if err != nil {
   279  			return perrors.Errorf("[Go restful] Query param parse error, error:%v", perrors.WithStack(err))
   280  		}
   281  		args[k] = param
   282  	}
   283  	return nil
   284  }
   285  
   286  // assembleArgsFromPathParams assemble arguments from path params
   287  func assembleArgsFromPathParams(methodConfig *rest_config.RestMethodConfig, argsLength int, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error {
   288  	var (
   289  		err   error
   290  		param interface{}
   291  		i64   int64
   292  	)
   293  	for k, v := range methodConfig.PathParamsMap {
   294  		if k < 0 || k >= argsLength {
   295  			return perrors.Errorf("[Go restful] Path param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName)
   296  		}
   297  		t := argsTypes[k]
   298  		kind := t.Kind()
   299  		if kind == reflect.Ptr {
   300  			t = t.Elem()
   301  			kind = t.Kind()
   302  		}
   303  		if kind == reflect.Int {
   304  			param, err = strconv.Atoi(req.PathParameter(v))
   305  		} else if kind == reflect.Int32 {
   306  			i64, err = strconv.ParseInt(req.PathParameter(v), 10, 32)
   307  			if err == nil {
   308  				param = int32(i64)
   309  			}
   310  		} else if kind == reflect.Int64 {
   311  			param, err = strconv.ParseInt(req.PathParameter(v), 10, 64)
   312  		} else if kind == reflect.String {
   313  			param = req.PathParameter(v)
   314  		} else {
   315  			return perrors.Errorf("[Go restful] Path param parse error, the index %v args's type isn't int or string", k)
   316  		}
   317  		if err != nil {
   318  			return perrors.Errorf("[Go restful] Path param parse error, error is %v", perrors.WithStack(err))
   319  		}
   320  		args[k] = param
   321  	}
   322  	return nil
   323  }