dou: github.com/ToQoz/dou Index | Files | Directories

package dou

import "github.com/ToQoz/dou"

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

Package Files

doc.go dou.go

func Deregister Uses

func Deregister(pluginName string)

Deregister plgugin that registered by the provided name.

func Register Uses

func Register(pluginName string, plugin Plugin)

Register makes a database driver available by the provided name.

type API Uses

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 NewAPI Uses

func NewAPI(pluginName string) (*API, error)

NewAPI new and initialize API.

func (*API) Error Uses

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) Marshal Uses

func (api *API) Marshal(v interface{}) ([]byte, error)

Marshal encode v. Encoding procedure will be implemented by api.plugin

func (*API) Ok Uses

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.

func (*API) Run Uses

func (api *API) Run(l net.Listener)

Run api server. If fail to serve listener, output error and exit.

func (*API) ServeHTTP Uses

func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP calls

1. call BeforeDispatch()
2. call Router.ServeHTTP()
3. call AfterDispatch()

And call OnPanic when panic occur. if panic occur before calling API.AfterDispatch, this call it after recovering.

func (*API) Stop Uses

func (api *API) Stop()

Stop api server

func (*API) Unmarshal Uses

func (api *API) Unmarshal(data []byte, v interface{}) error

Unmarshal encode v. Decoding procedure will be implemented by api.plugin

type Config Uses

type Config map[string]interface{}

Config can store configuration map for API.

type Plugin Uses

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 Uses

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 Uses

func NewSafeWriter(w http.ResponseWriter) *SafeWriter

NewSafeWriter new SafeWriter by given http.ResponseWriter

func (*SafeWriter) Write Uses

func (sw *SafeWriter) Write(p []byte) (int, error)

Directories

PathSynopsis
jsonapi

Package dou imports 7 packages (graph) and is imported by 1 packages. Updated 2016-07-16. Refresh now. Tools for package owners.