river

package module
v0.0.0-...-508f0eb Latest Latest
Warning

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

Go to latest
Published: Jul 28, 2016 License: Apache-2.0 Imports: 15 Imported by: 0

README

river

River is a simple and lightweight REST server.

Getting Started
rv := river.New()

Use middlewares

rv.Use(river.Logger()) 

Create endpoints

e := river.NewEndpoint(). 
    Get("/:id", func(c *river.Context){
        id := c.Param("id")
        ... // fetch data with id
        c.Render(200, data)
    }).
    Post("/", func(c *river.Context){
        ... // process c.Body and store in db
        c.Render(201, data)
    })
    ...

e.Use(MyMiddleware) // endpoint specific middleware

Handle endpoints

rv.Handle("/user", e) 

Run

rv.Run(":8080")

Check example code for more.

Approach
  • An endpoint is a REST endpoint with handlers for supported methods.
  • All endpoints are handled by a River instance.
  • Outputs are rendered via a preset or custom Renderer.
  • Middlewares and Renderers can be global or specific to an endpoint.
Request Flow

Basic flow

Request -> Middlewares -> Endpoint -> Renderer

Full flow

                    Request
                       |
                       |  
                     Router
                    /     \                  
                   /       \
                  /         \
              Found      Not Found / Method Not Allowed
                 \          /
                  \        /
                   \      /
              Global Middlewares
                   /      \
                  /        \
 Endpoint Middlewares    Not Found / Method Not Allowed Handler
        |                       |
        |                       |
     Endpoint                Renderer
        |
        |
     Renderer

Endpoint

Create

e := river.NewEndpoint()

Handle Requests

e.Get("/", handler).Post(...).Put(...) // method chaining
e.Handle(method, ...) // for custom request methods

River supports dependency injection. With that, any function can be an endpoint handler.

func () {...} // valid
func (c *river.Context) {...} // valid
func (c *river.Context, m MyStruct) {...} // valid
func (w http.ResponseWriter, r *http.Request) {...} // valid
func (w http.ResponseWriter, r *http.Request, m MyStruct) {...} // valid

JSON helper

func (c *river.Context){
    var users []User
    c.DecodeJSONBody(&users)
    ... // process users
}
Middleware

Any function that takes in the context can be used as a middleware.

type Middleware func(c *river.Context)

River comes with river.Recovery() for panic recovery.

rv.Use(Middleware) // global
e.Use(Middleware)  // endpoint

Middleware can choose to terminate request flow by not calling c.Next(). e.g. Authentication middleware.

func (c *river.Context){
    ... // do something before
    c.Next()
    ... // do something after
}

Any http.Handler can also be used as a middleware.

rv.UseHandler(handler)
Service Injection

Registering

var m MyStruct
...
rv.Register(m) // global
e.Register(m)  // endpoint

This will be passed as parameter to any endpoint handler that has MyStruct as a function parameter.

func handle(c *river.Context, m MyStruct) { ... }

Middlewares can also register request scoped service.

func AuthMiddleware(c *river.Context) {
    var session *Session
    ... // retrieve session
    c.Register(session)
}
Renderer

Renderer takes in data from endpoints and renders the data as response.

context.Render(...) renders using the configured Renderer. JSONRenderer is one of the available renderers.

Example Renderer, transform response to JSend format before sending as JSON.

func MyRenderer (c *river.Context, data interface{}) error {
    resp := river.M{"status" : "success", "data" : data}
    if _, ok := data.(error); ok {
        resp["status"] = "error"
        resp["message"] = data
        delete(resp, "data")
    }
    return JSONRenderer(c, resp)
}

Setting a Renderer. When an endpoint Renderer is not set, global Renderer is used.

rv.Renderer(MyRenderer) // global
e.Renderer(MyRenderer)  // endpoint
Custom server

River is an http.Handler. You can do without Run().

http.ListenAndServe(":8080", rv)
Router

River uses httprouter underneath.

Contributing
  • Create an issue to discuss.
  • Send in a PR.
Why the name "River", a "REST" server ? Can you REST on a River ?

Well, yes. You only need to know how to swim or wear a life jacket.

License

Apache 2

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// Log is the logger. This can be replaced or set to nil.
	Log = oslog.New(os.Stdout, "[River] ", 0)

	// LogRequests enables request log. Useful for development.
	LogRequests = true
)

Functions

func JSONRenderer

func JSONRenderer(c *Context, data interface{}) error

JSONRenderer is json renderer.

func PlainRenderer

func PlainRenderer(c *Context, data interface{}) error

PlainRenderer is plain text renderer.

Types

type Context

type Context struct {
	*http.Request
	// contains filtered or unexported fields
}

Context is a request scope context. Context implements http.ResponseWriter and embeds *http.Request.

It can be adapted for use in an http.Handler e.g.

handler.ServeHTTP(c, c.Request)

func (*Context) Deadline

func (c *Context) Deadline() (deadline time.Time, ok bool)

Deadline returns the time when work done on behalf of this context should be canceled. Deadline returns ok==false when no deadline is set. Successive calls to Deadline return the same results.

func (*Context) DecodeJSONBody

func (c *Context) DecodeJSONBody(v interface{}) error

DecodeJSONBody decodes the request body as JSON into v.

The request body must be JSON and v must be a pointer to a compatible type for the JSON body.

Type conversion is done if required; based on v's underlying type. If v points to a struct and request body is a json array, an attempt is made to decode to a slice of the struct and the first element of the slice will be stored in v.

Likewise if v points to a slice and request body is a json object, an attempt is made to decode to the item type of the slice and a slice containing the item will be stored in v.

var v []Type // c.DecodeJSONBody(&v) works even if body is a json object.
var v Type // c.DecodeJSONBody(&v) works even if body is a json array.

func (*Context) Done

func (c *Context) Done() <-chan struct{}

Done returns a channel that's closed when work done on behalf of this context should be canceled. Done may return nil if this context can never be canceled. Successive calls to Done return the same value.

func (*Context) Err

func (c *Context) Err() error

Err returns a non-nil error value after Done is closed. Err returns Canceled if the context was canceled or DeadlineExceeded if the context's deadline passed. No other values for Err are defined. After Done is closed, successive calls to Err return the same value.

func (*Context) Get

func (c *Context) Get(key string) interface{}

Get gets the value for key in the context. Key must have been previously set using c.Set.

func (*Context) Header

func (c *Context) Header() http.Header

Header returns the header map that will be sent by WriteHeader. Changing the header after a call to WriteHeader (or Write) has no effect unless the modified headers were declared as trailers by setting the "Trailer" header before the call to WriteHeader (see example). To suppress implicit response headers, set their value to nil.

func (*Context) Next

func (c *Context) Next()

Next calls the next handler in the middleware chain. A middleware must call Next, otherwise the request stops at the middleware.

Next has no effect if called in an endpoint handler.

func (*Context) Param

func (c *Context) Param(key string) string

Param returns URL parameters. If key is not found, empty string is returned.

Params are set with :key in the handle path. e.g. /:category/:id

func (*Context) Query

func (c *Context) Query(key string) string

Query returns URL query parameters. If key not found, empty string is returned.

func (*Context) Redirect

func (c *Context) Redirect(url string, code int)

Redirect performs HTTP redirect to url with code as redirect code. code must be 3xx, otherwise http.StatusFound (302) will be used.

func (*Context) Register

func (s *Context) Register(service interface{})

Register registers a new service. Services are identifiable by their types. Multiple services of same type should be grouped into a struct, and the struct should be registered instead.

func (*Context) Render

func (c *Context) Render(status int, data interface{})

Render renders data using the current endpoint's renderer (if any) or global renderer (if any) or PlainRenderer; in that preference order. status is HTTP status code to respond with.

func (*Context) RenderEmpty

func (c *Context) RenderEmpty(status int)

RenderEmpty renders status text for status as body. status is HTTP status code to respond with.

func (*Context) Set

func (c *Context) Set(key string, value interface{})

Set sets key in context to value.

func (*Context) Status

func (c *Context) Status() int

Status returns the response status code. This returns 0 unless response has been written.

func (*Context) Value

func (c *Context) Value(key interface{}) interface{}

Value returns the value associated with this context for key, or nil if no value is associated with key. Successive calls to Value with the same key returns the same result.

func (*Context) Write

func (c *Context) Write(b []byte) (int, error)

Write writes the data to the connection as part of an HTTP reply. If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) before writing the data. If the Header does not contain a Content-Type line, Write adds a Content-Type set to the result of passing the initial 512 bytes of written data to DetectContentType.

func (*Context) WriteHeader

func (c *Context) WriteHeader(status int)

WriteHeader sends an HTTP response header with status code. If WriteHeader is not called explicitly, the first call to Write will trigger an implicit WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly used to send error codes.

func (*Context) Written

func (c *Context) Written() int

Written returns the total number of bytes that have been written to the ResponseWriter.

type Endpoint

type Endpoint struct {
	// contains filtered or unexported fields
}

Endpoint is a REST endpoint.

func NewEndpoint

func NewEndpoint() *Endpoint

NewEndpoint creates a new Endpoint.

func (*Endpoint) Delete

func (e *Endpoint) Delete(p string, h Handler) *Endpoint

Delete sets the function for Delete requests.

func (*Endpoint) Get

func (e *Endpoint) Get(p string, h Handler) *Endpoint

Get sets the function for Get requests.

func (*Endpoint) Handle

func (e *Endpoint) Handle(requestMethod, p string, h Handler) *Endpoint

Handle sets the function for a custom requests.

func (*Endpoint) Options

func (e *Endpoint) Options(p string, h Handler) *Endpoint

Options sets the function for Options requests.

func (*Endpoint) Patch

func (e *Endpoint) Patch(p string, h Handler) *Endpoint

Patch sets the function for Patch requests.

func (*Endpoint) Post

func (e *Endpoint) Post(p string, h Handler) *Endpoint

Post sets the function for Post requests.

func (*Endpoint) Put

func (e *Endpoint) Put(p string, h Handler) *Endpoint

Put sets the function for Put requests.

func (*Endpoint) Register

func (s *Endpoint) Register(service interface{})

Register registers a new service. Services are identifiable by their types. Multiple services of same type should be grouped into a struct, and the struct should be registered instead.

func (*Endpoint) Renderer

func (e *Endpoint) Renderer(r Renderer) *Endpoint

Renderer sets the output renderer for endpoint.

func (*Endpoint) Use

func (c *Endpoint) Use(middlewares ...Middleware)

Use adds middlewares to the middleware chain.

func (*Endpoint) UseHandler

func (c *Endpoint) UseHandler(middlewares ...http.Handler)

UseHandler adds any http.Handler as middleware to the middleware chain.

type ErrHandler

type ErrHandler func(c *Context, err error)

ErrHandler handles error returned by Renderer.

type Handler

type Handler interface{}

Handler is an endpoint handler with support for dependency injection. Any function type (including http.Handler) is a valid Handler. Function parameters will be injected accordingly.

If a service is not previously registered and it is not one of *river.Context, http.ResponseWriter and *http.Request, zero value of the type (or nil if the type is a pointer) will be passed as the parameter.

If there is an attempt to register a non function type as a request handler, a panic occurs immediately. This prevents possible runtime panic.

The return values of the function (if any) are discarded.

type M

type M map[string]interface{}

M is a convenience wrapper for map[string]interface{}.

M{"status": "success, "data": M{"id": 1, "type": "complex"}}

type Middleware

type Middleware func(*Context)

Middleware is River middleware. A middleware needs to call c.Next() for request to continue to other middlewares.

func (c *river.Context){
  // do something before
  c.Next()
  // do something after
}

func Recovery

func Recovery(handlers ...func(c *Context, err interface{})) Middleware

Recovery creates a panic recovery middleware. handlers are called after recovery.

type Renderer

type Renderer func(c *Context, data interface{}) error

Renderer renders data in a specified format. Renderer should set Content-Type accordingly.

type River

type River struct {
	// contains filtered or unexported fields
}

River is a REST server handler and toolkit.

func New

func New(middlewares ...Middleware) *River

New creates a new River and initiates with middlewares. Middlewares can also be added with river.Use* methods.

Renderer defaults to JSONRenderer.

func (River) Dump

func (v River) Dump()

Dump dumps all endpoints that are being handled to the log.

func (*River) Handle

func (rv *River) Handle(p string, e *Endpoint) *River

Handle handles endpoint at path p. This should only be called after Endpoint requests have been handled.

func (*River) NotAllowed

func (rv *River) NotAllowed(h Handler) *River

NotAllowed replaces the default handler for methods not handled by any endpoint with h.

func (*River) NotFound

func (rv *River) NotFound(h Handler) *River

NotFound replaces the default handler for request paths without any endpoint.

func (*River) Register

func (s *River) Register(service interface{})

Register registers a new service. Services are identifiable by their types. Multiple services of same type should be grouped into a struct, and the struct should be registered instead.

func (*River) RenderError

func (rv *River) RenderError(h ErrHandler) *River

RenderError sets the handler that handles error returned by a Renderer.

func (*River) Renderer

func (rv *River) Renderer(r Renderer) *River

Renderer sets output renderer. An endpoint renderer overrules this.

func (*River) Run

func (rv *River) Run(addr string) error

Run starts River as an http server.

func (*River) ServeHTTP

func (rv *River) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*River) Use

func (c *River) Use(middlewares ...Middleware)

Use adds middlewares to the middleware chain.

func (*River) UseHandler

func (c *River) UseHandler(middlewares ...http.Handler)

UseHandler adds any http.Handler as middleware to the middleware chain.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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