goa

package module
v0.1.5 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2021 License: MIT Imports: 20 Imported by: 10

README

goa

A golang http router with regexp and document generation support.

Build Status Coverage Status Go Report Card Documentation

Usage

demo main package
package main

import (
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/lovego/fs"
	"github.com/lovego/goa"
	"github.com/lovego/goa/benchmark/example/users"
	"github.com/lovego/goa/middlewares"
	"github.com/lovego/goa/server"
	"github.com/lovego/goa/utilroutes"
	"github.com/lovego/logger"
)

func main() {
	router := goa.New()
	// logger should comes first, to handle panic and log all requests
	router.Use(middlewares.NewLogger(logger.New(os.Stdout)).Record)
	router.Use(middlewares.NewCORS(allowOrigin).Check)
	utilroutes.Setup(&router.RouterGroup)

	if os.Getenv("GOA_DOC") != "" {
		router.DocDir(filepath.Join(fs.SourceDir(), "docs", "apis"))
	}

	// If don't need document, use this simple style.
	router.Get("/", func(c *goa.Context) {
		c.Data("index", nil)
	})

	// If need document, use this style for automated routes document generation.
	router.Group("/users", "用户", "用户相关的接口").
		Get("/", func(req struct {
			Title   string        `用户列表`
			Desc    string        `根据搜索条件获取用户列表`
			Query   users.ListReq
			Session users.Session
		}, resp *struct {
			Data  users.ListResp
			Error error
		}) {
			resp.Data, resp.Error = req.Query.Run(&req.Session)
		}).
		Get(`/(\d+)`, func(req struct {
			Title string       `用户详情`
			Desc  string       `根据用户ID获取用户详情`
			Param int64        `用户ID`
			Ctx   *goa.Context 
		}, resp *struct {
			Data  users.DetailResp
			Error error
		}) {
			resp.Data, resp.Error = users.Detail(req.Param)
		})

	if os.Getenv("GOA_DOC") != "" {
		return
	}

	server.ListenAndServe(router)
}

func allowOrigin(origin string) bool {
	u, err := url.Parse(origin)
	if err != nil {
		return false
	}
	hostname := u.Hostname()
	return strings.HasSuffix(hostname, ".example.com") ||
		hostname == "example.com" || hostname == "localhost"
}
demo users package
package users

import "time"

type ListReq struct {
	Name     string `c:"用户名称"`
	Type     string `c:"用户类型"`
	Page     int    `c:"页码"`
	PageSize int    `c:"每页数据条数"`
}

type ListResp struct {
	TotalSize int `c:"总数据条数"`
	TotalPage int `c:"总页数"`
	Rows      []struct {
		Id    int    `c:"ID"`
		Name  string `c:"名称"`
		Phone string `c:"电话号码"`
	}
}

type Session struct {
	UserId  int64
	LoginAt time.Time
}

func (l *ListReq) Run(sess *Session) (ListResp, error) {
	return ListResp{}, nil
}

type DetailResp struct {
	TotalSize int `c:"总数据条数"`
	TotalPage int `c:"总页数"`
	Rows      []struct {
		Id    int    `c:"ID"`
		Name  string `c:"名称"`
		Phone string `c:"电话号码"`
	}
}

func Detail(userId int64) (DetailResp, error) {
	return DetailResp{}, nil
}

Handler func

The req (request) parameter

The req parameter must be a struct, and it can have the following 8 fields. The Title and Desc fields are just used for document generation, so can be of any type, and their values are untouched; The other fields's values are set in advance from the http.Request, so can be used directly in the handler. Except Session and Ctx, other fields's full tag is used as description for the corresponding object in the document. For Param, Query, Header and Body, if it's a struct, the struct fields's comment or c tag is used as the description for the fields in the document.

  1. Title: It's full tag is used as title of the route in document.
  2. Desc: It's full tag is used as description the route in document.
  3. Param: Subexpression parameters in regular expression path. If there is only one subexpression in the path and it's not named, the whole Param is set to the match properly. Otherwise, the Param must be a struct, and it's fields are set properly to the corresponding named subexpression. The first letter of the subexpression name is changed to uppercase to find the corresponding field.
  4. Query: Query parameters in the the request, Query must be a struct, and it's fields are set properly to the corresponding query paramter. The first letter of the query parameter name is changed to uppercase to find the corresponding field in the struct.
  5. Header: Headers in the request. Header must be a struct, and it's fields are set properly to the corresponding header. The field's header tag(if present) or it's name is used as the corresponding header name.
  6. Body: The request body is set to Body using json.Unmarshal.
  7. Session: Session is set to goa.Context.Get("session"), so the type must be exactly the same.
  8. Ctx: Ctx must be of type *goa.Context.
The resp (response) parameter.

The resp parameter must be a struct pointer, and it can have the following 3 fields. The fields's full tag is used as description for the corresponding object in the document. For Data and Header, if it's a struct, the struct fields's comment or c tag is used as the description for the fields in the document.

  1. Data: Data is writed in response body as a data field using json.Marshal.
  2. Error: Error is writed in response body as the code and message fields.
  3. Header: Header is writed in response headers.

see full examples and the generated documents.

Default middlewares

  • logging with error alarm support
  • list of requests in processing
  • CORS check

Attentions

  • static route is always matched before regexp route.
  • call c.Next() in middleware to pass control to the next midlleware or route, if you don't call c.Next() no remaining midlleware or route will be executed.
  • generally don't use midlleware after routes, because generally the routes don't call c.Next().

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Context

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

func (*Context) ClientAddr

func (c *Context) ClientAddr() string
Example
req := httptest.NewRequest("GET", "/path", nil)
c := &Context{Request: req}
fmt.Println(c.ClientAddr())

req.Header.Set("X-Real-IP", "192.0.2.2")
fmt.Println(c.ClientAddr())

req.Header.Set("X-Forwarded-For", "192.0.2.3")
fmt.Println(c.ClientAddr())
Output:

192.0.2.1
192.0.2.2
192.0.2.3

func (*Context) Context

func (c *Context) Context() context.Context
Example
c := &Context{Request: httptest.NewRequest("GET", "/", nil)}
fmt.Println(c.Context())
ctx := context.WithValue(context.Background(), "custom", 1)
c.Set("context", ctx)
fmt.Println(c.Context() == ctx)
Output:

context.Background
true

func (*Context) Data

func (c *Context) Data(data interface{}, err error)
Example
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Data([]string{"data1", "data2"}, nil)
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
Output:

200
map[Content-Type:[application/json; charset=utf-8]]
{"code":"ok","message":"success","data":["data1","data2"]}
 <nil>
Example (Code_message)
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Data(nil, errs.New("the-code", "the message"))
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
fmt.Println(c.GetError())
Output:

200
map[Content-Type:[application/json; charset=utf-8]]
{"code":"the-code","message":"the message"}
 <nil>
<nil>
Example (Code_message_data_error)
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
theErr := errs.New("the-code", "the message")
theErr.SetError(errors.New("some error"))
theErr.SetData([]string{"data1", "data2"})
c.Data(nil, theErr)
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
fmt.Println(c.GetError())
Output:

200
map[Content-Type:[application/json; charset=utf-8]]
{"code":"the-code","message":"the message","data":["data1","data2"]}
 <nil>
some error
Example (Code_message_error)
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
theErr := errs.New("the-code", "the message")
theErr.SetError(errors.New("some error"))
c.Data(nil, theErr)
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
fmt.Println(c.GetError())
Output:

200
map[Content-Type:[application/json; charset=utf-8]]
{"code":"the-code","message":"the message"}
 <nil>
some error
Example (Error)
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Data(nil, errors.New("some error"))
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
fmt.Println(c.GetError())
Output:

500
map[Content-Type:[application/json; charset=utf-8]]
{"code":"server-err","message":"Server Error."}
 <nil>
some error

func (*Context) Flush

func (c *Context) Flush()
Example
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Flush()

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	c := &Context{ResponseWriter: w}
	c.Flush()
}))
defer ts.Close()

if _, err := http.Get(ts.URL); err != nil {
	log.Fatal(err)
}
Output:

func (*Context) Get

func (c *Context) Get(key string) interface{}
Example
c := &Context{}
fmt.Println(c.Get("a"))
c.Set("a", "韩梅梅")
fmt.Println(c.Get("a"))
Output:

<nil>
韩梅梅

func (*Context) GetError

func (c *Context) GetError() error

func (*Context) Hijack

func (c *Context) Hijack() (net.Conn, *bufio.ReadWriter, error)
Example
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
fmt.Println(c.Hijack())

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	c := &Context{ResponseWriter: w}
	conn, buf, err := c.Hijack()
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	buf.Write([]byte("HTTP/1.1 200 OK\r\nHeader-Key: Header-Value\r\n\r\nBody"))
	buf.Flush()
}))
defer ts.Close()

if res, err := http.Get(ts.URL); err != nil {
	log.Fatal(err)
} else {
	fmt.Println(res.StatusCode)
	fmt.Println(res.Header)
	body, err := ioutil.ReadAll(res.Body)
	fmt.Println(string(body), err)
}
Output:

<nil> <nil> the ResponseWriter doesn't support hijacking.
200
map[Header-Key:[Header-Value]]
Body <nil>

func (*Context) Json

func (c *Context) Json(data interface{})
Example
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Json(map[string]interface{}{"k": "<value>"})
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
Output:

200
map[Content-Type:[application/json; charset=utf-8]]
{"k":"<value>"}
 <nil>
Example (Error)
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Json(map[string]interface{}{"a": "a", "b": func() {}})
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
Output:

200
map[Content-Type:[application/json; charset=utf-8]]
{"code":"json-marshal-error","message":"json marshal error"} <nil>

func (*Context) Json2

func (c *Context) Json2(data interface{}, err error)
Example
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Json2(map[string]interface{}{"k": "<value>"}, errors.New("the error"))
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
fmt.Println(c.GetError())
Output:

200
map[Content-Type:[application/json; charset=utf-8]]
{"k":"<value>"}
 <nil>
the error

func (*Context) Next

func (c *Context) Next()
Example
c := &Context{}
c.Next()
c.Next()
Output:

func (*Context) Ok

func (c *Context) Ok(message string)
Example
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Ok("success")
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
Output:

200
map[Content-Type:[application/json; charset=utf-8]]
{"code":"ok","message":"success"}
 <nil>

func (*Context) Origin added in v0.0.7

func (c *Context) Origin() string

func (*Context) Param

func (c *Context) Param(i int) string

Param returns captured subpatterns. index begin at 0, so pass 0 to get the first captured subpatterns.

Example
c := &Context{params: []string{"123", "sdf"}}
fmt.Println(c.Param(0), "-")
fmt.Println(c.Param(1), "-")
fmt.Println(c.Param(2), "-")
Output:

123 -
sdf -
 -

func (*Context) Redirect

func (c *Context) Redirect(url string)
Example
recorder := httptest.NewRecorder()
c := &Context{ResponseWriter: recorder}
c.Redirect("http://example.com/path")
res := recorder.Result()
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body), err)
fmt.Println(c.GetError())
Output:

302
map[Location:[http://example.com/path]]
 <nil>
<nil>

func (*Context) RequestBody

func (c *Context) RequestBody() ([]byte, error)
Example
buf := []byte("hello world!")
c := &Context{Request: &http.Request{Body: ioutil.NopCloser(bytes.NewBuffer(buf))}}
body, err := c.RequestBody()
if err != nil {
	fmt.Println(err)
} else {
	fmt.Println(string(body))
}
body, err = c.RequestBody()
if err != nil {
	fmt.Println(err)
} else {
	fmt.Println(string(body))
}
Output:

hello world!
hello world!

func (*Context) RequestId

func (c *Context) RequestId() string

func (*Context) ResponseBody

func (c *Context) ResponseBody() []byte
Example
c := &Context{ResponseWriter: httptest.NewRecorder()}
fmt.Println("empty" + string(c.ResponseBody()))

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	c := &Context{ResponseWriter: w}
	fmt.Println("empty" + string(c.ResponseBody()))

	c.Write([]byte("1"))
	fmt.Println(string(c.ResponseBody()))

	c.ResponseWriter.Write([]byte("23"))
	fmt.Println(string(c.ResponseBody()))

	c.Write([]byte("456"))
	fmt.Println(string(c.ResponseBody()))
}))

res, err := http.Get(ts.URL)
if err != nil {
	log.Fatal(err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
	log.Fatal(err)
}
fmt.Println(string(body))
Output:

empty
empty
1
1
1456
123456

func (*Context) ResponseBodySize

func (c *Context) ResponseBodySize() int64
Example
c := &Context{ResponseWriter: httptest.NewRecorder()}
fmt.Println(c.ResponseBodySize())

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	c := &Context{ResponseWriter: w}
	fmt.Println(c.ResponseBodySize())

	c.Write([]byte("1"))
	fmt.Println(c.ResponseBodySize())

	c.ResponseWriter.Write([]byte("23"))
	fmt.Println(c.ResponseBodySize())

	c.Write([]byte("456"))
	fmt.Println(c.ResponseBodySize())
}))
_, err := http.Get(ts.URL)
if err != nil {
	log.Fatal(err)
}
Output:

0
0
1
3
6

func (*Context) Scheme

func (c *Context) Scheme() string
Example
req := httptest.NewRequest("GET", "http://example.com/path", nil)
c := &Context{Request: req}
fmt.Println(c.Scheme())
req.Header.Set("X-Forwarded-Proto", "https")
fmt.Println(c.Scheme())
Output:

http
https

func (*Context) Set

func (c *Context) Set(key string, value interface{})
Example
c := &Context{}
c.Set("a", "韩梅梅")
fmt.Println(c.Get("a"))
Output:

韩梅梅

func (*Context) SetError

func (c *Context) SetError(err error)

func (*Context) Status

func (c *Context) Status() int64
Example
c := &Context{ResponseWriter: httptest.NewRecorder()}
fmt.Println(c.Status())

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	c := &Context{ResponseWriter: w}
	fmt.Println(c.Status())
	c.WriteHeader(http.StatusOK)
	fmt.Println(c.Status())
}))
defer ts.Close()

res, err := http.Get(ts.URL)
if err != nil {
	log.Fatal(err)
}

fmt.Println(res.StatusCode)
Output:

0
0
200
200

func (*Context) StatusJson

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

func (*Context) Url

func (c *Context) Url() string
Example
c := &Context{Request: httptest.NewRequest("GET", "/path", nil)}
fmt.Println(c.Url())

c = &Context{Request: httptest.NewRequest("GET", "http://example.com/path", nil)}
fmt.Println(c.Url())
Output:

http://example.com/path
http://example.com/path

func (*Context) Write

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

type HandlerFuncs

type HandlerFuncs []func(*Context)

func (HandlerFuncs) String

func (handlers HandlerFuncs) String() string

to print regex_tree.Node in unit tests.

func (HandlerFuncs) StringIndent

func (handlers HandlerFuncs) StringIndent(indent string) string

type Router

type Router struct {
	RouterGroup
	// contains filtered or unexported fields
}
Example
router := New()

router.Get("/", func(c *Context) {
	fmt.Println("root")
})
users := router.Group("/users")

users.Get("/", func(c *Context) {
	fmt.Println("list users")
})
users.Get(`/(\d+)`, func(c *Context) {
	fmt.Printf("show user: %s\n", c.Param(0))
})

users.Post(`/`, func(c *Context) {
	fmt.Println("create a user")
})
users.Post(`/postx`, func(c *Context) {
})

users = users.Group(`/(\d+)`)

users.Put(`/`, func(c *Context) {
	fmt.Printf("fully update user: %s\n", c.Param(0))
})
users.Put(`/putx`, func(c *Context) {
})

users.Patch(`/`, func(c *Context) {
	fmt.Printf("partially update user: %s\n", c.Param(0))
})
users.Patch(`/patchx`, func(c *Context) {
})

users.Delete(`/`, func(c *Context) {
	fmt.Printf("delete user: %s\n", c.Param(0))
})
users.Delete(`/deletex`, func(c *Context) {
})

request, err := http.NewRequest("GET", "http://localhost/", nil)
if err != nil {
	log.Panic(err)
}
for _, route := range [][2]string{
	{"GET", "/"},
	{"GET", "/users"},
	{"POST", "/users"},
	{"GET", "/users/101/"}, // with a trailing slash
	{"PUT", "/users/101"},
	{"PATCH", "/users/101"},
	{"DELETE", "/users/101"},
} {
	request.Method = route[0]
	request.URL.Path = route[1]
	router.ServeHTTP(nil, request)
}
Output:

root
list users
create a user
show user: 101
fully update user: 101
partially update user: 101
delete user: 101

func New

func New() *Router

func (*Router) BeforeLookup added in v0.0.8

func (r *Router) BeforeLookup(fun func(rw http.ResponseWriter, req *http.Request))

func (*Router) NotFound

func (r *Router) NotFound(handler func(*Context))
Example
router := New()
router.Use(func(c *Context) {
	fmt.Println("middleware")
	c.Next()
})
router.Get("/", func(c *Context) {
	fmt.Println("root")
})

request, err := http.NewRequest("GET", "http://localhost/404", nil)
if err != nil {
	log.Panic(err)
}
rw := httptest.NewRecorder()
router.ServeHTTP(rw, request)

response := rw.Result()
if body, err := ioutil.ReadAll(response.Body); err != nil {
	fmt.Println(err)
} else {
	fmt.Println(response.StatusCode, string(body))
}

router.NotFound(func(c *Context) {
	fmt.Println("404 not found")
})
router.ServeHTTP(nil, request)

request.URL.Path = "/"
router.ServeHTTP(nil, request)
Output:

middleware
404 {"code":"404","message":"Not Found."}
middleware
404 not found
middleware
root

func (*Router) ServeHTTP

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

func (*Router) String

func (r *Router) String() string
Example
router := New()
router.Use(func(c *Context) {
	c.Next()
})

router.Get("/", func(c *Context) {
	fmt.Println("root")
})
users := router.Group("/users")

users.Get("/", func(c *Context) {
	fmt.Println("list users")
})
users.Get(`/(\d+)`, func(c *Context) {
	fmt.Printf("show user: %s\n", c.Param(0))
})

users.Post(`/`, func(c *Context) {
	fmt.Println("create a user")
})
users.Post(`/postx`, func(c *Context) {
})

fmt.Println(router)
Output:

{
  handlers: [
    github.com/lovego/goa.ExampleRouter_String.func1
  ]
  routes: {
    GET:
    { static: /, data: [
      github.com/lovego/goa.ExampleRouter_String.func1
      github.com/lovego/goa.ExampleRouter_String.func2
    ], children: [
      { static: users, data: [
        github.com/lovego/goa.ExampleRouter_String.func1
        github.com/lovego/goa.ExampleRouter_String.func3
      ], children: [
        { dynamic: ^/([0-9]+), data: [
          github.com/lovego/goa.ExampleRouter_String.func1
          github.com/lovego/goa.ExampleRouter_String.func4
        ] }
      ] }
    ] }
    POST:
    { static: /users, data: [
      github.com/lovego/goa.ExampleRouter_String.func1
      github.com/lovego/goa.ExampleRouter_String.func5
    ], children: [
      { static: /postx, data: [
        github.com/lovego/goa.ExampleRouter_String.func1
        github.com/lovego/goa.ExampleRouter_String.func6
      ] }
    ] }
  }
  notFound: github.com/lovego/goa.defaultNotFound
}

func (*Router) Use

func (r *Router) Use(handlers ...func(*Context))
Example
router := New()
router.Use(func(c *Context) {
	fmt.Println("middleware 1 pre")
	c.Next()
	fmt.Println("middleware 1 post")
})
router.Use(func(c *Context) {
	fmt.Println("middleware 2 pre")
	c.Next()
	fmt.Println("middleware 2 post")
})
router.Get("/", func(c *Context) {
	fmt.Println("root")
})

request, err := http.NewRequest("GET", "http://localhost/", nil)
if err != nil {
	log.Panic(err)
}
router.ServeHTTP(nil, request)
Output:

middleware 1 pre
middleware 2 pre
root
middleware 2 post
middleware 1 post

type RouterGroup

type RouterGroup struct {
	// contains filtered or unexported fields
}
Example
g := &RouterGroup{basePath: "/users", routes: make(map[string]*regex_tree.Node)}
g.Use(func(*Context) {})
g.Get("/nil", nil)
g.Get("/", func(*Context) {})

fmt.Println(g)
fmt.Println(g.Lookup("HEAD", "/users"))
fmt.Println(g.Lookup("POST", "/users"))
Output:

{
  basePath: /users
  handlers: [
    github.com/lovego/goa.ExampleRouterGroup.func1
  ]
  routes: {
    GET:
    { static: /users, data: [
      github.com/lovego/goa.ExampleRouterGroup.func1
      github.com/lovego/goa.ExampleRouterGroup.func2
    ] }
  }
}

[
  github.com/lovego/goa.ExampleRouterGroup.func1
  github.com/lovego/goa.ExampleRouterGroup.func2
] []
[ ] []
Example (ConcatPath_basic)
fmt.Println(RouterGroup{}.concatPath("/"))
fmt.Println(RouterGroup{}.concatPath("/users/"))
fmt.Println(RouterGroup{basePath: "/admin"}.concatPath(`/users/(\d+)`))
Output:

/
/users
/admin/users/(\d+)
Example (ConcatPath_error1)
defer func() {
	fmt.Println(recover())
}()
fmt.Println(RouterGroup{}.concatPath(""))
Output:

router path must not be empty.
Example (ConcatPath_error2)
defer func() {
	fmt.Println(recover())
}()
fmt.Println(RouterGroup{}.concatPath("abc"))
Output:

router path must begin with "/".

func (*RouterGroup) Add

func (g *RouterGroup) Add(method, path string, handler interface{}, args ...interface{}) *RouterGroup
Example (Error1)
defer func() {
	fmt.Println(recover())
}()
g := &RouterGroup{routes: make(map[string]*regex_tree.Node)}
g.Add("GET", "/(", func(*Context) {})
Output:

error parsing regexp: missing closing ): `/(`
Example (Error2)
defer func() {
	fmt.Println(recover())
}()
g := &RouterGroup{routes: make(map[string]*regex_tree.Node)}
g.Add("GET", "/", func(*Context) {})
g.Add("GET", "/", func(*Context) {})
Output:

path already exists

func (*RouterGroup) Delete

func (g *RouterGroup) Delete(path string, handler interface{}, args ...interface{}) *RouterGroup

func (*RouterGroup) DocDir

func (g *RouterGroup) DocDir(dir string) *RouterGroup

func (*RouterGroup) Get

func (g *RouterGroup) Get(path string, handler interface{}, args ...interface{}) *RouterGroup

func (*RouterGroup) GetPost

func (g *RouterGroup) GetPost(path string, handler interface{}, args ...interface{}) *RouterGroup

func (*RouterGroup) Group

func (g *RouterGroup) Group(path string, descs ...string) *RouterGroup

func (*RouterGroup) Lookup

func (g *RouterGroup) Lookup(method, path string) (HandlerFuncs, []string)

func (*RouterGroup) Patch

func (g *RouterGroup) Patch(path string, handler interface{}, args ...interface{}) *RouterGroup

func (*RouterGroup) Post

func (g *RouterGroup) Post(path string, handler interface{}, args ...interface{}) *RouterGroup

func (*RouterGroup) Put

func (g *RouterGroup) Put(path string, handler interface{}, args ...interface{}) *RouterGroup

func (*RouterGroup) RoutesString

func (g *RouterGroup) RoutesString() string

func (*RouterGroup) String

func (g *RouterGroup) String() string

func (*RouterGroup) Use

func (g *RouterGroup) Use(handlers ...func(*Context)) *RouterGroup

Use adds middlewares to the group, which will be executed for all routes in this group.

func (*RouterGroup) Watch added in v0.0.9

func (g *RouterGroup) Watch(
	watchers ...func(method, fullPath string, args []interface{}) func(*Context),
) *RouterGroup

Watch watchs every route in the group, and optionally return a middleware to be executed only for this route.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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