11 // ErrorWriter is responsible for writing the provided error value
13 type ErrorWriter func(context.Context, http.ResponseWriter, error)
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"}`)
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
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)
39 h := &handler{fv, inType, hasCtx, errFunc}
43 func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
47 ctx = context.WithValue(ctx, reqKey, req)
48 ctx = context.WithValue(ctx, respKey, w)
49 a = append(a, reflect.ValueOf(ctx))
52 inPtr := reflect.New(h.inType)
53 err := Read(req.Body, inPtr.Interface())
55 h.errFunc(req.Context(), w, err)
58 a = append(a, inPtr.Elem())
66 switch n := len(rv); {
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)
76 res = rv[0].Interface()
77 err, _ = rv[1].Interface().(error)
80 h.errFunc(req.Context(), w, err)
84 Write(req.Context(), w, 200, res)
88 errorType = reflect.TypeOf((*error)(nil)).Elem()
89 contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
92 func funcInputType(fv reflect.Value) (hasCtx bool, t reflect.Type, err error) {
94 if ft.Kind() != reflect.Func || ft.IsVariadic() {
95 return false, nil, errors.New("need nonvariadic func in " + ft.String())
98 off := 0 // or 1 with context
99 hasCtx = ft.NumIn() >= 1 && ft.In(0).Implements(contextType)
104 if ft.NumIn() > off+1 {
105 return false, nil, errors.New("too many params in " + ft.String())
108 if ft.NumIn() == off+1 {
109 t = ft.In(ft.NumIn() - 1)
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())
115 return false, nil, errors.New("need at most two return values in " + ft.String())
118 return hasCtx, t, nil