1
0
mirror of https://github.com/ggicci/httpin.git synced 2024-12-10 10:00:07 +02:00
httpin/httpin.go
2021-04-21 13:38:48 +08:00

92 lines
2.1 KiB
Go

package httpin
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"reflect"
)
func New(inputStruct interface{}) Middleware {
engine, err := NewEngine(inputStruct)
if err != nil {
panic(fmt.Errorf("httpin: unable to create engine: %w", err))
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// Here we read the request and decode it to fill our structure.
// Once failed, the request should end here.
input, err := engine.ReadRequest(r)
if err != nil {
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
// We put the `input` to the request's context, and it will pass to the next hop.
ctx := context.WithValue(r.Context(), "httpin", input)
next.ServeHTTP(rw, r.WithContext(ctx))
})
}
}
func NewEngine(inputStruct interface{}) (*Engine, error) {
typ := reflect.TypeOf(inputStruct) // retrieve type information
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
return nil, UnsupportedType(typ.Name())
}
// TODO(ggicci): check typ
engine := &Engine{
inputType: typ,
}
// if err := engine.build(); err != nil {
// return nil, fmt.Errorf("httpin: build: %w", err)
// }
return engine, nil
}
type Engine struct {
inputType reflect.Type
}
func (e *Engine) ReadRequest(r *http.Request) (interface{}, error) {
return nil, nil
}
func (e *Engine) ReadForm(form url.Values) (interface{}, error) {
rv, err := readForm(e.inputType, form)
if err != nil {
return nil, err
}
return rv.Interface(), nil
}
func (e *Engine) ReadBody(body io.Reader) (interface{}, error) {
rv := e.newInstance()
if err := json.NewDecoder(body).Decode(rv.Interface()); err != nil {
return nil, fmt.Errorf("httpin: json decode: %w", err)
}
return rv.Interface(), nil
}
// newInstance creates a new instance of the input struct.
func (e *Engine) newInstance() reflect.Value {
return reflect.New(e.inputType)
}
// build builds extractors for the exported fields of the input struct.
func (e *Engine) build() error {
return errors.New("not implemented")
}