pong

package module
v0.0.0-...-faba33d Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2016 License: MIT Imports: 12 Imported by: 0

README

pong

Build Status Coverage Status GoDoc

A router for high performance web service write in golang.

Introduction

Pong is just a http router library, use it make high performance web service in minutes. It's work is to route a request to register handle then provide convenient function to get param from request and send response and also provide option HTTP session support. Pong process every request as a product in production line, use register middleware do some change to the product.This like the way in NodeJs's famous Express do. It has no dependency small and clear, support route conflict tips.

Performance

Hello World

    package main
    import (
    	"github.com/gwuhaolin/pong"
    	"net/http"
    )
    func main() {
    	po := pong.New()
        root := po.Root
    	// visit http://127.0.0.1:3000/ping will see string "pong"
    	root.Get("/ping", func(c *pong.Context) {
    		c.Response.String("pong")
    	})

    	// a sub router
    	sub := root.Router("/sub")

    	// visit http://127.0.0.1:3000/sub/pong will see JSON "{"name":"pong"}"
    	sub.Get("/:name", func(c *pong.Context) {
    		m := map[string]string{
    			"name":c.Request.Param("name"),
    		}
    		c.Response.JSON(m)
    	})

    	// Run Server Listen on 127.0.0.1:3000
    	http.ListenAndServe(":3000", po)
    }

Installation

    go get github.com/gwuhaolin/pong

Principle

Listen and Server

pong not provide Listen and Server, it just do thing about route and handle, so you can should standard lib's function

HTTPS
    po := pong.New()

	// visit https://127.0.0.1:3000/hi will see string "hi"
	root.Get("/hi", func(c *pong.Context) {
		c.Response.String("hi")
	})

	http.ListenAndServeTLS(":433", "cert.pem", "key.pem", nil)
HTTP2
    po := pong.New()

	// visit http://127.0.0.1:3000/hi will see string "hi"
	root.Get("/hi", func(c *pong.Context) {
		c.Response.String("hi")
	})

	server := &http.Server{
		Handler:po,
		Addr:":3000",
	}
	http2.ConfigureServer(server, &http2.Server{})
	server.ListenAndServe()
Multi-Server
    po0 := pong.New()
	po1 := pong.New()

	// visit http://127.0.0.1:3000/hi will see string "0"
	po0.Root.Get("/hi", func(c *pong.Context) {
		c.Response.String("0")
	})

	// visit http://127.0.0.1:3001/hi will see string "1"
	po1.Root.Get("/hi", func(c *pong.Context) {
		c.Response.String("1")
	})
	go func() {
		http.ListenAndServe(":3000", po0)
	}()
	http.ListenAndServe(":3001", po1)

Route

Route every request to the right handle is pong's job. Pong will build a tree in type map when you register your handle to a path, when server has run and request come in, pong will use map's hash algorithm to find a register handle. Pong's router not support regular expression because infrequency and avoid it can improve performance Pong support sub Router, a route like a tree which is comprise by one or more sub Router Pong's Root Router can access by pong.Root which point to root path /

HTTP Methods

after route a request to path, pong can also route diff HTTP method. This Delete Get Head Options Patch Post Put Trace Any are support.

    root := po.Root
    root.Delete("/", func(c *Context) {
		c.Response.String("Delete")
	})
    root.Put("/", func(c *Context) {
		c.Response.String("Put")
	})
    root.Any("/", func(c *Context) {
		c.Response.String("Any will overwrite all of them because is registed last, this means overwrite by registed order")
	})
Sub Router
	// visit / will see string "/"
    root.Get("/", func(c *Context) {
		c.Response.String("/")
	})
	sub := root.Router("sub")
	// visit /sub/hi will see string "sub"
	sub.Get("/hi", func(c *Context) {
        c.Response.String("sub")
    })
    sub2 := sub.Router("sub")
	// visit /sub/sub2/hi will see string "sub2"
	sub2.Get("/hi", func(c *Context) {
        c.Response.String("sub2")
    })
Path Param
	// visit /abc will see string "abc"
    root.Get("/:param", func(c *Context) {
		c.Response.String(c.Request.Param("param"))
	})
	// param in router path
	sub := root.Router("sub/:name")
	// visit /sub/abc/hi will see string "abc"
	sub.Get("/hi", func(c *Context) {
        c.Response.String(c.Request.Param("param"))
    })
Route Conflict Tips

see Route Conflict this code:

	root.Get("/path", func(c *Context) {
		c.Response.String("path")
	})
	root.Get("/:name", func(c *Context) {
		c.Response.String(c.Request.Param("name"))
	})

:name march /path, when this happen pong will print warning to tell developer fix Conflict. But this code can still run, pong has rule you must know: path's(/path) priority level is high than param's(/:name), so for this code when you:

  • visit /path will see string path, which use handle set in root.Get("/path",handle)
  • visit /hal will see string hal, which use handle set in root.Get("/:name",handle)

Request

Query Param
	// visit /?param=abc will see string "abc"
    root.Get("/", func(c *Context) {
		c.Response.String(c.Request.Query("param"))
	})
Form Param
	// post / with body "param=abc" will see string "abc"
    root.Post("", func(c *Context) {
		c.Response.String(c.Request.Form("param"))
	})
Post File
    // post / with a text file will see file's context
    root.Post("/", func(c *Context) {
		file, _, _ := c.Request.File("file")
		bs, _ := ioutil.ReadAll(file)
		c.Response.String(string(bs))
	})

Bind

Pong provide convenient way to parse request's params and bind to a struct

BindJSON

parse request's body data as JSON and use standard lib json.Unmarshal to bind data to struct

    type testUser struct {
    	Name  string `json:"name"`
    	Age   int `json:"age"`
    }
    // post / with a json string will see json again
    root.Post("/", func(c *Context) {
		user := testUser{}
		c.Request.BindJSON(&bindUser)
		c.Response.JSON(user)
	})
BindXML

parse request's body data as XML and use standard lib XML.Unmarshal to bind data to struct

    // post / with a xml string will see xml again
    root.Post("/", func(c *Context) {
		user := testUser{}
		c.Request.BindXML(&bindUser)
		c.Response.XML(user)
	})
BindForm

parse request's body post form as map and bind data to struct use filed name

    // post / with a name=hal&age=23 will see json "{"name":"hal","age":23}"
    root.Post("/", func(c *Context) {
		user := testUser{}
		c.Request.BindXML(&bindUser)
		c.Response.JSON(user)
	})
BindQuery

parse request's query params as map and bind data to struct use filed name

    // visit /?name=hal&age=23 will see json "{"name":"hal","age":23}"
    root.Post("/", func(c *Context) {
		user := testUser{}
		c.Request.BindQuery(&bindUser)
		c.Response.JSON(user)
	})
AutoBind

auto bind will look request's http Header ContentType

  • if request ContentType is applicationJSON will use BindJSON to parse
  • if request ContentType is applicationXML will use BindXML to parse
  • if request ContentType is applicationForm or multipartForm will use BindForm to parse
  • else will return an ErrorTypeNotSupport error
    // post / with a json "{"name":"hal","age":23}" will see "{"name":"hal","age":23}"
    // post / with a name=hal&age=23 will see json "{"name":"hal","age":23}"
    // visit /?name=hal&age=23 will see json "{"name":"hal","age":23}"
    // post / with a xml will see "{"name":"hal","age":23}"
    root.Post("/", func(c *Context) {
		user := testUser{}
		c.Request.AutoBind(&bindUser)
		c.Response.JSON(user)
	})

Response

Set Header

write a HTTP Header to response use before response has send to client

    c.Response.Header("X-name", "mine header")

write a HTTP Cookie to response

    c.Response.Cookie(&http.Cookie{Name: "id", Value: "123"})
Send JSON

send JSON response to client, parse data by standard lib's json.Marshal and then send to client

Send JSONP

parse data by standard lib's json.Marshal and then send to client will wrap json to JavaScript's call method with give callback param

    // visit /hi will see json "callback({"name":"hal","age":23})"
    root.Get("/hi", func(c *Context) {
        user := testUser{
                Name:"hal",
                Age:23,
        }
		c.Response.JSONP(user,"callback")
	})
Send XML

parse data by standard lib's xml.Marshal and then send to client

Send File

send a file response to client

    // visit /hi will see file hi.zip
    root.Get("/hi", func(c *Context) {
		c.Response.File("hi.zip")
	})
Send String

send String response to client

Redirect

Redirect replies to the request with a redirect to url, which may be a path relative to the request path.

    // visit /redirect will redirect to /
    root.Get("/redirect", func(c *Context) {
		c.Response.Redirect("/")
	})
Render HTML Template

send HTML response to client by render HTML template with give data, LoadTemplateGlob before use Render

    po.LoadTemplateGlob("*.html")
    // visit /index will see index.html template render by data
    root.Get("/:name", func(c *Context) {

    		c.Response.Render(name, dataToRender)
    })

Middleware

pong's Middleware is a Handle Function which define as func(*Context), in handle function you can use Context for a request to do what Context provide.

Router Middleware

pong's route tree is build by many sub router, pong.Root is the root router every request will go through. A router can set a list Middleware and every request go through this router will handle by register Middleware list in order.

    root := po.Root
    // a Router Middleware to log every request
    root.Middleware(func(c *Context) {
    		req := c.Request.HTTPRequest
    		fmt.Println(req.Method, req.Host, req.RequestURI)
    })
Tail Middleware

pong can set a list of Tail Middleware which will be execute before response data to client after all of the other middleware register in router has execute.

    // a Tail Middleware to log every response
    po.TailMiddleware(func(c *Context) {
            res := c.Response
            fmt.Println(req.StatusCode, req.Header())
    })

Config

Handle 404 not find

when pong's router can't find a handle to request' URL, pong will use NotFindHandle to handle this request which will send response with code 404, and string 'page not find'. You can define your handle to rewrite NotFindHandle, for example:

    // when 404 not find happen, will Redirect user to page /404.html
    po.NotFindHandle = func(c *Context) {
            c.Response.Redirect("/404.html")
    }
Error Handle

when send response to client cause error happen, pong will use HTTPErrorHandle to handle this request, default is response with code 500, and string inter server error. You can define your handle to rewrite HTTPErrorHandle, for example:

    // when 500 error happen, will Redirect user to page /500.html
    po.HTTPErrorHandle = func(err error, c *Context) {
            fmt.Errorf("%v",err)
            c.Response.Redirect("/500.html")
    }

Session

Pong provide session support, you can store data to memory or Redis. Also can write your session manager work with pong.

Set and Get
    // make a in memory session manager
    sessionManager := memory_session.New()
    // tell pong to enable session, and store data use in memory session manager
	po.EnableSession(sessionManager)
    // set a value with name to this session
    root.Get("/a", func(c *Context) {
    		c.Session.Set("keyName","value")
    })
    // get the value by name from this session
    root.Get("/a", func(c *Context) {
    		c.Session.Get("keyName")
    })
Reset Session

update old sessionId with new one, this will update sessionId store in browser's cookie and session manager's store

    // set a value with name to this session
    root.Get("/a", func(c *Context) {
    		c.ResetSession()
    })
Destory Session

remove sessionId store in browser's cookie and session manager's store

    // set a value with name to this session
    root.Get("/a", func(c *Context) {
    		c.DestorySession()
    })
Store Session In Redis
    // make a in memory session manager
    sessionManager := redis_session.New()
    // tell pong to enable session, and store data use in memory session manager
	po.EnableSession(sessionManager)
Write Your Session Manager

to write your session manager work with pong, you should implement the interface SessionIO which pong how to read and write session data. SessionIO define this methods:

  • NewSession() (sessionId string) : NewSession should generate a sessionId which is unique compare to existent,and return this sessionId,this sessionId string will store in browser by cookies,so the sessionId string should compatible with cookies value rule
  • Destory(sessionId string) error : Destory should do operation to remove an session's data in store by give sessionId
  • Reset(oldSessionId string) (newSessionId string, err error) : Reset should update the give old sessionId to a new id,but the value should be the same
  • Has(sessionId string) bool : return whether this sessionId is existent in store
  • Read(sessionId string) (wholeValue map[string]interface{}) : read the whole value point to the give sessionId
  • Write(sessionId string, changes map[string]interface{}) error : update the sessionId's value to store, the give value just has changed part not all of the value point to sessionId

See pong's build in session manager who implement interface SessionIO for example to learn how to write your session manager':

LICENSE

Copyright (c) 2016 吴浩麟, The MIT License (MIT)

Documentation

Overview

pong is a simple HTTP router for go.

Example:

package main

import (
	"github.com/gwuhaolin/pong"
	"net/http"
	"log"
)

func main() {
	po := pong.New()

	// visitor http://127.0.0.1:3000/ping will see string "pong"
	po.Root.Get("/ping", func(c *pong.Context) {
		c.Response.String("pong")
	})

	// a sub router
	sub := po.Root.Router("/sub")

	// visitor http://127.0.0.1:3000/sub/pong will see JSON "{"name":"pong"}"
	sub.Get("/:name", func(c *pong.Context) {
		m := map[string]string{
			"name":c.Request.Param("name"),
		}
		c.Response.JSON(m)
	})

	// Run Server Listen on 127.0.0.1:3000
	log.Println(http.ListenAndServe(":3000", po))
}

Learn more at https://github.com/gwuhaolin/pong

Index

Constants

This section is empty.

Variables

View Source
var (
	// SessionId's Cookies name store in browser
	SessionCookiesName = "SESSIONID"
	// this error will be return when use bind in request when bind data to struct fail
	ErrorTypeNotSupport = errors.New("type not support")
)

Functions

This section is empty.

Types

type Context

type Context struct {

	// HTTP Session
	Session *Session
	// HTTP Request,used to get params like query post-form post-file...
	Request *Request
	// HTTP Response,used to send response to client.Can send JSON XML string file...
	Response *Response
	// contains filtered or unexported fields
}

Context represents context for the current request. It holds request and response objects, path parameters, data and registered handler. Context is handle by middleware list in order

func (*Context) DestorySession

func (c *Context) DestorySession() error

remove sessionId this will remove sessionId store in browser's cookie and session manager's store

func (*Context) Get

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

get a value which is set by Context.Set() method. if the give name is not store a nil will return

func (*Context) ResetSession

func (c *Context) ResetSession() error

update old sessionId with new one this will update sessionId store in browser's cookie and session manager's store

func (*Context) Set

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

set a value to this context in a handle,and in next handle you can read the value by Context.Get() the data is store with type map[string]interface{} in memory,so set a same can overwrite old value

type HandleFunc

type HandleFunc func(*Context)

HandleFunc is a handle in Middleware list, like a machine production line to do some change used to read something from request and store by Context.Request make a response to client by Context.Response

type Pong

type Pong struct {

	// Root router to path /
	Root *Router
	// 404 not find handle
	// when pong's router can't find a handle to request' URL,pong will use NotFindHandle to handle this request
	// default is response with code 404, and string page not find
	NotFindHandle HandleFunc
	// when send response to client cause error happen, pong will use HTTPErrorHandle to handle this request
	// default is response with code 500, and string inter server error
	HTTPErrorHandle func(error, *Context)
	// contains filtered or unexported fields
}

func New

func New() *Pong

make a pong instance and return is pointer.

func (*Pong) EnableSession

func (pong *Pong) EnableSession(sessionManager SessionIO)

if you want you HTTP session call this EnableSession will use memory to store session data EnableSession will read sessionId from request cookies value by name SessionCookiesName default is "SESSIONID" as sessionId EnableSession will cause performance drop compare to not use Session

func (*Pong) LoadTemplateGlob

func (pong *Pong) LoadTemplateGlob(path string)

load HTML template files whit glob if you will use render in response,you must call LoadTemplateGlob first to load template files. LoadTemplateGlob creates a new Template and parses the template definitions from the files identified by the pattern, which must match at least one file. The returned template will have the (base) name and (parsed) contents of the first file matched by the pattern. LoadTemplateGlob is equivalent to calling ParseFiles with the list of files matched by the pattern.

func (*Pong) ServeHTTP

func (pong *Pong) ServeHTTP(writer http.ResponseWriter, request *http.Request)

http.Server's ListenAndServe Handler

func (*Pong) TailMiddleware

func (pong *Pong) TailMiddleware(middlewareList ...HandleFunc)

add a middleware in the process's tail. which will execute before response data to client and after all of the other middleware register in router if you add more than one middlewares,this middlewares will execute in order

type Request

type Request struct {

	//point to http.Request in golang's standard lib
	HTTPRequest *http.Request
	// contains filtered or unexported fields
}

A Request represents an HTTP request received by a server or to be sent by a client. Request has some convenient method to get params form client

func (*Request) AutoBind

func (req *Request) AutoBind(pointer interface{}) error

auto bind will look request's http Header ContentType

if request ContentType is applicationJSON will use BindJSON to parse if request ContentType is applicationXML will use BindXML to parse if request ContentType is applicationForm or multipartForm will use BindForm to parse else will return an ErrorTypeNotSupport error

func (*Request) BindForm

func (req *Request) BindForm(pointer interface{}) error

parse request's body post form as map and bind data to struct use filed name

an error will return if the struct filed type is not support

func (*Request) BindJSON

func (req *Request) BindJSON(pointer interface{}) error

parse request's body data as JSON and use standard lib json.Unmarshal to bind data to struct

an error will return if json.Unmarshal return error

func (*Request) BindQuery

func (req *Request) BindQuery(pointer interface{}) error

parse request's query params as map and bind data to struct use filed name

an error will return if the struct filed type is not support

func (*Request) BindXML

func (req *Request) BindXML(pointer interface{}) error

parse request's body data as XML and use standard lib XML.Unmarshal to bind data to struct

an error will return if xml.Unmarshal return error

func (*Request) File

func (req *Request) File(name string) (multipart.File, *multipart.FileHeader, error)

returns the first file for the provided form key.

func (*Request) Form

func (req *Request) Form(name string) string

get Form param form request's body

returns the first value for the named component of the POST or PUT request body. URL query parameters are ignored. support both application/x-www-form-urlencoded and multipart/form-data

If key is not present, returns the empty string.

func (*Request) Param

func (req *Request) Param(name string) string

get Path param in request URL

for example:

register router is
	router.get("/user/:id",handle)
the request URL is
	/user/123
request.Param("id") == "123"

If key is not present, returns the empty string.

func (*Request) Query

func (req *Request) Query(name string) string

get Query param in request URL

for example:

the request URL is
	/user?name=hal
request.Query("name") == "hal"

If key is not present, returns the empty string.

type Response

type Response struct {

	// point to http.ResponseWriter in golang's standard lib
	HTTPResponseWriter http.ResponseWriter
	// HTTP status code response to client
	StatusCode int
	// contains filtered or unexported fields
}

is used by an HTTP handler to response to client's request.

func (*Response) Cookie

func (res *Response) Cookie(cookie *http.Cookie)

write a HTTP Cookie to response

use before response has send to client

func (*Response) File

func (res *Response) File(filePath string)

send a file response to client

replies to the request with the contents of the named file or directory. If the provided file or direcory name is a relative path, it is interpreted relative to the current directory and may ascend to parent directories. If the provided name is constructed from user input, it should be sanitized before calling ServeFile. As a precaution, ServeFile will reject requests where r.URL.Path contains a ".." path element.

As a special case, ServeFile redirects any request where r.URL.Path ends in "/index.html" to the same path, without the final "index.html". To avoid such redirects either modify the path or use ServeContent.

func (*Response) HTML

func (res *Response) HTML(html string)

send HTML response to client by html string

func (*Response) Header

func (res *Response) Header(name string, value string)

write a HTTP Header to response

use before response has send to client

func (*Response) JSON

func (res *Response) JSON(data interface{})

send JSON response to client

parse data by standard lib's json.Marshal and then send to client

if json.Marshal fail will call HTTPErrorHandle with error and context,to handle error you should define your pong.HTTPErrorHandle

func (*Response) JSONP

func (res *Response) JSONP(data interface{}, callback string)

send JSONP response to client

parse data by standard lib's json.Marshal and then send to client will wrap json to JavaScript's call method with give callback param

if json.Marshal fail will call HTTPErrorHandle with error and context,to handle error you should define your pong.HTTPErrorHandle

func (*Response) Redirect

func (res *Response) Redirect(url string)

Redirect replies to the request with a redirect to url, which may be a path relative to the request path.

The Response.StatusCode should be in the 3xx range and is usually StatusMovedPermanently, StatusFound or StatusSeeOther.

func (*Response) Render

func (res *Response) Render(template string, data interface{})

send HTML response to client by render HTML template with give data

LoadTemplateGlob before use Render

func (*Response) String

func (res *Response) String(str string)

send String response to client

func (*Response) XML

func (res *Response) XML(data interface{})

send XML response to client

parse data by standard lib's xml.Marshal and then send to client

if xml.Marshal fail will call HTTPErrorHandle with error and context,to handle error you should define your pong.HTTPErrorHandle

type Router

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

func (*Router) Any

func (r *Router) Any(path string, handle HandleFunc)

register an path to handle any type HTTP request incloud "GET" "HEAD" "POST" "PUT" "PATCH" "DELETE" "CONNECT" "OPTIONS" "TRACE"

func (*Router) Delete

func (r *Router) Delete(path string, handle HandleFunc)

register an path to handle HTTP Delete request

func (*Router) Get

func (r *Router) Get(path string, handle HandleFunc)

register an path to handle HTTP Get request

func (*Router) Head

func (r *Router) Head(path string, handle HandleFunc)

register an path to handle HTTP Head request

func (*Router) Middleware

func (r *Router) Middleware(handles ...HandleFunc)

add a Middleware to this router this Middleware list will execute in order before execute the handle you provide to response all of this router's sub router will also execute this Middleware list,parent's Middleware list first child's Middleware list later

func (*Router) Options

func (r *Router) Options(path string, handle HandleFunc)

register an path to handle HTTP Options request

func (*Router) Patch

func (r *Router) Patch(path string, handle HandleFunc)

register an path to handle HTTP Patch request

func (*Router) Post

func (r *Router) Post(path string, handle HandleFunc)

register an path to handle HTTP Post request

func (*Router) Put

func (r *Router) Put(path string, handle HandleFunc)

register an path to handle HTTP Put request

func (*Router) Router

func (r *Router) Router(path string) *Router

Add a sub router to this router

func (*Router) Trace

func (r *Router) Trace(path string, handle HandleFunc)

register an path to handle HTTP Trace request

type Session

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

func (*Session) Get

func (s *Session) Get(name string) interface{}

get the value by name from this session

func (*Session) Set

func (s *Session) Set(changes map[string]interface{}) error

set a value with name to this session can be used to overwrite old value

type SessionIO

type SessionIO interface {
	// NewSession should generate a sessionId which is unique compare to existent,and return this sessionId
	// this sessionId string will store in browser by cookies,so the sessionId string should compatible with cookies value rule
	NewSession() (sessionId string)
	// Destory should do operation to remove an session's data in store by give sessionId
	Destory(sessionId string) error
	// Reset should update the give old sessionId to a new id,but the value should be the same
	Reset(oldSessionId string) (newSessionId string, err error)
	// return whether this sessionId is existent in store
	Has(sessionId string) bool
	// read the whole value point to the give sessionId
	Read(sessionId string) (wholeValue map[string]interface{})
	// update the sessionId's value to store
	// the give value just has changed part not all of the value point to sessionId
	Write(sessionId string, changes map[string]interface{}) error
}

SessionIO define a interface to handle Session's read and write pong provide a in memory session manager as default stand by SessionManager interface you can define yourself's SessionManager like store session to Redis,MongoDB,File

Directories

Path Synopsis
session

Jump to

Keyboard shortcuts

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