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
	Prepare()
	Get()
	Post()
	Delete()
	Put()
	Head()
	Patch()
	Options()
	Finish()
	Render() error
58 59
	XsrfToken() string
	CheckXsrfCookie() bool
A
#2  
astaxie 已提交
60 61
}

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

func (c *Controller) Prepare() {

}

func (c *Controller) Finish() {
A
astaxie 已提交
78 79 80

}

A
#2  
astaxie 已提交
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 108 109
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 已提交
110 111 112 113 114
	rb, err := c.RenderBytes()

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

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

A
astaxie 已提交
126
func (c *Controller) RenderBytes() ([]byte, error) {
A
#2  
astaxie 已提交
127 128 129
	//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 == "" {
130
			c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
A
#2  
astaxie 已提交
131
		}
132
		if RunMode == "dev" {
A
astaxie 已提交
133
			BuildTemplate(ViewsPath)
134
		}
A
#2  
astaxie 已提交
135
		newbytes := bytes.NewBufferString("")
A
astaxie 已提交
136
		if _, ok := BeeTemplates[c.TplNames]; !ok {
A
astaxie 已提交
137 138 139
			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 已提交
140
		err := BeeTemplates[c.TplNames].ExecuteTemplate(newbytes, c.TplNames, c.Data)
A
astaxie 已提交
141
		if err != nil {
A
astaxie 已提交
142
			Trace("template Execute err:", err)
143
			return nil, err
A
astaxie 已提交
144
		}
A
#2  
astaxie 已提交
145 146
		tplcontent, _ := ioutil.ReadAll(newbytes)
		c.Data["LayoutContent"] = template.HTML(string(tplcontent))
A
astaxie 已提交
147
		ibytes := bytes.NewBufferString("")
A
astaxie 已提交
148
		err = BeeTemplates[c.Layout].ExecuteTemplate(ibytes, c.Layout, c.Data)
A
#2  
astaxie 已提交
149 150
		if err != nil {
			Trace("template Execute err:", err)
151
			return nil, err
A
#2  
astaxie 已提交
152
		}
A
astaxie 已提交
153 154
		icontent, _ := ioutil.ReadAll(ibytes)
		return icontent, nil
A
#2  
astaxie 已提交
155 156
	} else {
		if c.TplNames == "" {
157
			c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
A
#2  
astaxie 已提交
158
		}
159
		if RunMode == "dev" {
A
astaxie 已提交
160
			BuildTemplate(ViewsPath)
161
		}
A
astaxie 已提交
162
		ibytes := bytes.NewBufferString("")
A
astaxie 已提交
163
		if _, ok := BeeTemplates[c.TplNames]; !ok {
A
astaxie 已提交
164 165 166
			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 已提交
167
		err := BeeTemplates[c.TplNames].ExecuteTemplate(ibytes, c.TplNames, c.Data)
A
#2  
astaxie 已提交
168
		if err != nil {
A
astaxie 已提交
169
			Trace("template Execute err:", err)
170
			return nil, err
A
#2  
astaxie 已提交
171
		}
A
astaxie 已提交
172 173
		icontent, _ := ioutil.ReadAll(ibytes)
		return icontent, nil
A
#2  
astaxie 已提交
174
	}
A
astaxie 已提交
175
	return []byte{}, nil
A
#2  
astaxie 已提交
176 177 178 179 180 181
}

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

A
fix #16  
astaxie 已提交
182
func (c *Controller) Abort(code string) {
183 184 185 186 187 188 189 190 191
	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 已提交
192
	panic(USERSTOPRUN)
A
fix #16  
astaxie 已提交
193 194
}

A
astaxie 已提交
195 196 197 198 199 200 201 202 203 204 205
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 已提交
206
func (c *Controller) ServeJson(encoding ...bool) {
A
astaxie 已提交
207 208
	var hasIndent bool
	var hasencoding bool
209
	if RunMode == "prod" {
A
astaxie 已提交
210
		hasIndent = false
211
	} else {
A
astaxie 已提交
212
		hasIndent = true
213
	}
A
astaxie 已提交
214
	if len(encoding) > 0 && encoding[0] == true {
A
astaxie 已提交
215
		hasencoding = true
A
astaxie 已提交
216
	}
A
astaxie 已提交
217
	c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding)
A
#2  
astaxie 已提交
218 219
}

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

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

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

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

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

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

270 271 272 273 274 275 276 277
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 已提交
278 279 280 281
func (c *Controller) GetFloat(key string) (float64, error) {
	return strconv.ParseFloat(c.Input().Get(key), 64)
}

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

A
astaxie 已提交
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
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 已提交
301 302
func (c *Controller) StartSession() session.SessionStore {
	if c.CruSession == nil {
303
		c.CruSession = c.Ctx.Input.CruSession
A
astaxie 已提交
304 305
	}
	return c.CruSession
X
xiemengjun 已提交
306
}
A
session  
astaxie 已提交
307

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

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

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

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

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

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

342 343 344 345 346 347 348 349
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 已提交
350 351 352 353
	if len(parts) != 3 {
		return "", false
	}

354 355 356 357 358 359 360 361 362 363
	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 已提交
364
	res, _ := base64.URLEncoding.DecodeString(vs)
365 366 367
	return string(res), true
}

A
astaxie 已提交
368
func (c *Controller) SetSecureCookie(Secret, name, val string, age int64) {
369 370 371 372 373 374 375 376 377
	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 已提交
378 379
func (c *Controller) XsrfToken() string {
	if c._xsrf_token == "" {
380 381
		token, ok := c.GetSecureCookie(XSRFKEY, "_xsrf")
		if !ok {
A
astaxie 已提交
382
			var expire int64
A
astaxie 已提交
383
			if c.XSRFExpire > 0 {
A
astaxie 已提交
384
				expire = int64(c.XSRFExpire)
A
astaxie 已提交
385
			} else {
A
astaxie 已提交
386
				expire = int64(XSRFExpire)
A
astaxie 已提交
387
			}
388
			token = getRandomString(15)
389
			c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire)
A
astaxie 已提交
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
		}
		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 已提交
406
	} else if c._xsrf_token != token {
A
astaxie 已提交
407 408 409 410 411 412 413
		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 已提交
414
		c._xsrf_token + "\"/>"
A
astaxie 已提交
415
}
A
fix #18  
astaxie 已提交
416

417 418
func (c *Controller) GetControllerAndAction() (controllerName, actionName string) {
	return c.controllerName, c.actionName
A
fix #18  
astaxie 已提交
419
}
420 421 422 423 424 425 426 427 428 429 430

//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)
}