feat: fix error creation to bind arguments directly + add optional error argument in NewResponse()
This commit is contained in:
parent
4e25c647b2
commit
b9f240b86b
|
@ -13,6 +13,14 @@ type Error struct {
|
||||||
Arguments []interface{} `json:"arguments"`
|
Arguments []interface{} `json:"arguments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewError returns a new error from a base error with errorarguments.
|
||||||
|
func NewError(baseError Error, arguments ...interface{}) Error {
|
||||||
|
for _, arg := range arguments {
|
||||||
|
baseError.Put(arg)
|
||||||
|
}
|
||||||
|
return baseError
|
||||||
|
}
|
||||||
|
|
||||||
// Put adds an argument to the error
|
// Put adds an argument to the error
|
||||||
// to be displayed back to API caller
|
// to be displayed back to API caller
|
||||||
func (e *Error) Put(arg interface{}) {
|
func (e *Error) Put(arg interface{}) {
|
||||||
|
|
|
@ -16,14 +16,21 @@ type Response struct {
|
||||||
Err Error
|
Err Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewResponse creates an empty response
|
// NewResponse creates an empty response. An optional error can be passed as its first argument.
|
||||||
func NewResponse() *Response {
|
func NewResponse(errors ...Error) *Response {
|
||||||
return &Response{
|
res := &Response{
|
||||||
Status: http.StatusOK,
|
Status: http.StatusOK,
|
||||||
Data: make(ResponseData),
|
Data: make(ResponseData),
|
||||||
Err: ErrorFailure(),
|
Err: ErrorFailure(),
|
||||||
Headers: make(http.Header),
|
Headers: make(http.Header),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optional error
|
||||||
|
if len(errors) == 1 {
|
||||||
|
res.Err = errors[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetData adds/overrides a new response field
|
// SetData adds/overrides a new response field
|
||||||
|
@ -51,3 +58,17 @@ func (i *Response) MarshalJSON() ([]byte, error) {
|
||||||
|
|
||||||
return json.Marshal(fmt)
|
return json.Marshal(fmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write writes to an HTTP response.
|
||||||
|
func (i *Response) Write(w http.ResponseWriter) error {
|
||||||
|
w.WriteHeader(i.Status)
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
fmt, err := json.Marshal(i)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Write(fmt)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,19 +6,18 @@ import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewReader craetes a new reader
|
// NewReader creates a new reader from a reader and a boundary.
|
||||||
func NewReader(_src io.Reader, _boundary string) (*Reader, error) {
|
func NewReader(r io.Reader, boundary string) (*Reader, error) {
|
||||||
|
|
||||||
reader := &Reader{
|
reader := &Reader{
|
||||||
reader: nil,
|
reader: nil,
|
||||||
boundary: fmt.Sprintf("--%s", _boundary),
|
boundary: fmt.Sprintf("--%s", boundary),
|
||||||
Data: make(map[string]*Component),
|
Data: make(map[string]*Component),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Create reader
|
// 1. Create reader
|
||||||
dst, ok := _src.(*bufio.Reader)
|
dst, ok := r.(*bufio.Reader)
|
||||||
if !ok {
|
if !ok {
|
||||||
dst = bufio.NewReader(_src)
|
dst = bufio.NewReader(r)
|
||||||
}
|
}
|
||||||
reader.reader = dst
|
reader.reader = dst
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,24 @@ package multipart
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ConstError is a wrapper to set constant errors
|
||||||
|
type ConstError string
|
||||||
|
|
||||||
|
// Error implements error
|
||||||
|
func (err ConstError) Error() string {
|
||||||
|
return string(err)
|
||||||
|
}
|
||||||
|
|
||||||
// ErrMissingDataName is set when a multipart variable/file has no name="..."
|
// ErrMissingDataName is set when a multipart variable/file has no name="..."
|
||||||
var ErrMissingDataName = errors.New("data has no name")
|
var ErrMissingDataName = ConstError("data has no name")
|
||||||
|
|
||||||
// ErrDataNameConflict is set when a multipart variable/file name is already used
|
// ErrDataNameConflict is set when a multipart variable/file name is already used
|
||||||
var ErrDataNameConflict = errors.New("data name conflict")
|
var ErrDataNameConflict = ConstError("data name conflict")
|
||||||
|
|
||||||
// ErrNoHeader is set when a multipart variable/file has no (valid) header
|
// ErrNoHeader is set when a multipart variable/file has no (valid) header
|
||||||
var ErrNoHeader = errors.New("data has no header")
|
var ErrNoHeader = ConstError("data has no header")
|
||||||
|
|
||||||
// Component represents a multipart variable/file
|
// Component represents a multipart variable/file
|
||||||
type Component struct {
|
type Component struct {
|
||||||
|
|
28
server.go
28
server.go
|
@ -51,7 +51,6 @@ func New(configPath string) (*Server, error) {
|
||||||
|
|
||||||
// ServeHTTP implements http.Handler and has to be called on each request
|
// ServeHTTP implements http.Handler and has to be called on each request
|
||||||
func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
defer req.Body.Close()
|
defer req.Body.Close()
|
||||||
|
|
||||||
// 1. build API request from HTTP request
|
// 1. build API request from HTTP request
|
||||||
|
@ -73,7 +72,9 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||||
// 3. check if matching methodDef exists in config */
|
// 3. check if matching methodDef exists in config */
|
||||||
var methodDef = serviceDef.Method(req.Method)
|
var methodDef = serviceDef.Method(req.Method)
|
||||||
if methodDef == nil {
|
if methodDef == nil {
|
||||||
httpError(res, api.ErrorUnknownMethod())
|
apiResponse := api.NewResponse(api.ErrorUnknownMethod())
|
||||||
|
apiResponse.Write(res)
|
||||||
|
logError(apiResponse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +87,9 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
// Fail if argument check failed
|
// Fail if argument check failed
|
||||||
if paramError.Code != api.ErrorSuccess().Code {
|
if paramError.Code != api.ErrorSuccess().Code {
|
||||||
httpError(res, paramError)
|
apiResponse := api.NewResponse(paramError)
|
||||||
|
apiResponse.Write(res)
|
||||||
|
logError(apiResponse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,16 +112,17 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||||
// fail if found no handler
|
// fail if found no handler
|
||||||
if serviceHandler == nil {
|
if serviceHandler == nil {
|
||||||
if serviceFound {
|
if serviceFound {
|
||||||
apiError := api.ErrorUncallableMethod()
|
apiError := api.NewError(api.ErrorUncallableMethod(), servicePath, req.Method)
|
||||||
apiError.Put(servicePath)
|
apiResponse := api.NewResponse(apiError)
|
||||||
apiError.Put(req.Method)
|
apiResponse.Write(res)
|
||||||
httpError(res, apiError)
|
logError(apiResponse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiError := api.ErrorUncallableService()
|
apiError := api.NewError(api.ErrorUncallableService(), servicePath)
|
||||||
apiError.Put(servicePath)
|
apiResponse := api.NewResponse(apiError)
|
||||||
httpError(res, apiError)
|
apiResponse.Write(res)
|
||||||
|
logError(apiResponse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,8 +142,8 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. build JSON apiResponse
|
// 3. write to response
|
||||||
httpPrint(res, apiResponse)
|
apiResponse.Write(res)
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
33
util.go
33
util.go
|
@ -1,9 +1,7 @@
|
||||||
package aicra
|
package aicra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.xdrm.io/go/aicra/api"
|
"git.xdrm.io/go/aicra/api"
|
||||||
"git.xdrm.io/go/aicra/internal/config"
|
"git.xdrm.io/go/aicra/internal/config"
|
||||||
|
@ -21,17 +19,17 @@ func (s *Server) extractParameters(store *reqdata.Store, methodParam map[string]
|
||||||
// for each param of the config
|
// for each param of the config
|
||||||
for name, param := range methodParam {
|
for name, param := range methodParam {
|
||||||
|
|
||||||
/* (1) Extract value */
|
// 1. extract value
|
||||||
p, isset := store.Set[name]
|
p, isset := store.Set[name]
|
||||||
|
|
||||||
/* (2) Required & missing */
|
// 2. fail if required & missing
|
||||||
if !isset && !param.Optional {
|
if !isset && !param.Optional {
|
||||||
apiError = api.ErrorMissingParam()
|
apiError = api.ErrorMissingParam()
|
||||||
apiError.Put(name)
|
apiError.Put(name)
|
||||||
return nil, apiError
|
return nil, apiError
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (3) Optional & missing: set default value */
|
// 3. optional & missing: set default value
|
||||||
if !isset {
|
if !isset {
|
||||||
p = &reqdata.Parameter{
|
p = &reqdata.Parameter{
|
||||||
Parsed: true,
|
Parsed: true,
|
||||||
|
@ -47,12 +45,12 @@ func (s *Server) extractParameters(store *reqdata.Store, methodParam map[string]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (4) Parse parameter if not file */
|
// 4. parse parameter if not file
|
||||||
if !p.File {
|
if !p.File {
|
||||||
p.Parse()
|
p.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (5) Fail on unexpected multipart file */
|
// 5. fail on unexpected multipart file
|
||||||
waitFile, gotFile := param.Type == "FILE", p.File
|
waitFile, gotFile := param.Type == "FILE", p.File
|
||||||
if gotFile && !waitFile || !gotFile && waitFile {
|
if gotFile && !waitFile || !gotFile && waitFile {
|
||||||
apiError = api.ErrorInvalidParam()
|
apiError = api.ErrorInvalidParam()
|
||||||
|
@ -61,13 +59,13 @@ func (s *Server) extractParameters(store *reqdata.Store, methodParam map[string]
|
||||||
return nil, apiError
|
return nil, apiError
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (6) Do not check if file */
|
// 6. do not check if file
|
||||||
if gotFile {
|
if gotFile {
|
||||||
parameters[param.Rename] = p.Value
|
parameters[param.Rename] = p.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (7) Check type */
|
// 7. check type
|
||||||
if s.Checkers.Run(param.Type, p.Value) != nil {
|
if s.Checkers.Run(param.Type, p.Value) != nil {
|
||||||
|
|
||||||
apiError = api.ErrorInvalidParam()
|
apiError = api.ErrorInvalidParam()
|
||||||
|
@ -85,20 +83,7 @@ func (s *Server) extractParameters(store *reqdata.Store, methodParam map[string]
|
||||||
return parameters, apiError
|
return parameters, apiError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints an HTTP response
|
|
||||||
func httpPrint(r http.ResponseWriter, res *api.Response) {
|
|
||||||
r.WriteHeader(res.Status)
|
|
||||||
|
|
||||||
// write this json
|
|
||||||
jsonResponse, _ := json.Marshal(res)
|
|
||||||
r.Header().Add("Content-Type", "application/json")
|
|
||||||
r.Write(jsonResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prints an error as HTTP response
|
// Prints an error as HTTP response
|
||||||
func httpError(r http.ResponseWriter, e api.Error) {
|
func logError(res *api.Response) {
|
||||||
JSON, _ := json.Marshal(e)
|
log.Printf("[http.fail] %v\n", res.Err)
|
||||||
r.Header().Add("Content-Type", "application/json")
|
|
||||||
r.Write(JSON)
|
|
||||||
log.Printf("[http.fail] %s\n", e.Reason)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue