Application

elton实例的相关方法说明

New

创建一个elton实例,并初始化相应的http.Server。

Example

package main

import (
	"github.com/vicanso/elton"
)

func main() {
	e := elton.New()
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

NewWithoutServer

创建一个Elton的实例,并未初始化相应的http.Server,主要用于需要自定义http server的场景,如各类超时设置等。

Example

package main

import (
	"net/http"
	"time"

	"github.com/vicanso/elton"
)

func main() {
	e := elton.NewWithoutServer()
	s := &http.Server{
		Handler:      e,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
	e.Server = s
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

ErrorHandler

自定义的Error处理,若路由处理过程中返回Error,则会触发此调用,若未指定此处理函数,则使用默认的处理,简单的输出err.Error()

注意若在处理过程中返回的Error已被处理(如Error Handler),则并不会触发此出错调用,尽量使用中间件将Error转换为相应的输出(如JSON),而此处理仅用于针对未处理出错响应。

Example

package main

import (
	"log"
	"net/http"

	"github.com/vicanso/elton"
	"github.com/vicanso/hes"
)

func main() {
	e := elton.New()

	e.ErrorHandler = func(c *elton.Context, err error) {
		if err != nil {
			log.Printf("未处理异常,url:%s, err:%v", c.Request.RequestURI, err)
		}
		he, ok := err.(*hes.Error)
		if ok {
			c.Response.WriteHeader(he.StatusCode)
			c.Response.Write([]byte(he.Message))
		} else {
			c.Response.WriteHeader(http.StatusInternalServerError)
			c.Response.Write([]byte(err.Error()))
		}
	}

	e.GET("/", func(c *elton.Context) (err error) {
		return hes.New("abcd")
	})
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

NotFoundHandler

未匹配到相应路由时的处理,当无法获取到相应路由时,则会调用此函数(未匹配相应路由时,所有的中间件也不会被调用)。如果有相关统计需要或者自定义的404页面,则可调整此函数,否则可不设置使用默认处理(返回404 Not Found)。

Example

package main

import (
	"log"
	"net/http"

	"github.com/vicanso/elton"
	"github.com/vicanso/hes"
)

func main() {
	e := elton.New()

	e.NotFoundHandler = func(resp http.ResponseWriter, req *http.Request) {
		// 可增加统计,方便分析404的处理是被攻击还是接口调用错误
		log.Printf("404, url:%s", req.RequestURI)
		resp.WriteHeader(http.StatusNotFound)
		resp.Write([]byte("Custom not found"))
	}

	e.GET("/ping", func(c *elton.Context) (err error) {
		return hes.New("abcd")
	})
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

MethodNotAllowedHandler

该HTTP请求方式不允许,路由匹配正确但是method不匹配时,则会调用此函数(此时所有的中间件也不会被调用)。如果有相关统计需要或者自定义的405页面,则可调整此函数,否则可不设置使用默认处理(返回405 Method Not Allowed)。

Example

package main

import (
	"log"
	"net/http"

	"github.com/vicanso/elton"
	"github.com/vicanso/hes"
)

func main() {
	e := elton.New()

	e.MethodNotAllowedHandler = func(resp http.ResponseWriter, req *http.Request) {
		// 可增加统计,方便分析405的处理是被攻击还是接口调用错误
		log.Printf("405, method: %s, url:%s", req.Method, req.RequestURI)
		resp.WriteHeader(http.StatusMethodNotAllowed)
		resp.Write([]byte("Custom method not allowed"))
	}

	e.GET("/ping", func(c *elton.Context) (err error) {
		return hes.New("abcd")
	})
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

GenerateID

ID生成函数,用于每次请求调用时,生成唯一的ID值。

Example

package main

import (
	"log"
	"math/rand"
	"time"

	"github.com/oklog/ulid"
	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

func main() {
	e := elton.New()

	e.GenerateID = func() string {
		t := time.Now()
		entropy := rand.New(rand.NewSource(t.UnixNano()))
		return ulid.MustNew(ulid.Timestamp(t), entropy).String()
	}

	e.Use(middleware.NewDefaultResponder())

	e.GET("/", func(c *elton.Context) (err error) {
		log.Println(c.ID)
		c.Body = c.ID
		return
	})
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

EnableTrace

是否启用调用跟踪,设置此参数为true,则会记录每个Handler的调用时长,建议使用时对全局中间件设定名称。

Example

package main

import (
	"log"

	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

func main() {
	e := elton.New()

	e.EnableTrace = true
	e.OnTrace(func(c *elton.Context, traceInfos elton.TraceInfos) {
		log.Println(traceInfos[0])
		// 设置HTTP响应头:Server-Timing
		c.ServerTiming(traceInfos, "elton-")
	})

	fn := middleware.NewDefaultResponder()
	// 自定义该中间件的名称,如果设置为"-",则忽略该中间件
	e.UseWithName(fn, "responder")

	e.GET("/", func(c *elton.Context) (err error) {
		c.Body = "Hello, World!"
		return
	})
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

SignedKeys

用于生成带签名的cookie的密钥,基于keygrip来生成与校验是否合法。使用SignedCookie获取cookie时,会校验cookie的合法性,而AddSignedCookie则会在添加cookie的同时再另外添加相对应的sig cookie。

Example

package main

import (
	"bytes"
	"net/http"

	"github.com/vicanso/elton"
)

func main() {
	e := elton.New()

	e.SignedKeys = new(elton.RWMutexSignedKeys)
	// 密钥,用于生成cookie的签名
	e.SignedKeys.SetKeys([]string{
		"secret key",
	})

	e.GET("/", func(c *elton.Context) (err error) {
		cookie, _ := c.SignedCookie("jt")
		// 如果该cookie不存在
		if cookie == nil {
			// 设置signed cookie
			err = c.AddSignedCookie(&http.Cookie{
				Name:  "jt",
				Value: "abcd",
			})
			if err != nil {
				return
			}
		}

		c.BodyBuffer = bytes.NewBufferString("Hello, World!")
		return
	})
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

ListenAndServe

设定监听地址,并调用http.Server的ListenAndServe提供HTTP服务。

Example

package main

import (
	"github.com/vicanso/elton"
)

func main() {
	e := elton.New()
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

ListenAndServeTLS

设定监听地址,并调用http.Server的ListenAndServeTLS提供HTTPS服务。

Example

package main

import (
	"github.com/vicanso/elton"
)

func main() {
    // 加密证书相关路径
    certFile := "~/cert/cert"
    keyFile := "~/cert/key"
	e := elton.New()
	err := e.ListenAndServeTLS(":3000", certFile, keyFile)
	if err != nil {
		panic(err)
	}
}

Close

关闭服务,调用http.Server的Close方法。

Example

package main

import (
	"time"

	"github.com/vicanso/elton"
)

func main() {
	e := elton.New()

	go func() {
		time.Sleep(5 * time.Second)
		e.Close()
	}()

	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

GracefulClose

优雅的关闭当前服务,首先将实例的状态设置为StatusClosing,此时所有新的请求都将直接出错,在等于指定时间后,则调用http.Server的Close方法。

Example

package main

import (
	"time"

	"github.com/vicanso/elton"
)

func main() {
	e := elton.New()

	go func() {
		e.GracefulClose(10 * time.Second)
	}()

	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

Handle

添加Handler的处理函数,配置请求的Method与Path,添加相应的处理函数。Elton还支持GET,POST,PUT,PATCH,DELETE,HEAD,TRACE以及OPTIONS的方法,这几个方法与Handle一致,Method则为相对应的处理,以及可使用ALL来指定支持所有的http method。

Example

package main

import (
	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

func main() {
	e := elton.New()

	e.Use(middleware.NewDefaultResponder())

	noop := func(c *elton.Context) error {
		return c.Next()
	}

	e.Handle("GET", "/", noop, func(c *elton.Context) (err error) {
		c.Body = "Hello, World!"
		return
	})

	e.POST("/users/{type}", func(c *elton.Context) (err error) {
		c.Body = "OK"
		return
	})

	e.GET("/files/*", func(c *elton.Context) (err error) {
		c.Body = "file content"
		return
	})

	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

Use/UseWithName

添加全局中间件处理函数,对于所有路由都需要使用到的中间件,则使用此函数添加,若非所有路由都使用到,可以只添加到相应的Group或者就单独添加至Handler。特别需要注意的是,如session之类需要读取数据库的,如非必要,不要使用全局中间件形式。UseWithName在添加中间件时指定其名称,用于在trace时生成统计耗时使用。

Example

package main

import (
	"log"
	"time"

	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

func main() {
	e := elton.New()

	// 记录HTTP请求的时间、响应码
	e.Use(func(c *elton.Context) (err error) {
		startedAt := time.Now()
		req := c.Request
		err = c.Next()
		log.Printf("%s %s %d use %s", req.Method, req.URL.RequestURI(), c.StatusCode, time.Since(startedAt).String())
		return err
	})

	e.UseWithName(middleware.NewDefaultResponder(), "responder")

	e.GET("/", func(c *elton.Context) (err error) {
		c.Body = "Hello, World!"
		return
	})

	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

Pre

添加全局前置中间件处理函数,对于所有请求都会调用(包括无匹配路由的请求),一般用于前置时对url做调整,如删除前缀等。

Example

package main

import (
	"net/http"
	"strings"

	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

func main() {
	e := elton.New()
	// 如果url以/api开头,则替换
	urlPrefix := "/api"
	e.Pre(func(req *http.Request) {
		path := req.URL.Path
		if strings.HasPrefix(path, urlPrefix) {
			req.URL.Path = path[len(urlPrefix):]
		}
	})
	e.Use(middleware.NewDefaultResponder())

	e.GET("/", func(c *elton.Context) (err error) {
		c.Body = "Hello, World!"
		return
	})
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

AddGroup

将group中的所有路由处理添加至Elton,需要注意,在调用AddGroup时则将该分组下的所有路由都添加至实例,因此如果在AddGroup后再调整该group则是无效的。

Example

package main

import (
	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

func main() {
	e := elton.New()
	e.Use(middleware.NewDefaultResponder())
	userGroup := elton.NewGroup("/users", func(c *elton.Context) error {
		return c.Next()
	})
	userGroup.GET("/me", func(c *elton.Context) error {
		c.Body = "nickname"
		return nil
	})

	e.AddGroup(userGroup)
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

OnError

添加Error的监听函数,如果当任一Handler的处理返回Error,并且其它的Handler并未将此Error处理(建议使用专门的中间件处理出错),则会触发error事件,建议使用此事件来监控程序未处理异常。

Example

package main

import (
	"errors"
	"log"

	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

func main() {
	e := elton.New()

	e.OnError(func(c *elton.Context, err error) {
		// 发送邮件告警等
		log.Println("error: " + err.Error())
	})

	e.Use(middleware.NewDefaultResponder())

	e.GET("/", func(c *elton.Context) (err error) {
		c.Body = "Hello, World!"
		return
	})
	// 由于未设置公共的出错处理中间件,此error会触发事件
	// 实际使用中,需要添加公共的error handler来处理,error事件只用于监控异常的出错
	e.GET("/error", func(c *elton.Context) (err error) {
		return errors.New("abcd")
	})
	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

Last updated