controller.go 9.8 KB
Newer Older
A
#2  
astaxie 已提交
1 2 3 4
package beego

import (
	"bytes"
A
astaxie 已提交
5
	"crypto/hmac"
6
	"crypto/rand"
A
astaxie 已提交
7 8
	"crypto/sha1"
	"encoding/base64"
A
astaxie 已提交
9
	"errors"
A
astaxie 已提交
10
	"fmt"
A
#2  
astaxie 已提交
11
	"html/template"
A
astaxie 已提交
12
	"io"
A
#2  
astaxie 已提交
13
	"io/ioutil"
A
astaxie 已提交
14
	"mime/multipart"
A
#2  
astaxie 已提交
15 16
	"net/http"
	"net/url"
A
astaxie 已提交
17
	"os"
A
astaxie 已提交
18
	"reflect"
A
#2  
astaxie 已提交
19
	"strconv"
A
astaxie 已提交
20
	"strings"
A
astaxie 已提交
21
	"time"
A
astaxie 已提交
22 23 24

	"github.com/astaxie/beego/context"
	"github.com/astaxie/beego/session"
A
#2  
astaxie 已提交
25 26
)

A
astaxie 已提交
27 28 29 30
var (
	USERSTOPRUN = errors.New("User stop run")
)

A
#2  
astaxie 已提交
31
type Controller struct {
32 33 34 35 36 37 38 39 40 41 42 43
	Ctx            *context.Context
	Data           map[interface{}]interface{}
	controllerName string
	actionName     string
	TplNames       string
	Layout         string
	TplExt         string
	_xsrf_token    string
	gotofunc       string
	CruSession     session.SessionStore
	XSRFExpire     int
	AppController  interface{}
A
#2  
astaxie 已提交
44 45 46
}

type ControllerInterface interface {
47
	Init(ct *context.Context, controllerName, actionName string, app interface{})
A
#2  
astaxie 已提交
48 49 50 51 52 53 54 55 56 57 58 59
	Prepare()
	Get()
	Post()
	Delete()
	Put()
	Head()
	Patch()
	Options()
	Finish()
	Render() error
}

60
func (c *Controller) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
A
#2  
astaxie 已提交
61 62 63
	c.Data = make(map[interface{}]interface{})
	c.Layout = ""
	c.TplNames = ""
64 65
	c.controllerName = controllerName
	c.actionName = actionName
A
#2  
astaxie 已提交
66 67
	c.Ctx = ctx
	c.TplExt = "tpl"
A
astaxie 已提交
68
	c.AppController = app
A
#2  
astaxie 已提交
69 70 71 72 73 74 75
}

func (c *Controller) Prepare() {

}

func (c *Controller) Finish() {
A
astaxie 已提交
76 77 78

}

A
#2  
astaxie 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
func (c *Controller) Get() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}

func (c *Controller) Post() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}

func (c *Controller) Delete() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}

func (c *Controller) Put() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}

func (c *Controller) Head() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}

func (c *Controller) Patch() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}

func (c *Controller) Options() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}

func (c *Controller) Render() error {
A
astaxie 已提交
108 109 110 111 112
	rb, err := c.RenderBytes()

	if err != nil {
		return err
	} else {
A
astaxie 已提交
113 114
		c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
		c.Ctx.Output.Body(rb)
A
astaxie 已提交
115 116 117 118
	}
	return nil
}

A
astaxie 已提交
119 120 121 122 123
func (c *Controller) RenderString() (string, error) {
	b, e := c.RenderBytes()
	return string(b), e
}

A
astaxie 已提交
124
func (c *Controller) RenderBytes() ([]byte, error) {
A
#2  
astaxie 已提交
125 126 127
	//if the controller has set layout, then first get the tplname's content set the content to the layout
	if c.Layout != "" {
		if c.TplNames == "" {
128
			c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
A
#2  
astaxie 已提交
129
		}
130
		if RunMode == "dev" {
A
astaxie 已提交
131
			BuildTemplate(ViewsPath)
132
		}
A
#2  
astaxie 已提交
133
		newbytes := bytes.NewBufferString("")
A
astaxie 已提交
134
		if _, ok := BeeTemplates[c.TplNames]; !ok {
A
astaxie 已提交
135 136 137
			panic("can't find templatefile in the path:" + c.TplNames)
			return []byte{}, errors.New("can't find templatefile in the path:" + c.TplNames)
		}
A
astaxie 已提交
138
		err := BeeTemplates[c.TplNames].ExecuteTemplate(newbytes, c.TplNames, c.Data)
A
astaxie 已提交
139
		if err != nil {
A
astaxie 已提交
140
			Trace("template Execute err:", err)
141
			return nil, err
A
astaxie 已提交
142
		}
A
#2  
astaxie 已提交
143 144
		tplcontent, _ := ioutil.ReadAll(newbytes)
		c.Data["LayoutContent"] = template.HTML(string(tplcontent))
A
astaxie 已提交
145
		ibytes := bytes.NewBufferString("")
A
astaxie 已提交
146
		err = BeeTemplates[c.Layout].ExecuteTemplate(ibytes, c.Layout, c.Data)
A
#2  
astaxie 已提交
147 148
		if err != nil {
			Trace("template Execute err:", err)
149
			return nil, err
A
#2  
astaxie 已提交
150
		}
A
astaxie 已提交
151 152
		icontent, _ := ioutil.ReadAll(ibytes)
		return icontent, nil
A
#2  
astaxie 已提交
153 154
	} else {
		if c.TplNames == "" {
155
			c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
A
#2  
astaxie 已提交
156
		}
157
		if RunMode == "dev" {
A
astaxie 已提交
158
			BuildTemplate(ViewsPath)
159
		}
A
astaxie 已提交
160
		ibytes := bytes.NewBufferString("")
A
astaxie 已提交
161
		if _, ok := BeeTemplates[c.TplNames]; !ok {
A
astaxie 已提交
162 163 164
			panic("can't find templatefile in the path:" + c.TplNames)
			return []byte{}, errors.New("can't find templatefile in the path:" + c.TplNames)
		}
A
astaxie 已提交
165
		err := BeeTemplates[c.TplNames].ExecuteTemplate(ibytes, c.TplNames, c.Data)
A
#2  
astaxie 已提交
166
		if err != nil {
A
astaxie 已提交
167
			Trace("template Execute err:", err)
168
			return nil, err
A
#2  
astaxie 已提交
169
		}
A
astaxie 已提交
170 171
		icontent, _ := ioutil.ReadAll(ibytes)
		return icontent, nil
A
#2  
astaxie 已提交
172
	}
A
astaxie 已提交
173
	return []byte{}, nil
A
#2  
astaxie 已提交
174 175 176 177 178 179
}

func (c *Controller) Redirect(url string, code int) {
	c.Ctx.Redirect(code, url)
}

A
fix #16  
astaxie 已提交
180
func (c *Controller) Abort(code string) {
181 182 183 184 185 186 187 188 189
	status, err := strconv.Atoi(code)
	if err == nil {
		c.Ctx.Abort(status, code)
	} else {
		c.Ctx.Abort(200, code)
	}
}

func (c *Controller) StopRun() {
A
astaxie 已提交
190
	panic(USERSTOPRUN)
A
fix #16  
astaxie 已提交
191 192
}

A
astaxie 已提交
193 194 195 196 197 198 199 200 201 202 203
func (c *Controller) UrlFor(endpoint string, values ...string) string {
	if len(endpoint) <= 0 {
		return ""
	}
	if endpoint[0] == '.' {
		return UrlFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...)
	} else {
		return UrlFor(endpoint, values...)
	}
}

A
astaxie 已提交
204
func (c *Controller) ServeJson(encoding ...bool) {
A
astaxie 已提交
205 206
	var hasIndent bool
	var hasencoding bool
207
	if RunMode == "prod" {
A
astaxie 已提交
208
		hasIndent = false
209
	} else {
A
astaxie 已提交
210
		hasIndent = true
211
	}
A
astaxie 已提交
212
	if len(encoding) > 0 && encoding[0] == true {
A
astaxie 已提交
213
		hasencoding = true
A
astaxie 已提交
214
	}
A
astaxie 已提交
215
	c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding)
A
#2  
astaxie 已提交
216 217
}

L
lw 已提交
218
func (c *Controller) ServeJsonp() {
A
astaxie 已提交
219
	var hasIndent bool
220
	if RunMode == "prod" {
A
astaxie 已提交
221
		hasIndent = false
222
	} else {
A
astaxie 已提交
223
		hasIndent = true
L
lw 已提交
224
	}
A
astaxie 已提交
225
	c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent)
L
lw 已提交
226 227
}

A
#2  
astaxie 已提交
228
func (c *Controller) ServeXml() {
A
astaxie 已提交
229
	var hasIndent bool
230
	if RunMode == "prod" {
A
astaxie 已提交
231
		hasIndent = false
232
	} else {
A
astaxie 已提交
233
		hasIndent = true
234
	}
A
astaxie 已提交
235
	c.Ctx.Output.Xml(c.Data["xml"], hasIndent)
A
#2  
astaxie 已提交
236 237 238
}

func (c *Controller) Input() url.Values {
239
	ct := c.Ctx.Request.Header.Get("Content-Type")
A
astaxie 已提交
240
	if strings.Contains(ct, "multipart/form-data") {
241 242 243 244
		c.Ctx.Request.ParseMultipartForm(MaxMemory) //64MB
	} else {
		c.Ctx.Request.ParseForm()
	}
A
#2  
astaxie 已提交
245 246
	return c.Ctx.Request.Form
}
X
xiemengjun 已提交
247

248 249 250 251
func (c *Controller) ParseForm(obj interface{}) error {
	return ParseForm(c.Input(), obj)
}

252 253 254 255
func (c *Controller) GetString(key string) string {
	return c.Input().Get(key)
}

Y
yecrane 已提交
256
func (c *Controller) GetStrings(key string) []string {
A
fix #87  
astaxie 已提交
257 258
	r := c.Ctx.Request
	if r.Form == nil {
Y
yecrane 已提交
259 260
		return []string{}
	}
A
fix #87  
astaxie 已提交
261 262 263 264 265
	vs := r.Form[key]
	if len(vs) > 0 {
		return vs
	}
	return []string{}
Y
yecrane 已提交
266 267
}

268 269 270 271 272 273 274 275
func (c *Controller) GetInt(key string) (int64, error) {
	return strconv.ParseInt(c.Input().Get(key), 10, 64)
}

func (c *Controller) GetBool(key string) (bool, error) {
	return strconv.ParseBool(c.Input().Get(key))
}

A
astaxie 已提交
276 277 278 279
func (c *Controller) GetFloat(key string) (float64, error) {
	return strconv.ParseFloat(c.Input().Get(key), 64)
}

A
astaxie 已提交
280 281 282 283
func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) {
	return c.Ctx.Request.FormFile(key)
}

A
astaxie 已提交
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
func (c *Controller) SaveToFile(fromfile, tofile string) error {
	file, _, err := c.Ctx.Request.FormFile(fromfile)
	if err != nil {
		return err
	}
	defer file.Close()
	f, err := os.OpenFile(tofile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	io.Copy(f, file)
	return nil
}

A
astaxie 已提交
299 300
func (c *Controller) StartSession() session.SessionStore {
	if c.CruSession == nil {
301
		c.CruSession = c.Ctx.Input.CruSession
A
astaxie 已提交
302 303
	}
	return c.CruSession
X
xiemengjun 已提交
304
}
A
session  
astaxie 已提交
305

306
func (c *Controller) SetSession(name interface{}, value interface{}) {
A
astaxie 已提交
307 308 309 310
	if c.CruSession == nil {
		c.StartSession()
	}
	c.CruSession.Set(name, value)
A
session  
astaxie 已提交
311 312
}

313
func (c *Controller) GetSession(name interface{}) interface{} {
A
astaxie 已提交
314 315 316 317
	if c.CruSession == nil {
		c.StartSession()
	}
	return c.CruSession.Get(name)
A
session  
astaxie 已提交
318 319
}

320
func (c *Controller) DelSession(name interface{}) {
A
astaxie 已提交
321 322 323 324
	if c.CruSession == nil {
		c.StartSession()
	}
	c.CruSession.Delete(name)
A
session  
astaxie 已提交
325
}
A
fix #87  
astaxie 已提交
326

327 328 329 330 331
func (c *Controller) SessionRegenerateID() {
	c.CruSession = GlobalSessions.SessionRegenerateId(c.Ctx.ResponseWriter, c.Ctx.Request)
	c.Ctx.Input.CruSession = c.CruSession
}

A
astaxie 已提交
332 333 334 335
func (c *Controller) DestroySession() {
	GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request)
}

A
fix #87  
astaxie 已提交
336
func (c *Controller) IsAjax() bool {
A
astaxie 已提交
337
	return c.Ctx.Input.IsAjax()
A
fix #87  
astaxie 已提交
338
}
A
astaxie 已提交
339

340 341 342 343 344 345 346 347
func (c *Controller) GetSecureCookie(Secret, key string) (string, bool) {
	val := c.Ctx.GetCookie(key)
	if val == "" {
		return "", false
	}

	parts := strings.SplitN(val, "|", 3)

A
astaxie 已提交
348 349 350 351
	if len(parts) != 3 {
		return "", false
	}

352 353 354 355 356 357 358 359 360 361
	vs := parts[0]
	timestamp := parts[1]
	sig := parts[2]

	h := hmac.New(sha1.New, []byte(Secret))
	fmt.Fprintf(h, "%s%s", vs, timestamp)

	if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
		return "", false
	}
A
astaxie 已提交
362
	res, _ := base64.URLEncoding.DecodeString(vs)
363 364 365
	return string(res), true
}

A
astaxie 已提交
366
func (c *Controller) SetSecureCookie(Secret, name, val string, age int64) {
367 368 369 370 371 372 373 374 375
	vs := base64.URLEncoding.EncodeToString([]byte(val))
	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
	h := hmac.New(sha1.New, []byte(Secret))
	fmt.Fprintf(h, "%s%s", vs, timestamp)
	sig := fmt.Sprintf("%02x", h.Sum(nil))
	cookie := strings.Join([]string{vs, timestamp, sig}, "|")
	c.Ctx.SetCookie(name, cookie, age, "/")
}

A
astaxie 已提交
376 377
func (c *Controller) XsrfToken() string {
	if c._xsrf_token == "" {
378 379
		token, ok := c.GetSecureCookie(XSRFKEY, "_xsrf")
		if !ok {
A
astaxie 已提交
380
			var expire int64
A
astaxie 已提交
381
			if c.XSRFExpire > 0 {
A
astaxie 已提交
382
				expire = int64(c.XSRFExpire)
A
astaxie 已提交
383
			} else {
A
astaxie 已提交
384
				expire = int64(XSRFExpire)
A
astaxie 已提交
385
			}
386
			token = getRandomString(15)
387
			c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire)
A
astaxie 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
		}
		c._xsrf_token = token
	}
	return c._xsrf_token
}

func (c *Controller) CheckXsrfCookie() bool {
	token := c.GetString("_xsrf")
	if token == "" {
		token = c.Ctx.Request.Header.Get("X-Xsrftoken")
	}
	if token == "" {
		token = c.Ctx.Request.Header.Get("X-Csrftoken")
	}
	if token == "" {
		c.Ctx.Abort(403, "'_xsrf' argument missing from POST")
A
astaxie 已提交
404
	} else if c._xsrf_token != token {
A
astaxie 已提交
405 406 407 408 409 410 411
		c.Ctx.Abort(403, "XSRF cookie does not match POST argument")
	}
	return true
}

func (c *Controller) XsrfFormHtml() string {
	return "<input type=\"hidden\" name=\"_xsrf\" value=\"" +
A
#230  
astaxie 已提交
412
		c._xsrf_token + "\"/>"
A
astaxie 已提交
413
}
A
fix #18  
astaxie 已提交
414 415 416 417 418 419 420

func (c *Controller) GoToFunc(funcname string) {
	if funcname[0] < 65 || funcname[0] > 90 {
		panic("GoToFunc should exported function")
	}
	c.gotofunc = funcname
}
421 422 423 424 425 426 427 428 429 430 431

//utils func for controller internal
func getRandomString(n int) string {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	rand.Read(bytes)
	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}
	return string(bytes)
}