ctxrouter

package module
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2018 License: Apache-2.0 Imports: 9 Imported by: 0

README

A HTTP request router with Context

Go Report Card

Features

  • Context Append on Current Function
  • Best Performance (no regexp match)
  • Wildcards Router Support (PathPrefix)
  • Decode request body before business layer (JSON, xml or other)
  • Auto reflect url params to numbers
  • Zero Garbage

Examples

Hello Word

func main() {
	r := ctxrouter.New()
	r.Get("/hello.html", (*Context).HelloHtml)
	r.Get("/hello/{name}/id/{age}", (*Context).Hello)
	r.Get("/hello/{name}/error", (*Context).HelloHtml)
	http.ListenAndServe(":8081", r)
}

func (ctx *Context) HelloHtml() {
	ctx.Writer.Write([]byte("<html>hello</html>"))
}

func (ctx *Context) Hello(name string, id int) (interface{}, error){
	return map[string]interface{}{"name":name, "id":id},nil
}

func (ctx *Context) HelloError(name string) (interface{}, error){
	return nil, errors.CodeError(errors.InvalidArgument).WithDescription("some error")
}

type Context struct {
	ctxrouter.Context
}

Basic Example

package main

import (
	"github.com/ti/ctxrouter"
	"net/http"
	"strconv"
)

func main() {
	r := ctxrouter.New()
	r.Get("/resp/{msg}", (*Context).Resp)
	r.Get("/basic/{name}", (*Context).Hello)
	r.Get("/normal/{name}", NormalHello)
	r.Get("/func/{name}/{id}",Hello)
	r.Get("/", (*Context).Index)
	//auto decode url with string or int
	r.Get("/basic/{name}/json/{age}", (*Context).Json)
	//match path prefixes /all/*:
	r.All("/basic/{path=**}",(*Context).All)
	//a simple func without implement ctxrouter.Context
	http.ListenAndServe(":8081", r)
}

//context common style
func (ctx *Context) Resp(msg string) (interface{}, error){
	if msg == "error" {
		return nil,errors.CodeError(errors.InvalidArgument).WithDescription"hello error")
	}
	return map[string]bool{"success":true},nil
}

//context style
func (ctx *Context) Hello(id string) {
	//ctx.Request ...
	ctx.Writer.Write([]byte("hello " + id))
}
//normal style
func NormalHello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("hello " + ctxrouter.Params(r)[0]))
}
//func style
func Hello(ctx *ctxrouter.Context, name string,  id int) {
	ctx.Text("hello " + name + ", id is " + strconv.Itoa(id))
}

type Context struct {
	ctxrouter.Context
}

func (c *Context) Index() {
	c.Text("index")
}

func (c *Context) All(path string) {
	c.Text("all router goes here " +  path)
}

func (c *Context) Json(name string, age int) {
	type Person struct {
		Name string
		Age   int
	}
	c.JSON(Person{Name:name,Age:age})
}

Custom Error

func main() {
	r := ctxrouter.New()
	r.Get("/resp1", (*Context).RespError)
	r.Get("/resp2", (*Context).RespErrorDefault)
	http.ListenAndServe(":8081", r)
}

type Error struct {
	Code  int `json:"code"`
	Error   string `json:"error"`
}

func (this *Error) StatusCode() int{
	return 400
}

func (this *Error) IsNil() bool {
	return this.Code == 0
}

//Use Custom error
func (ctx *Context) RespError() (interface{}, *Error){
	return nil,&Error{Code:3000, Error:"some error message"}
}


//use default error
func (ctx *Context) RespErrorDefault() (interface{}, error){
	return nil,errors.CodeError(errors.InvalidArgument).WithDescription"hello error")
}

type Context struct {
	ctxrouter.Context
}

With Powerful Context

//do something  Workflow with ctx router
package main

import (
	"net/http"
	"github.com/ti/ctxrouter"
)

type Context struct {
	ctxrouter.Context
	Data  map[string]string
}

func (c *Context) Start() {
	c.Data = make(map[string]string)
	c.Data["context"] = "0"
	c.Step()
}

func (c *Context) Step() {
	c.Data["context1"] = "1"
	c.End()
}

func (c *Context) End() {
	c.Data["context2"] = "2"
	c.JSON(c.Data)
}

func main() {
	r := ctxrouter.New()
	r.Get("/context",(*Context).Start)
	http.ListenAndServe(":8081", r)
}

Decode Request Before Business Layer

package main

import (
	"net/http"
	"github.com/ti/ctxrouter"
)

//decode request sample
type User struct {
	Id      int             `json:"int"`
	Name    string          `json:"name"`
}

type UserContext struct {
	ctxrouter.Context
	Data  *User
}

//Auto Decode Json or other request
func (ctx *UserContext) DecodeRequest() error {
	ctx.Data = new(User)
	ctx.Context.Data = ctx.Data
	return ctx.Context.DecodeRequest()
}

func (ctx *UserContext) SayHello() {
	ctx.Text("Hello "+ ctx.Data.Name)
}

func main() {
	r := ctxrouter.New()
	r.Post("/users/hello",(*UserContext).SayHello)
	http.ListenAndServe(":8081", r)
}
curl -i -X POST \
   -H "Content-Type:application/json" \
   -d \
'{"name":"leenanxi"}' \
 'http://localhost:8081/users/hello'

Normal HTTP Handler

Alert: This is Not recommended if you start a new project.

package main

import (
	"github.com/ti/ctxrouter"
	"net/http"
)

func NormalHelloHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("HELLO"))
}

func NormalHandler(w http.ResponseWriter, r *http.Request) {
	params := ctxrouter.Params(r)
	w.Write([]byte("Name:" + params[0] + "\nAge:" + params[1] ))
}

func main() {
	r := ctxrouter.New()
	r.Get("/normal/hello",NormalHelloHandler)
	r.Get("/normal/v1/:name/:age",NormalHandler)
	//support any http.Handler interface
	r.Get("/404",http.NotFoundHandler())
	http.ListenAndServe(":8081", r)
}

How Middleware X?

The router is http.Handler, so you can chain any http.Handler compatible middleware before the router, for example http://www.gorillatoolkit.org/pkg/handlers

package main

import (
	"github.com/ti/ctxrouter"
	"github.com/gorilla/handlers"
	"os"
	"net/http"
)

//context style
func (ctx *Context) Hello(name string) {
	ctx.Text("hello " + name)
}

func main() {
	r := ctxrouter.New()
	r.Get("/hello/:name", (*Context).Hello)
	http.ListenAndServe(":8081", (handlers.LoggingHandler(os.Stdout, r)))
}

type Context struct {
	ctxrouter.Context
}

Static Files

package main

import (
	"github.com/ti/ctxrouter"
	"net/http"
)

func main() {
	var dir = "/your/static/dir/path"
	r := ctxrouter.New()
	r.All("/static/*path",http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
	http.ListenAndServe(":8081", r)
}

Restful Api

package main

import (
	"net/http"
	"github.com/ti/ctxrouter"
)
func main() {
	r := ctxrouter.New()
	r.Get("/apps", (*AppContext).GetApps)
	r.Get("/apps/{id}", (*AppContext).GetApp)
	r.Post("/apps", (*AppContext).PostApps)
	r.Patch("/apps/{id}", (*AppContext).PatchApp)
	r.Put("/apps/{id}", (*AppContext).PutApp)
	r.Delete("/apps/{id}", (*AppContext).DeleteApp)
	http.ListenAndServe(":8081", r)
}
type AppContext struct {
	ctxrouter.Context
}
func (ctx *AppContext) GetApps() {
	ctx.Text("get apps")
}
func (ctx *AppContext) GetApp(id string) {
	ctx.Text("get app " + id)
}
func (ctx *AppContext) PostApps() {
	ctx.Text("post apps")
}
func (ctx *AppContext) DeleteApp(id string) {
	ctx.Text("delete app " + id)
}
func (ctx *AppContext) PutApp(id string) {
	ctx.Text("put app " + id)
}
func (ctx *AppContext) PatchApp(id string) {
	ctx.Text("patch app " + id)
}

Full Example

//full example with all features in one file, you can read sections above
package main

import (
	"net/http"
	"fmt"
	"github.com/ti/ctxrouter"
)

func main() {
	r := ctxrouter.New()
	r.Get("/", (*Controller).Index)
	r.Get("/basic/{name}", (*Controller).Hello)
	//match path prefixes /all/*:
	r.All("/basic/{path=**}",(*Controller).All)
	//auto decode url with string or int
	r.Get("/basic/{name}/json/:age", (*Controller).Json)
	//a simple func without implement ctxrouter.Context
	r.Get("/basic/{name}/simple",Simple)

	r.Post("/users/hello",(*UserContext).PrintHello)

	//do something  Workflow with ctx router
	r.Get("/context/",(*Context).Start)


	r.Get("/normal/hello",NormalHelloHandler)
	r.Get("/normal/v1/{name}/{age}",NormalHandler)
	//support any http.Handler interface
	r.Get("/404",http.NotFoundHandler())

	//static files
	var dir = "/your/static/dir/path"
	r.All("/static/{path=*}",http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
	http.ListenAndServe(":8081", r)
}

type Controller struct {
	ctxrouter.Context
}

func (c *Controller) Index() {
	c.Text("index")
}

func (c *Controller) Hello(name string) {
	fmt.Fprintln(c.Writer, "hello "+name)
}

func (c *Controller) All(path string) {
	c.Text("all router goes here " +  path)
}
//input json and output json
func (c *Controller) Json(name string, age int) {
	type Person struct {
		Name string
		Age   int
	}
	c.JSON(Person{Name:name,Age:age})
}

func Simple(ctx *ctxrouter.Context, name string) {
	ctx.Text("simple " + name)
}

//decode request sample
type User struct {
	Id      int             `json:"int"`
	Name    string          `json:"name"`
}

type UserContext struct {
	ctxrouter.Context
	Data  *User
}

//Auto Decode Json or other request
func (ctx *UserContext) DecodeRequest() error{
	ctx.Data = new(User)
	ctx.Context.Data = ctx.Data
	return ctx.Context.DecodeRequest()
}

func (ctx *UserContext) PrintHello() {
	ctx.Text("Hello "+ ctx.Data.Name)
}

type Context struct {
	ctxrouter.Context
	Data  map[string]string
}

func (c *Context) Start() {
	c.Data = make(map[string]string)
	c.Data["context"] = "0"
	c.Step()
}

func (c *Context) Step() {
	c.Data["context1"] = "1"
	c.End()
}

func (c *Context) End() {
	c.Data["context2"] = "2"
	c.JSON(c.Data)
}

func NormalHelloHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("HELLO"))
}

func NormalHandler(w http.ResponseWriter, r *http.Request) {
	//get router Params from "X-Ctxrouter-Params" without any extra function
	params := ctxrouter.Params(r)
	w.Write([]byte("Name:" + params[0] + "\nAge:" + params[1] ))
}

Thanks

  • route matching algorithm is by google

Documentation

Index

Constants

View Source
const (
	// OpNop does nothing
	OpNop = OpCode(iota)
	// OpPush pushes a component to stack
	OpPush
	// OpLitPush pushes a component to stack if it matches to the literal
	OpLitPush
	// OpPushM concatenates the remaining components and pushes it to stack
	OpPushM
	// OpConcatN pops N items from stack, concatenates them and pushes it back to stack
	OpConcatN
	// OpCapture pops an item and binds it to the variable
	OpCapture
	// OpEnd is the least positive invalid opcode.
	OpEnd
)

These constants are the valid values of OpCode.

Variables

View Source
var (
	// ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
	ErrNotMatch = errors.New("not match to the path pattern")
	// ErrInvalidPattern indicates that the given definition of Pattern is not valid.
	ErrInvalidPattern = errors.New("invalid pattern")
)

Functions

func JSONResponse

func JSONResponse(w http.ResponseWriter, data interface{})

JSONResponse response json to any http writer if data is a error it will response a errror json

func JSONResponseVerbose

func JSONResponseVerbose(w http.ResponseWriter, status int, header http.Header, data interface{})

JSONResponseVerbose response json to any http writer, include status ,header, data

func Params

func Params(req *http.Request) []string

Params get params form request (It is faster than most other function, because there is no extra compute ) req http.Request

Types

type Compiler

type Compiler interface {
	Compile() Template
}

Compiler compiles utilities representation of path templates into marshallable operations. They can be unmarshalled by runtime.NewPattern.

func Parse

func Parse(tmpl string) (Compiler, error)

Parse parses the string representation of path template

type Context

type Context struct {
	Writer  http.ResponseWriter
	Request *http.Request
	Data    interface{}
}

Context the context of http you can use by startContext, nextContext, procContext, endContext ....

func (*Context) DecodeJSON

func (c *Context) DecodeJSON(data interface{}) error

DecodeJSON decode json

func (*Context) DecodeRequest

func (c *Context) DecodeRequest() error

DecodeRequest You can implement your DecodeRequest, it can be form or something else

func (*Context) Init

func (c *Context) Init(w http.ResponseWriter, r *http.Request)

Init the start of context you can define you own link (c *context){c.Context.Init(w,r), your code ...}

func (*Context) JSON

func (c *Context) JSON(data interface{})

JSON response json

func (*Context) Redirect

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

Redirect http 302 to url

func (*Context) Status

func (c *Context) Status(status int)

Status set response status code

func (*Context) StatusError

func (c *Context) StatusError(status int, errorDescription string)

StatusError output standard error json body by http.Status code exp: StatusError(404,"not fond something"),will response {"error":"not_found", "error_description":"not fond something"}

func (*Context) StatusText

func (c *Context) StatusText(status int)

StatusText response textplain by http.Status code

func (*Context) Text

func (c *Context) Text(data string)

Text response textplain

type ContextInterface

type ContextInterface interface {
	Init(http.ResponseWriter, *http.Request)
	DecodeRequest() error
}

ContextInterface the interface of any context the context must have Init and DecodeRequest

type Error

type Error interface {
	StatusCode() int
	Error() string
	IsNil() bool
}

Error You can custom any error structure you want

type Handler

type Handler struct {
	Pat Pattern
	V   interface{}
	// contains filtered or unexported fields
}

Handler the handler instance in router

type InvalidTemplateError

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

InvalidTemplateError indicates that the path template is not valid.

func (InvalidTemplateError) Error

func (e InvalidTemplateError) Error() string

type OpCode

type OpCode int

An OpCode is a opcode of compiled path patterns.

type Pattern

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

Pattern is a template pattern of http request paths defined in github.com/googleapis/googleapis/google/api/http.proto.

func MustPattern

func MustPattern(p Pattern, err error) Pattern

MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.

func NewPattern

func NewPattern(ops []int, pool []string, verb string) (Pattern, error)

NewPattern returns a new Pattern from the given definition values. "ops" is a sequence of op codes. "pool" is a constant pool. "verb" is the verb part of the pattern. It is empty if the pattern does not have the part. "version" must be 1 for now. It returns an error if the given definition is invalid.

func ParsePatternURL

func ParsePatternURL(path string) (pattern Pattern, err error)

ParsePatternURL parse any path to google pattern

func (Pattern) Match

func (p Pattern) Match(components []string, verb string) (map[string]string, []string, error)

Match examines components if it matches to the Pattern. If it matches, the function returns a mapping from field paths to their captured values. If otherwise, the function returns an error.

func (Pattern) Reduction

func (p Pattern) Reduction(pathParams map[string]string) string

Reduction reduction the path by path Params

func (Pattern) String

func (p Pattern) String() string

func (Pattern) Verb

func (p Pattern) Verb() string

Verb returns the verb part of the Pattern.

type Router

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

Router the router

func New

func New() *Router

New new router

func (*Router) All

func (r *Router) All(path string, controller interface{})

All http all method

func (*Router) Delete

func (r *Router) Delete(path string, controller interface{})

Delete http Delete method

func (*Router) Get

func (r *Router) Get(path string, controller interface{})

Get http Get method

func (*Router) Handle

func (s *Router) Handle(method, path string, v interface{}) error

Handle handler path in router

func (*Router) Head

func (r *Router) Head(path string, controller interface{})

Head http Head method

func (*Router) Match

func (s *Router) Match(method string, path string) (h Handler, pathParams map[string]string, paramsList []string, err error)

Match dispatches the request to the first handler whose pattern matches to r.Method and r.Path.

func (*Router) Options

func (r *Router) Options(path string, controller interface{})

Options http Options method

func (*Router) Patch

func (r *Router) Patch(path string, controller interface{})

Patch http Patch method

func (*Router) Post

func (r *Router) Post(path string, controller interface{})

Post http Post method

func (*Router) Put

func (r *Router) Put(path string, controller interface{})

Put http Put method

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP just used by system http handler

type Template

type Template struct {
	// OpCodes is a sequence of operations.
	OpCodes []int
	// Pool is a constant pool
	Pool []string
	// Verb is a VERB part in the template.
	Verb string
	// Fields is a list of field paths bound in this template.
	Fields []string
	// Original template (example: /v1/a_bit_of_everything)
	Template string
}

Template is a compiled representation of path templates.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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