looli

package module
v0.1.4 Latest Latest
Warning

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

Go to latest
Published: May 28, 2017 License: MIT Imports: 19 Imported by: 0

README

looli

Build Status Coverage Status License

looli is a minimalist web framework for golang.

Feature

Installation

go get github.com/cssivision/looli

Usage

Router

looli build on the top of router library, which support Named parameters Wildcard parameters Trailing slash redirect Case sensitive Prefix router, for detail.

Using GET, POST, PUT, PATCH, DELETE and OPTIONS
package main

import (
	"github.com/cssivision/looli"
	"log"
)

func main() {
	router := looli.Default()

	router.Get("/a", func(c *looli.Context) {})
	router.Post("/a", func(c *looli.Context) {})
	router.Put("/a", func(c *looli.Context) {})
	router.Delete("/a", func(c *looli.Context) {})
	router.Patch("/a", func(c *looli.Context) {})
	router.Head("/a", func(c *looli.Context) {})
	router.Options("/a", func(c *looli.Context) {})

	log.Fatal(router.Run(":8080"))
}

Named parameter

Named parameters only match a single path segment:

Pattern: /user/:name

 /user/gordon              match
 /user/you                 match
 /user/gordon/profile      no match
 /user/                    no match

Pattern: /:user/:name

 /a/gordon                 match
 /b/you                    match
 /user/gordon/profile      no match
 /user/                    no match
package main

import (
    "net/http"
    "github.com/cssivision/looli"
)

func main() {
    router := looli.Default()

    router.Get("/a/:name", func(c *looli.Context) {
        c.Status(200)
        c.String("hello " + c.Param("name") + "!\n")
    })

    http.ListenAndServe(":8080", router)
}
Wildcard pattern

Match everything, therefore they must always be at the end of the pattern:

Pattern: /src/*filepath

 /src/                     match
 /src/somefile.go          match
 /src/subdir/somefile.go   match
package main

import (
    "net/http"
    "github.com/cssivision/looli"
)

func main() {
    router := looli.Default()

    router.Get("/a/*filepath", func(c *looli.Context) {
        c.Status(200)
        c.String("hello " + c.Param("filepath") + "!\n")
    })

    http.ListenAndServe(":8080", router)
}
Trailing slash redirect

By default will redirect, which means if we register path /a/b, we can request with /a/b/, conversely also success. redirect will work only in the situation that the request can not found, if both define path /a/b and /a/b/, redirect will not work.

/a/b -> /a/b/
/a/b/ -> /a/b
package main

import (
    "net/http"
    "github.com/cssivision/looli"
)

func main() {
    router := looli.Default()

    // default is true, we can forbidden this behavior by set is to false
    // request with /a/ will get 404
    router.SetTrailingSlashRedirect(false)

    router.Get("/a", func(c *looli.Context) {
        c.Status(200)
        c.String("hello world!\n")
    })

    http.ListenAndServe(":8080", router)
}
Case sensitive

By default is not case sensitive, which means if we register path /a/b, request with /A/B will get 404 not found. if we set true, request path with /A/B will success.

package main

import (
    "net/http"
    "github.com/cssivision/looli"
)

func main() {
    router := looli.Default()

    // default is false, we can forbidden this behavior by set is to true
    // request with /A/ will success.
    router.SetIgnoreCase(true)

    router.Get("/a", func(c *looli.Context) {
        c.Status(200)
        c.String("hello world!\n")
    })

    http.ListenAndServe(":8080", router)
}
Prefix router

Group router using prefix

package main

import (
    "net/http"
    "github.com/cssivision/looli"
)

func main() {
    router := looli.Default()

    v1 := router.Prefix("/v1")
    v1.Get("/a", func(c *looli.Context) {
        c.Status(200)
        c.String("hello world version1\n")
    })

    v2 := router.Prefix("/v2")
    v2.Get("/a", func(c *looli.Context) {
        c.Status(200)
        c.String("hello world version2\n")
    })

    router.Get("/a", func(c *looli.Context) {
        c.Status(200)
        c.String("hello world!\n")
    })

    http.ListenAndServe(":8080", router)
}
Serving static files
package main

import (
    "net/http"
    "github.com/cssivision/looli"
)

func main() {
    router := looli.Default()

    // Serve file in the path
    router.StaticFile("/somefile.go", "file/path")

    // Serve files in staic directory
    router.Static("/static", "./static")

    http.ListenAndServe(":8080", router)
}

Context

Context supply some syntactic sugar.

Query and Form
package main

import (
    "net/http"
    "github.com/cssivision/looli"
)

func main() {
    router := looli.Default()

    router.Get("/query", func(c *looli.Context) {
        id := c.Query("id")
        name := c.DefaultQuery("name", "cssivision")
        c.Status(200)
        c.String("hello %s, %s\n", id, name)
    })

    router.Post("/form", func(c *looli.Context) {
        name := c.DefaultPostForm("name", "somebody")
        age := c.PostForm("age")
        c.Status(200)
        c.JSON(looli.JSON{
            "name": name,
            "age": age,
        })
    })

    http.ListenAndServe(":8080", router)
}

query

curl 'localhost:8080/query?id=1&name=cssivision'

form

curl -d 'age=21&other=haha' 'localhost:8080/form?id=1&name=cssivision'

Use method to operate header and cookie

package main

import (
    "fmt"
    "github.com/cssivision/looli"
    "log"
    "net/http"
)

func main() {
    router := looli.Default()

    router.Get("/header", func(c *looli.Context) {
        fmt.Println(c.Header("User-Agent"))
        c.SetHeader("fake-header", "fake")
        c.Status(200)
        c.String("fake header has setted\n")
    })

    router.Get("/cookie", func(c *looli.Context) {
        val, _ := c.Cookie("fake-cookie")
        fmt.Println(val)
        c.SetCookie(&http.Cookie{
            Name: "fake-cookie",
            Value: "fake",
        })
        c.Status(200)
        c.String("fake cookie has setted\n")
    })

    log.Fatal(router.Run(":8080"))
}
Data binding

To bind a request into a type, use data binding, data can from query, post body. currently support binding of JSON, XML and standard form values (x-www-form-urlencoded and multipart/form-data). When using the Bind-method, the binder depending on the Content-Type header.

Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set json:"fieldname".

The Validation of the incoming data show below.

package main

import (
    "fmt"
    "github.com/cssivision/looli"
    "net/http"
)

type Infomation struct {
    Name string`json:"name"`
    Age int`json:"age"`
}

func (i *Infomation) Validate() error {
    // data validate,
    return nil
}

func main() {
    router := looli.Default()

    // curl 'localhost:8080/query?name=cssivision&age=21'
    router.Get("/query", func(c *looli.Context) {
        query := new(Infomation)
        if err := c.Bind(query); err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(query.Name)
        fmt.Println(query.Age)
        c.Status(200)
        c.JSON(query)
    })

    // curl -d "name=cssivision&age=21" 'localhost:8080/form'
    router.Post("/form", func(c *looli.Context) {
        form := new(Infomation)
        if err := c.Bind(form); err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(form.Name)
        fmt.Println(form.Age)
        c.Status(200)
        c.JSON(form)
    })

    // curl  -H "Content-Type: application/json" -X POST -d '{"name":"cssivision","age":21}' localhost:8080/json
    router.Post("/json", func(c *looli.Context) {
        json := new(Infomation)
        if err := c.Bind(json); err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(json.Name)
        fmt.Println(json.Age)
        c.Status(200)
        c.JSON(json)
    })

    http.ListenAndServe(":8080", router)
}
String JSON rendering
package main

import (
    "github.com/cssivision/looli"
    "net/http"
)

func main() {
    router := looli.Default()

    router.Get("/string", func(c *looli.Context) {
        c.String("the response is %s\n", "string")
    })

    router.Get("/json1", func(c *looli.Context) {
        c.JSON(looli.JSON{
            "name": "cssivision",
            "age": 21,
        })
    })

    router.Get("/json2", func(c *looli.Context) {
        var msg struct {
            Name string`json:"name"`
            Age int`json:"age"`
        }

        msg.Name = "cssivision"
        msg.Age = 21

        c.JSON(msg)
    })

    http.ListenAndServe(":8080", router)
}
HTML rendering
package main

import (
    "github.com/cssivision/looli"
    "net/http"
)

func main() {
    router := looli.Default()

    router.LoadHTMLGlob("templates/*")
    router.Get("/html", func(c *looli.Context) {
        c.HTML("index.tmpl", looli.JSON{
            "title": "my site",
        })
    })

    http.ListenAndServe(":8080", router)
}

templates/index.tmpl

<html>
    <h1>
        {{ .title }}
    </h1>
</html>

Middleware

looli.Default() with middleware Logger() Recover() by default, without middleware use looli.New() instead.

Using middleware
package main

import (
    "net/http"
    "github.com/cssivision/looli"
    "log"
)

func main() {
    router := looli.New()

    // global middleware
    router.Use(looli.Logger())
    router.Get("/a", func(c *looli.Context) {
        c.Status(200)
        c.String("hello world!\n")
    })

    // multi handler for specificed path
    router.Get("/b", func(c *looli.Context) {
        c.String("first handler\n")
    }, func(c *looli.Context) {
        c.String("second handler\n")
    })

    v1 := router.Prefix("/v1")

    // recover middleware only work for /v1 prefix router
    v1.Use(looli.Recover())
    v1.Get("/a", func(c *looli.Context) {
        panic("error!")
        c.Status(200)
        c.String("hello world!\n")
    })

    log.Fatal(http.ListenAndServe(":8080", router))
}
Builtin middlewares
Custome middleware
package main

import (
    "log"
    "net/http"
    "github.com/cssivision/looli"
    "time"
)

func Logger() looli.HandlerFunc {
    return func(c *looli.Context) {
        t := time.Now()
        // before request
        c.Next()
        // after request
        latency := time.Since(t)
        log.Print(latency)
    }
}

func main() {
    router := looli.New()

    // global middleware
    router.Use(Logger())
    router.Get("/a", func(c *looli.Context) {
        c.Status(200)
        c.String("hello world!\n")
    })

    http.ListenAndServe(":8080", router)
}

Licenses

All source code is licensed under the MIT License.

Todo

  • elegant error handle

Documentation

Overview

Package looli is a minimalist web framework for go

router := looli.Default()

v1 := router.Prefix("/v1")
v1.Get("/a", func(c *looli.Context) {
    c.Status(200)
    c.String("hello world version1\n")
})

v2 := router.Prefix("/v2")
v2.Get("/a", func(c *looli.Context) {
    c.Status(200)
    c.String("hello world version2\n")
})

router.Get("/a", func(c *looli.Context) {
    c.Status(200)
    c.String("hello world!\n")
})

Index

Constants

View Source
const (
	MIMEJSON              = "application/json"
	MIMEXML               = "application/xml"
	MIMEXML2              = "text/xml"
	MIMEPOSTForm          = "application/x-www-form-urlencoded"
	MIMEMultipartPOSTForm = "multipart/form-data"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Binding

type Binding interface {
	Bind(*http.Request, interface{}) error
}

type BindingStruct

type BindingStruct interface {
	Validate() error
}

type Context

type Context struct {
	http.ResponseWriter

	// Short for http.Request
	Request *http.Request

	// Param is URL parameter, a map[string]string.
	Params Params

	// Short for Request.URL.Path
	Path string

	// Short for Request.Method
	Method string

	// Error when processing request
	Err *Error
	// contains filtered or unexported fields
}

Context construct Request and ResponseWriter, provide useful methods

func NewContext

func NewContext(p *RouterPrefix, rw http.ResponseWriter, req *http.Request) *Context

func (*Context) Abort

func (c *Context) Abort()

Abort prevents pending handlers from being called. Note that this will not stop the current handler. if you want to stop current handler you should return, after call abort, call Abort to ensure the remaining handlers for this request are not called.

func (*Context) AbortWithStatus

func (c *Context) AbortWithStatus(code int)

AbortWithStatus prevents pending handlers from being called and set statuscode. Note that this will not stop the current handler. if you want to stop current handler you should return, after call abort, call Abort to ensure the remaining handlers for this request are not called.

func (*Context) Bind

func (c *Context) Bind(data BindingStruct) error

Bind checks the Content-Type to select a binding engine automatically, Depending the "Content-Type" header different bindings are used:

"application/json" --> JSON
"application/xml"  --> XML

otherwise --> returns an error It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer. Like ParseBody() but this method also writes a 400 error if the json is not valid.

func (*Context) ClientIP

func (c *Context) ClientIP() string

ClientIP implements a best effort algorithm to return the real client IP, it parses X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.

func (*Context) ContentType

func (c *Context) ContentType() string

ContentType return content-type from header

func (*Context) Cookie

func (c *Context) Cookie(name string) (string, error)

Cookie get cookie from request header by name, if err != nil, return "", err

func (*Context) DefaultPostForm

func (c *Context) DefaultPostForm(key, defaultValue string) string

DefaultPostForm returns the specified key from a POST urlencoded form or multipart form when it exists, otherwise it returns the specified defaultValue string.

func (*Context) DefaultQuery

func (c *Context) DefaultQuery(key, defaultValue string) string

Query returns the keyed url query value if it exists, othewise it returns spectfic defaultValue. It is shortcut for `c.Request.URL.Query().Get(key)` GET /path?&name=cssivision&age=23

		c.DefaultQuery("name", "balabala") == "cssivision"
     c.Query("age", "24") == "23"
		c.Query("sex", "male") == "male"

func (*Context) Error

func (c *Context) Error(err error)

func (*Context) HTML

func (c *Context) HTML(name string, data interface{})

func (*Context) Header

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

Get gets the first value associated with the given key. It is case insensitive

func (*Context) IsAborted

func (c *Context) IsAborted() bool

IsAborted returns true if the current context was aborted.

func (*Context) JSON

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

JSON write obj to response

func (*Context) Next

func (c *Context) Next()

Next should be used only inside middleware. It executes the pending handlers in the chain inside the calling handler

func (*Context) Param

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

Param return the parameters by name in the request path

func (*Context) PostForm

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

PostForm returns the specified key from a POST urlencoded form or multipart form when it exists, otherwise it returns an empty string.

func (*Context) Query

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

Query returns the keyed url query value if it exists, othewise it returns an empty string `("")`. It is shortcut for `c.Request.URL.Query().Get(key)` GET /path?&name=cssivision&age=23

		c.Query("name") == "cssivision"
     c.Query("age") == "23"
		c.Query("sex") == ""

func (*Context) Redirect

func (c *Context) Redirect(location string)

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

func (*Context) ServeFile

func (c *Context) ServeFile(filepath string)

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 (*Context) SetCookie

func (c *Context) SetCookie(cookie *http.Cookie)

SetCookie use http.SetCookie to set set-cookie header

func (*Context) SetHeader

func (c *Context) SetHeader(key, value string)

Set sets the header entries associated with key to the single element value. It replaces any existing values associated with key.

func (*Context) Status

func (c *Context) Status(code 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) String

func (c *Context) String(format string, values ...interface{})

String write format string to response

type Engine

type Engine struct {
	// router with basePath, default basePath = ""
	RouterPrefix

	// when set true, implements a best effort algorithm to return the real client IP, it parses
	// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
	ForwardedByClientIP bool

	// template used to render HTML
	Template *template.Template
	// contains filtered or unexported fields
}

func Default

func Default() *Engine

Default return engine instance, add logger, recover handler to it.

func New

func New() *Engine

func (*Engine) LoadHTMLFiles

func (engine *Engine) LoadHTMLFiles(files ...string)

func (*Engine) LoadHTMLGlob

func (engine *Engine) LoadHTMLGlob(pattern string)

func (*Engine) NoMethod

func (engine *Engine) NoMethod(handlers ...HandlerFunc)

NoMethod which is called when method is not registered. If it is not set, noMethod is used.

func (*Engine) NoRoute

func (engine *Engine) NoRoute(handlers ...HandlerFunc)

NoRoute which is called when no matching route is found. If it is not set, noRoute is used.

func (*Engine) ServeHTTP

func (engine *Engine) ServeHTTP(rw http.ResponseWriter, req *http.Request)

http.Handler interface

func (*Engine) SetIgnoreCase

func (engine *Engine) SetIgnoreCase(ignoreCase bool)

set IgnoreCase value

func (*Engine) SetTrailingSlashRedirect

func (engine *Engine) SetTrailingSlashRedirect(redirect bool)

set TrailingSlashRedirect value

type Error

type Error struct {
	Err  error
	Code int
	Meta interface{}
}

func (*Error) Error

func (err *Error) Error() string

type Handle

type Handle func(http.ResponseWriter, *http.Request, Params)

Handle is a function that can be registered to a route to handle HTTP requests. Like http.HandlerFunc, but has a third parameter for the values of named/wildcards parameters.

type Handler

type Handler interface {
	Handle(*Context)
}

type HandlerFunc

type HandlerFunc func(*Context)

func Logger

func Logger() HandlerFunc

func LoggerWithWriter

func LoggerWithWriter(out io.Writer) HandlerFunc

func Recover

func Recover() HandlerFunc

func RecoverWithWriter

func RecoverWithWriter(out io.Writer) HandlerFunc

type JSON

type JSON map[string]interface{}

type Params

type Params map[string]string

Param is a single URL parameter, a map[string]string.

type Router

type Router struct {

	// Ignore case when matching URL path.
	IgnoreCase bool

	// Enables automatic redirection if the current route can't be matched but a
	// handler for the path with (without) the trailing slash exists.
	// TrailingSlashRedirect: /a/b/ -> /a/b
	// TrailingSlashRedirect: /a/b -> /a/b/
	TrailingSlashRedirect bool

	// Configurable http.Handler which is called when no matching route is
	// found. If it is not set, http.NotFound is used.
	NoRoute []HandlerFunc

	// Configurable http.Handler which is called when method is not allowed. If it is not set, http.NotFound is used.
	NoMethod []HandlerFunc
	// contains filtered or unexported fields
}

Router is a http.Handler which can be used to dispatch requests to different handler functions via configurable routes

func NewRouter

func NewRouter() *Router

New returns a new initialized Router, with default configuration

func (*Router) Handle

func (r *Router) Handle(method, pattern string, handlers []HandlerFunc)

Handle registers a new request handle with the given path and method. For GET, POST, PUT, PATCH and DELETE requests the respective shortcut functions can be used.

type RouterPrefix

type RouterPrefix struct {
	Middlewares []HandlerFunc
	// contains filtered or unexported fields
}

RouterPrefix is used internally to configure router, a RouterPrefix is associated with a basePath and an array of handlers (middleware)

func (*RouterPrefix) Any

func (p *RouterPrefix) Any(pattern string, handlers ...HandlerFunc)

Any registers a route that matches all the HTTP methods. GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE

func (*RouterPrefix) Delete

func (p *RouterPrefix) Delete(pattern string, handlers ...HandlerFunc)

Delete is a shortcut for router.Handle("DELETE", path, handle)

func (*RouterPrefix) Get

func (p *RouterPrefix) Get(pattern string, handlers ...HandlerFunc)

Get is a shortcut for router.Handle("GET", path, handle)

func (*RouterPrefix) Handle

func (p *RouterPrefix) Handle(method, pattern string, handlers ...HandlerFunc)

Handle registers a new request handle and middleware with the given path and method.

func (*RouterPrefix) Head

func (p *RouterPrefix) Head(pattern string, handlers ...HandlerFunc)

Head is a shortcut for router.Handle("HEAD", path, handle)

func (*RouterPrefix) Options

func (p *RouterPrefix) Options(pattern string, handlers ...HandlerFunc)

Options is a shortcut for router.Handle("OPTIONS", path, handle)

func (*RouterPrefix) Patch

func (p *RouterPrefix) Patch(pattern string, handlers ...HandlerFunc)

Patch is a shortcut for router.Handle("PATCH", path, handle)

func (*RouterPrefix) Post

func (p *RouterPrefix) Post(pattern string, handlers ...HandlerFunc)

Post is a shortcut for router.Handle("Post", path, handle)

func (*RouterPrefix) Prefix

func (p *RouterPrefix) Prefix(basePath string) *RouterPrefix

Prefix creates a new router prefix. You should add all the routes that have common middlwares or the same path prefix. For example, all the routes that use a common middlware could be grouped.

func (*RouterPrefix) Put

func (p *RouterPrefix) Put(pattern string, handlers ...HandlerFunc)

Put is a shortcut for router.Handle("Put", path, handle)

func (*RouterPrefix) Static

func (p *RouterPrefix) Static(pattern, dir string)

Static register router pattern and response file in the request url

func (*RouterPrefix) StaticFile

func (p *RouterPrefix) StaticFile(pattern, filepath string)

StaticFile register router pattern and response file in path

func (*RouterPrefix) Use

func (p *RouterPrefix) Use(middleware ...HandlerFunc)

Use adds middleware to the router.

func (*RouterPrefix) UseHandler

func (p *RouterPrefix) UseHandler(handlers ...Handler)

Use adds handlers as middleware to the router.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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