github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/net/http/httpjson/handler.go (about)

     1  package httpjson
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"net/http"
     8  	"reflect"
     9  )
    10  
    11  // ErrorWriter is responsible for writing the provided error value
    12  // to the response.
    13  type ErrorWriter func(context.Context, http.ResponseWriter, error)
    14  
    15  // DefaultResponse will be sent as the response body
    16  // when the handler function signature
    17  // has no return value.
    18  var DefaultResponse = json.RawMessage(`{"message":"ok"}`)
    19  
    20  // handler is an http.Handler that calls a function for each request.
    21  // It uses the signature of the function to decide how to interpret
    22  type handler struct {
    23  	fv      reflect.Value
    24  	inType  reflect.Type
    25  	hasCtx  bool
    26  	errFunc ErrorWriter
    27  }
    28  
    29  // Handler returns an HTTP handler for function f.
    30  // See the package doc for details on allowed signatures for f.
    31  // If f returns a non-nil error, the handler will call errFunc.
    32  func Handler(f interface{}, errFunc ErrorWriter) (http.Handler, error) {
    33  	fv := reflect.ValueOf(f)
    34  	hasCtx, inType, err := funcInputType(fv)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	h := &handler{fv, inType, hasCtx, errFunc}
    40  	return h, nil
    41  }
    42  
    43  func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    44  	var a []reflect.Value
    45  	if h.hasCtx {
    46  		ctx := req.Context()
    47  		ctx = context.WithValue(ctx, reqKey, req)
    48  		ctx = context.WithValue(ctx, respKey, w)
    49  		a = append(a, reflect.ValueOf(ctx))
    50  	}
    51  	if h.inType != nil {
    52  		inPtr := reflect.New(h.inType)
    53  		err := Read(req.Body, inPtr.Interface())
    54  		if err != nil {
    55  			h.errFunc(req.Context(), w, err)
    56  			return
    57  		}
    58  		a = append(a, inPtr.Elem())
    59  	}
    60  	rv := h.fv.Call(a)
    61  
    62  	var (
    63  		res interface{}
    64  		err error
    65  	)
    66  	switch n := len(rv); {
    67  	case n == 0:
    68  		res = &DefaultResponse
    69  	case n == 1 && !h.fv.Type().Out(0).Implements(errorType):
    70  		res = rv[0].Interface()
    71  	case n == 1 && h.fv.Type().Out(0).Implements(errorType):
    72  		// out param is of type error; its value can still be nil
    73  		res = &DefaultResponse
    74  		err, _ = rv[0].Interface().(error)
    75  	case n == 2:
    76  		res = rv[0].Interface()
    77  		err, _ = rv[1].Interface().(error)
    78  	}
    79  	if err != nil {
    80  		h.errFunc(req.Context(), w, err)
    81  		return
    82  	}
    83  
    84  	Write(req.Context(), w, 200, res)
    85  }
    86  
    87  var (
    88  	errorType   = reflect.TypeOf((*error)(nil)).Elem()
    89  	contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
    90  )
    91  
    92  func funcInputType(fv reflect.Value) (hasCtx bool, t reflect.Type, err error) {
    93  	ft := fv.Type()
    94  	if ft.Kind() != reflect.Func || ft.IsVariadic() {
    95  		return false, nil, errors.New("need nonvariadic func in " + ft.String())
    96  	}
    97  
    98  	off := 0 // or 1 with context
    99  	hasCtx = ft.NumIn() >= 1 && ft.In(0).Implements(contextType)
   100  	if hasCtx {
   101  		off = 1
   102  	}
   103  
   104  	if ft.NumIn() > off+1 {
   105  		return false, nil, errors.New("too many params in " + ft.String())
   106  	}
   107  
   108  	if ft.NumIn() == off+1 {
   109  		t = ft.In(ft.NumIn() - 1)
   110  	}
   111  
   112  	if n := ft.NumOut(); n == 2 && !ft.Out(1).Implements(errorType) {
   113  		return false, nil, errors.New("second return value must be error in " + ft.String())
   114  	} else if n > 2 {
   115  		return false, nil, errors.New("need at most two return values in " + ft.String())
   116  	}
   117  
   118  	return hasCtx, t, nil
   119  }