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