123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- package tango
- import (
- "encoding/json"
- "encoding/xml"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
- "reflect"
- )
- type Handler interface {
- Handle(*Context)
- }
- type Context struct {
- tan *Tango
- Logger
- idx int
- req *http.Request
- ResponseWriter
- route *Route
- params url.Values
- callArgs []reflect.Value
- matched bool
- cookies *cookies
- secCookies *secureCookies
- action interface{}
- Result interface{}
- }
- func NewContext(
- tan *Tango,
- req *http.Request,
- resp ResponseWriter,
- logger Logger) *Context {
- return &Context{
- tan: tan,
- idx: 0,
- req: req,
- ResponseWriter: resp,
- Logger: logger,
- }
- }
- func (ctx *Context) HandleError() {
- ctx.tan.ErrHandler.Handle(ctx)
- }
- func (ctx *Context) Req() *http.Request {
- return ctx.req
- }
- func (ctx *Context) SecureCookies(secret string) Cookies {
- if ctx.secCookies == nil {
- ctx.secCookies = &secureCookies{
- &cookies{
- ctx.req,
- ctx.ResponseWriter,
- },
- secret,
- }
- }
- return ctx.secCookies
- }
- func (ctx *Context) Cookies() Cookies {
- if ctx.cookies == nil {
- ctx.cookies = &cookies{
- ctx.req,
- ctx.ResponseWriter,
- }
- }
- return ctx.cookies
- }
- func (ctx *Context) Route() *Route {
- ctx.newAction()
- return ctx.route
- }
- func (ctx *Context) Params() url.Values {
- ctx.newAction()
- return ctx.params
- }
- func (ctx *Context) Action() interface{} {
- ctx.newAction()
- return ctx.action
- }
- func (ctx *Context) newAction() {
- if !ctx.matched {
- reqPath := removeStick(ctx.Req().URL.Path)
- ctx.route, ctx.params = ctx.tan.Match(reqPath, ctx.Req().Method)
- if ctx.route != nil {
- vc := ctx.route.newAction()
- ctx.action = vc.Interface()
- switch ctx.route.routeType {
- case StructPtrRoute:
- ctx.callArgs = []reflect.Value{vc.Elem()}
- case StructRoute:
- ctx.callArgs = []reflect.Value{vc}
- case FuncRoute:
- ctx.callArgs = []reflect.Value{}
- case FuncHttpRoute:
- ctx.callArgs = []reflect.Value{reflect.ValueOf(ctx.ResponseWriter),
- reflect.ValueOf(ctx.Req())}
- case FuncReqRoute:
- ctx.callArgs = []reflect.Value{reflect.ValueOf(ctx.Req())}
- case FuncResponseRoute:
- ctx.callArgs = []reflect.Value{reflect.ValueOf(ctx.ResponseWriter)}
- case FuncCtxRoute:
- ctx.callArgs = []reflect.Value{reflect.ValueOf(ctx)}
- default:
- panic("routeType error")
- }
- }
- ctx.matched = true
- }
- }
- func (ctx *Context) Next() {
- ctx.idx += 1
- ctx.Invoke()
- }
- func (ctx *Context) Invoke() {
- if ctx.idx < len(ctx.tan.handlers) {
- ctx.tan.handlers[ctx.idx].Handle(ctx)
- } else {
- ctx.newAction()
- // route is matched
- if ctx.action != nil {
- ret := ctx.route.method.Call(ctx.callArgs)
- if len(ret) > 0 {
- ctx.Result = ret[0].Interface()
- }
- // not route matched
- } else {
- if !ctx.Written() {
- ctx.NotFound()
- }
- }
- }
- }
- func (ctx *Context) ServeFile(path string) error {
- http.ServeFile(ctx, ctx.Req(), path)
- return nil
- }
- func (ctx *Context) ServeXml(obj interface{}) error {
- encoder := xml.NewEncoder(ctx)
- err := encoder.Encode(obj)
- if err == nil {
- ctx.Header().Set("Content-Type", "application/xml")
- }
- return err
- }
- func (ctx *Context) ServeJson(obj interface{}) error {
- encoder := json.NewEncoder(ctx)
- err := encoder.Encode(obj)
- if err == nil {
- ctx.Header().Set("Content-Type", "application/json")
- }
- return err
- }
- func (ctx *Context) Download(fpath string) error {
- f, err := os.Open(fpath)
- if err != nil {
- return err
- }
- defer f.Close()
- fName := filepath.Base(fpath)
- ctx.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%v\"", fName))
- _, err = io.Copy(ctx, f)
- return err
- }
- func (ctx *Context) Redirect(url string, status ...int) {
- s := http.StatusFound
- if len(status) > 0 {
- s = status[0]
- }
- http.Redirect(ctx.ResponseWriter, ctx.Req(), url, s)
- }
- // Notmodified writes a 304 HTTP response
- func (ctx *Context) NotModified() {
- ctx.WriteHeader(http.StatusNotModified)
- }
- func (ctx *Context) Unauthorized() {
- ctx.Abort(http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized))
- }
- // NotFound writes a 404 HTTP response
- func (ctx *Context) NotFound(message ...string) {
- if len(message) == 0 {
- ctx.Abort(http.StatusNotFound, http.StatusText(http.StatusNotFound))
- return
- }
- ctx.Abort(http.StatusNotFound, message[0])
- }
- // Abort is a helper method that sends an HTTP header and an optional
- // body. It is useful for returning 4xx or 5xx errors.
- // Once it has been called, any return value from the handler will
- // not be written to the response.
- func (ctx *Context) Abort(status int, body string) {
- ctx.Result = Abort(status, body)
- ctx.HandleError()
- }
- type Contexter interface {
- SetContext(*Context)
- }
- type Ctx struct {
- *Context
- }
- func (c *Ctx) SetContext(ctx *Context) {
- c.Context = ctx
- }
- func Contexts() HandlerFunc {
- return func(ctx *Context) {
- if action := ctx.Action(); action != nil {
- if a, ok := action.(Contexter); ok {
- a.SetContext(ctx)
- }
- }
- ctx.Next()
- }
- }
|