feature: add http middleware capability #18

Merged
xdrm-brackets merged 5 commits from feature/middleware into 0.3.0 2021-04-18 16:08:12 +00:00
4 changed files with 55 additions and 10 deletions

6
api/adapter.go Normal file
View File

@ -0,0 +1,6 @@
package api
import "net/http"
// Adapter to encapsulate incoming requests
type Adapter func(http.HandlerFunc) http.HandlerFunc

View File

@ -5,6 +5,7 @@ import (
"io"
"net/http"
"git.xdrm.io/go/aicra/api"
"git.xdrm.io/go/aicra/datatype"
"git.xdrm.io/go/aicra/internal/config"
"git.xdrm.io/go/aicra/internal/dynfunc"
@ -14,6 +15,7 @@ import (
type Builder struct {
conf *config.Server
handlers []*apiHandler
adapters []api.Adapter
}
// represents an api handler (method-pattern combination)
@ -31,9 +33,23 @@ func (b *Builder) AddType(t datatype.T) {
if b.conf.Services != nil {
panic(errLateType)
}
if b.conf.Types == nil {
b.conf.Types = make([]datatype.T, 0)
}
b.conf.Types = append(b.conf.Types, t)
}
// Use adds an http adapter (middleware)
func (b *Builder) Use(adapter api.Adapter) {
if b.conf == nil {
b.conf = &config.Server{}
}
if b.adapters == nil {
b.adapters = make([]api.Adapter, 0)
}
b.adapters = append(b.adapters, adapter)
}
// Setup the builder with its api definition file
// panics if already setup
func (b *Builder) Setup(r io.Reader) error {

View File

@ -1,7 +1,6 @@
package aicra
import (
"log"
"net/http"
"git.xdrm.io/go/aicra/api"
@ -12,17 +11,17 @@ import (
// Handler wraps the builder to handle requests
type Handler Builder
// ServeHTTP implements http.Handler
// ServeHTTP implements http.Handler and wraps it in middlewares (adapters)
func (s Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer func() {
if rc := recover(); rc != nil {
log.Printf("recovering request: %s\n", rc)
// try to send error response
api.EmptyResponse().WithError(api.ErrUncallableService).ServeHTTP(w, r)
}
}()
defer r.Body.Close()
var h = http.HandlerFunc(s.handleRequest)
for _, adapter := range s.adapters {
h = adapter(h)
}
h(w, r)
}
func (s Handler) handleRequest(w http.ResponseWriter, r *http.Request) {
// 1. find a matching service from config
var service = s.conf.Find(r)
if service == nil {

View File

@ -2,6 +2,7 @@ package dynfunc
import (
"fmt"
"log"
"reflect"
"git.xdrm.io/go/aicra/api"
@ -70,6 +71,29 @@ func (h *Handler) Handle(data map[string]interface{}) (map[string]interface{}, a
if !inData {
continue
}
var refvalue = reflect.ValueOf(value)
// T to pointer of T
if field.Kind() == reflect.Ptr {
var ptrType = field.Type().Elem()
if !refvalue.Type().ConvertibleTo(ptrType) {
log.Printf("Cannot convert %v into %v", refvalue.Type(), ptrType)
return nil, api.ErrUncallableService
}
ptr := reflect.New(ptrType)
ptr.Elem().Set(reflect.ValueOf(value).Convert(ptrType))
field.Set(ptr)
continue
}
if !reflect.ValueOf(value).Type().ConvertibleTo(field.Type()) {
log.Printf("Cannot convert %v into %v", reflect.ValueOf(value).Type(), field.Type())
return nil, api.ErrUncallableService
}
field.Set(reflect.ValueOf(value).Convert(field.Type()))
}
callArgs = append(callArgs, callStruct)