dou

package module
v0.0.0-...-35aabf2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 10, 2014 License: BSD-3-Clause Imports: 7 Imported by: 3

README

Dou(胴、道、銅)

Build Status Coverage Status GoDoc

Package dou is tiny and flexible toolkit for creating a api server. And you can creating a custom plugin in accordance with your api type or domain-specific use-case. This is NOT framework.

see http://godoc.org/github.com/ToQoz/dou

By the way

  • means that dou is not fullstack. But when cooperating with other package, it can become perfect. And it is easy to do.
  • means that dou provide minimum way by plugin system.
  • means that dou is not gold. So it is soft(flexible) because you can override behaviour provided by plugin.

Requirement

  • go1.1 or later

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

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.

func Register

func Register(pluginName string, plugin Plugin)

Register makes a database driver available 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 NewAPI

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

NewAPI new and initialize API.

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

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

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

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.

func (*API) Run

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

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

func (*API) ServeHTTP

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

func (api *API) Stop()

Stop api server

func (*API) Unmarshal

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

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

type Config

type Config map[string]interface{}

Config can store configuration map for API.

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

func (*SafeWriter) Write

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

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL