Documentation ¶
Overview ¶
Package dou is tiny and flexible toolkit for creating a api server.
Simple usage. If you want to see more example, check github.com/ToQoz/dou/example/full
package main import ( "errors" "github.com/ToQoz/dou" _ "github.com/ToQoz/dou/jsonapi" "log" "net" "net/http" "os" "os/signal" "time" ) // --- API Error type --- type apiError struct { Message string `json:"message"` } func newAPIError(err error) *apiError { return &apiError{Message: err.Error()} } type apiErrors struct { Errors []*apiError `json:"errors"` } func newAPIErrors(errs []error) *apiErrors { aErrs := &apiErrors{} for _, err := range errs { aErrs.Errors = append(aErrs.Errors, newAPIError(err)) } return aErrs } func main() { defer teardown() api, err := dou.NewAPI("jsonapi") if err != nil { log.Fatal(err) } api.ReadTimeout = 10 * time.Second api.WriteTimeout = 10 * time.Second api.MaxHeaderBytes = 1 << 20 // --- Map routes --- api.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/": api.Ok(w, map[string]string{"hello": "world"}, http.StatusOK) case "/error": err := errors.New("some error occur") api.Error(w, newAPIError(err), http.StatusInternalServerError) case "/errors": var errs []error errs = append(errs, errors.New("1 error occur")) errs = append(errs, errors.New("2 error occur")) api.Error(w, newAPIErrors(errs), http.StatusInternalServerError) default: api.Error(w, map[string]string{"message": http.StatusText(http.StatusNotFound)}, http.StatusNotFound) } }) // --- Create listener --- // You can use utility, for example github.com/lestrrat/go-server-starter-listener etc. l, err := net.Listen("tcp", ":8099") if err != nil { log.Fatalf("Could not listen: %s", ":8099") } log.Printf("Listen: %s", ":8099") // --- Handle C-c --- c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for sig := range c { log.Print("Stopping the server...") switch sig { case os.Interrupt: // --- Stop Server --- api.Stop() return default: log.Print("Receive unknown signal...") } } }() // --- Run Server --- api.Run(l) } func teardown() { log.Print("Tearing down...") log.Print("Finished - bye bye. ;-)") }
You can creating a custom plugin in accordance with your api type or domain-specific use-case. The plugin should keep following interface.
type Plugin interface { OnPanic(w http.ResponseWriter, r *http.Request) BeforeDispatch(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) AfterDispatch(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) Marshal(v interface{}) ([]byte, error) Unmarshal(data []byte, v interface{}) error APIStatus(w http.ResponseWriter, code int) }
Index ¶
- func Deregister(pluginName string)
- func Register(pluginName string, plugin Plugin)
- type API
- func (api *API) Error(w http.ResponseWriter, resource interface{}, httpStatusCode int)
- func (api *API) Marshal(v interface{}) ([]byte, error)
- func (api *API) Ok(w http.ResponseWriter, resource interface{}, httpStatusCode int)
- func (api *API) Run(l net.Listener)
- func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (api *API) Stop()
- func (api *API) Unmarshal(data []byte, v interface{}) error
- type Config
- type Plugin
- type SafeWriter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Deregister ¶
func Deregister(pluginName string)
Deregister plgugin that registered by the provided name.
Types ¶
type API ¶
type API struct { Handler http.Handler Config Config Listener net.Listener Plugin Plugin LogStackTrace bool // Log stack trace when panic occur ReadTimeout time.Duration // for http.Server WriteTimeout time.Duration // for http.Server MaxHeaderBytes int // for http.Server // You change BeforeDispatch behavior that provided by plugin overriding this. // This will be set default func in NewAPI. It simply call Plugin.BeforeDispatch() BeforeDispatch func(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) // You change AfterDispatch behavior that provided by plugin overriding this. // This will be set default func in NewAPI. It simply call Plugin.AfterDispatch() AfterDispatch func(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) // You change OnPanic behavior that provided by plugin overriding this. // This will be set default func in NewAPI. It simply call Plugin.OnPanic() OnPanic func(w http.ResponseWriter, r *http.Request) // APIStatus write api status code. // It will be implemented by api.Plugin. // e.g. github.com/ToQoz/dou/jsonapi Use "X-API-Status" header. APIStatus func(w http.ResponseWriter, code int) }
API is the bone of dou. API adds a few triggers to http.Handler and provide a few useful helpers for creating api. Thanks of plugin system, API don't need to be responsible for many compatible content-type and api domain rule.
func (*API) Error ¶
func (api *API) Error(w http.ResponseWriter, resource interface{}, httpStatusCode int)
Error marshals and writes resource with http status code. Use this when you want to return error response. This is almost same as api.Ok except NAME(Ok, Error).
func (*API) Ok ¶
func (api *API) Ok(w http.ResponseWriter, resource interface{}, httpStatusCode int)
Ok marshals and writes resource with http status code. Use this when you want to return non-error response.
type Plugin ¶
type Plugin interface { OnPanic(w http.ResponseWriter, r *http.Request) BeforeDispatch(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) AfterDispatch(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) Marshal(v interface{}) ([]byte, error) Unmarshal(data []byte, v interface{}) error APIStatus(w http.ResponseWriter, code int) }
Plugin is interface for dou.API plugin. This plugin system make dou flexible and thin. You can create Plugin like a github.com/ToQoz/dou/jsonapi in accordance the use. see also github.com/ToQoz/dou/jsonapi
type SafeWriter ¶
type SafeWriter struct { Wrote bool http.ResponseWriter }
SafeWriter is safe http.ResponseWriter For prevent unintentionally multiple calling http.ResponseWriter.Write, this has bool `Worte`. When recovering panic, this is useful for prevent unintentionally writing to the continuation that was written before panic. Example: Write([]byte(`[]`)) ->
SomeFunc() -> (panic) -> OnPanic() -> Write(`{"message": "Internal server error"}`)
In ideal theory that I think, we have to prevent panic after calling Write. But no accident, no life :)
func NewSafeWriter ¶
func NewSafeWriter(w http.ResponseWriter) *SafeWriter
NewSafeWriter new SafeWriter by given http.ResponseWriter