golax

package module
v0.6.3 Latest Latest
Warning

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

Go to latest
Published: Feb 3, 2022 License: MIT Imports: 8 Imported by: 6

README

GoDoc

Golax is the official go implementation for the Lax framework.

Related docs:

About Lax

Lax wants to be the best "user experience" for developers making REST APIs.

The design principles for Lax are:

  • The lowest language overhead
  • Extremely fast to develop
  • Very easy to read and trace.

Getting started

my_api := golax.NewApi()

my_api.Root.
    Interceptor(golax.InterceptorError).
    Interceptor(myLogingInterceptor)

my_api.Root.Node("hello").
    Method("GET", func(c *golax.Context) {
        // At this point, Root interceptors has been already executed
        fmt.Fprintln(c.Response, "Hello world!")
    })

my_api.Serve()

Routing example

Routing is based on nodes.

There are three types: static, regex and parameter.

  • static: Only matches with the url part if it is exactly the same.
  • regex: Surrounded by ( and ), if the regex match.
  • parameter: Surrounded by { and }, always matches.

Performance

The performance compared with the most popular alternative is very similar (actually golax performs slightly better) however code readability and maintainability is far better with golax implementation.

Tests has been executed in a Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz.

Learn more about this https://github.com/fulldump/golax-performance.

How interceptor works

If I want to handle a GET /users/1234/stats request, all interceptors in nodes from <root> to .../stats are executed:

Normal flow

To abort the execution, call to c.Error(404, "Resource not found"):

Break flow

Handling parameters

my_api := golax.NewApi()

my_api.Root.
    Node("users").
    Node("{user_id}").
    Method("GET", func (c *golax.Context) {
        fmt.Fprintln(c.Response, "You are looking for user " + c.Parameter)
    })

my_api.Serve()

It is also possible get all parameters:

func (c *golax.Context) {
    fmt.Fprintln(c.Response, "All parameters:", c.Parameters)
}

Support for Google custom methods

According to Google's API design guidelines to map RPC services to REST HTTP, it describes custom methods as extra operations that can not be easyly mapped to HTTP verbs. More info about custom methods

For example, this URL has a custom method :activate:

https://my.service.com/v1/users/31231231231:activate

Golax support custom methods as operations:

my_api.Root.
    Node("v1").
    Node("users").
    Node("{user_id}").
    Operation("activate").
    Method("POST", func(c *golax.Context) {
        user_id := c.Parameters["{user_id}"]"
        fmt.Fprintln(c.Response, "Here is custom method ':activate' for user "+user_id)
    })

Sample use cases

TODO: put here some examples to cover cool things:

  • fluent implementation
  • node cycling
  • readability
  • node preference
  • sample logging interceptor
  • sample auth interceptor
  • sample api errors

Documentation

Index

Constants

This section is empty.

Variables

View Source
var InterceptorError = &Interceptor{
	Documentation: Doc{
		Name: "Error",
		Description: `
Print JSON error in this form:

´´´json
{
	"status_code": 404,
	"error_code": 21,
	"description_code": "User '231223' not found."
}
´´´
		`,
	},
	After: func(c *Context) {
		if nil != c.LastError {
			json.NewEncoder(c.Response).Encode(c.LastError)
		}
	},
}

InterceptorError prints an error in JSON format if Context.LastError is not nil. Example:

{
    "status_code": 404,
    "error_code": 1000023,
    "description_code": "User 'fulanez' not found.",
}
View Source
var InterceptorLog = &Interceptor{
	Documentation: Doc{
		Name: "Log",
		Description: `
Log all HTTP requests to stdout in this form:

´´´
2016/02/20 11:09:17 GET	/favicon.ico	404	59B
2016/02/20 11:09:34 GET	/service/v1/	405	68B
2016/02/20 11:09:46 GET	/service/v1/doc	405	68B
´´´
		`,
	},
	After: func(c *Context) {
		log.Printf(
			"%s\t%s\t%d\t%dB",
			c.Request.Method,
			c.Request.URL.RequestURI(),
			c.Response.StatusCode,
			c.Response.Length,
		)
	},
}

InterceptorLog prints an access log to standard output. Example: 2016/02/20 11:09:17 GET /favicon.ico 404 59B 2016/02/20 11:09:34 GET /service/v1/ 405 68B 2016/02/20 11:09:46 GET /service/v1/doc 405 68B

View Source
var InterceptorNoCache = &Interceptor{
	Documentation: Doc{
		Name: "InterceptorNoCache",
		Description: `
			Avoid caching via http headers
		`,
	},
	Before: func(c *Context) {
		add := c.Response.Header().Add

		add("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
		add("Pragma", "no-cache")
		add("Expires", "0")
	},
}

InterceptorNoCache set some headers to force response to not be cached by user agent.

Functions

func SplitTail added in v0.5.0

func SplitTail(s, sep string) []string

SplitTail split by separator and return pending tail and last part

Types

type Api

type Api struct {
	Root       *Node
	Prefix     string
	Handler405 Handler
	Handler404 Handler
	Handler500 Handler
}

Api is a complete API that implements http.Handler interface.

func NewApi

func NewApi() *Api

NewApi instances and initializes a new *Api.

func (*Api) Serve

func (a *Api) Serve()

Serve start a default server on address 0.0.0.0:8000

func (*Api) ServeHTTP

func (a *Api) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler interface. This code is ugly but... It works! This is a critical part for the performance, so it has to be written with love

type Context

type Context struct {
	Request      *http.Request
	Response     *ExtendedWriter
	Parameter    string
	Parameters   map[string]string
	LastError    *ContextError
	Scope        map[string]interface{}
	PathHandlers string
	// contains filtered or unexported fields
}

Context is a space to store information to be passed between interceptors and the final handler.

func NewContext

func NewContext() *Context

NewContext instances and initializes a Context

func (*Context) Error

func (c *Context) Error(s int, d string) *ContextError

func (*Context) Get

func (c *Context) Get(k string) (interface{}, bool)

Get retrieves a value from a Context

func (*Context) Set

func (c *Context) Set(k string, v interface{})

Set stores a key-value tuple inside a Context

type ContextError

type ContextError struct {
	StatusCode  int    `json:"status_code"`
	ErrorCode   int    `json:"error_code"`
	Description string `json:"description_code"`
}

ContextError is the error passed back when context.Error is called. It can be used inside an interceptor or a handler

type Doc added in v0.4.0

type Doc struct {
	Name        string
	Description string
	Ommit       bool
}

Doc represents documentation information that can be attached to a node, method or interceptor.

type ExtendedWriter added in v0.2.0

type ExtendedWriter struct {
	StatusCode int

	Length int
	http.ResponseWriter
	// contains filtered or unexported fields
}

ExtendedWriter wraps http.ResponseWriter with StatusCode & Length

func NewExtendedWriter added in v0.2.0

func NewExtendedWriter(w http.ResponseWriter) *ExtendedWriter

NewExtendedWriter instances a new *ExtendedWriter

func (*ExtendedWriter) Write added in v0.2.0

func (w *ExtendedWriter) Write(p []byte) (int, error)

Write replaces default behaviour of http.ResponseWriter

func (*ExtendedWriter) WriteHeader added in v0.2.0

func (w *ExtendedWriter) WriteHeader(statusCode int)

WriteHeader replaces default behaviour of http.ResponseWriter

type Handler

type Handler func(c *Context)

Handler is a function that implements an HTTP method for a Node, Operation, Interceptor.Before and Interceptor.After items.

type Interceptor

type Interceptor struct {
	Before        Handler
	After         Handler
	Documentation Doc
}

Interceptor are pieces of code attached to nodes that can interact with the context and break the execution. A interceptor has two pieces of code, `Before` is executed before the handler and `After` is executed after the handler. The `After` code is executed always if the `Before` code has been executed successfully (without calling to `context.Error(...)`.

type Node

type Node struct {
	Interceptors         []*Interceptor
	InterceptorsDeep     []*Interceptor
	Methods              map[string]Handler
	Children             []*Node
	Documentation        Doc
	DocumentationMethods map[string]Doc
	Operations           map[string]*Operation
	// contains filtered or unexported fields
}

Node represents a path part of an URL

func NewNode

func NewNode() *Node

NewNode instances and initializes a new node

func (*Node) Doc added in v0.4.0

func (n *Node) Doc(d Doc) *Node

Doc attaches documentation to a *Node

func (*Node) GetPath added in v0.5.0

func (n *Node) GetPath() string

GetPath retrieves a node path

func (*Node) Interceptor

func (n *Node) Interceptor(m *Interceptor) *Node

Interceptor attaches an *Interceptor to a *Node

func (*Node) InterceptorDeep added in v0.6.0

func (n *Node) InterceptorDeep(m *Interceptor) *Node

InterceptorDeep attaches an *Interceptor to a *Node but will be executed after all regular interceptors.

func (*Node) Method

func (n *Node) Method(m string, h Handler, d ...Doc) *Node

Method implements an HTTP method for a handler and optionally allows a third documentation parameter

func (*Node) Node

func (n *Node) Node(p string) *Node

Node appends a child node

func (*Node) Operation added in v0.5.0

func (n *Node) Operation(p string) *Operation

Operation appends an operation to a *Node

func (*Node) SetPath added in v0.5.0

func (n *Node) SetPath(p string)

SetPath modifies a node path

type Operation added in v0.5.0

type Operation struct {
	Path         string // Operation name
	Interceptors []*Interceptor
	Methods      map[string]Handler
}

Operation is a terminal node, ready to execute code but exposed as Google custom methods (with :operation syntax)

func NewOperation added in v0.5.0

func NewOperation() *Operation

NewOperation instances and initialize an Operation

func (*Operation) Interceptor added in v0.5.0

func (o *Operation) Interceptor(m *Interceptor) *Operation

Interceptor attaches an Interceptor to an operation

func (*Operation) Method added in v0.5.0

func (o *Operation) Method(m string, h Handler) *Operation

Method implement an HTTP method for an operation

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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