controller.go 9.6 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 27
)

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

type ControllerInterface interface {
A
astaxie 已提交
42
	Init(ct *context.Context, childName string, app interface{})
A
#2  
astaxie 已提交
43 44 45 46 47 48 49 50 51 52 53 54
	Prepare()
	Get()
	Post()
	Delete()
	Put()
	Head()
	Patch()
	Options()
	Finish()
	Render() error
}

A
astaxie 已提交
55
func (c *Controller) Init(ctx *context.Context, childName string, app interface{}) {
A
#2  
astaxie 已提交
56 57 58
	c.Data = make(map[interface{}]interface{})
	c.Layout = ""
	c.TplNames = ""
A
astaxie 已提交
59
	c.ChildName = childName
A
#2  
astaxie 已提交
60 61
	c.Ctx = ctx
	c.TplExt = "tpl"
A
astaxie 已提交
62
	c.AppController = app
A
#2  
astaxie 已提交
63 64 65 66 67 68 69
}

func (c *Controller) Prepare() {

}

func (c *Controller) Finish() {
A
astaxie 已提交
70 71 72

}

A
#2  
astaxie 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
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 已提交
102 103 104 105 106
	rb, err := c.RenderBytes()

	if err != nil {
		return err
	} else {
A
astaxie 已提交
107 108
		c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
		c.Ctx.Output.Body(rb)
A
astaxie 已提交
109 110 111 112
	}
	return nil
}

A
astaxie 已提交
113 114 115 116 117
func (c *Controller) RenderString() (string, error) {
	b, e := c.RenderBytes()
	return string(b), e
}

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

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

A
fix #16  
astaxie 已提交
174
func (c *Controller) Abort(code string) {
175 176 177 178 179 180 181 182 183
	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 已提交
184
	panic("StopRun")
A
fix #16  
astaxie 已提交
185 186
}

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

L
lw 已提交
212
func (c *Controller) ServeJsonp() {
A
astaxie 已提交
213
	var hasIndent bool
214
	if RunMode == "prod" {
A
astaxie 已提交
215
		hasIndent = false
216
	} else {
A
astaxie 已提交
217
		hasIndent = true
L
lw 已提交
218
	}
A
astaxie 已提交
219
	c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent)
L
lw 已提交
220 221
}

A
#2  
astaxie 已提交
222
func (c *Controller) ServeXml() {
A
astaxie 已提交
223
	var hasIndent bool
224
	if RunMode == "prod" {
A
astaxie 已提交
225
		hasIndent = false
226
	} else {
A
astaxie 已提交
227
		hasIndent = true
228
	}
A
astaxie 已提交
229
	c.Ctx.Output.Xml(c.Data["xml"], hasIndent)
A
#2  
astaxie 已提交
230 231 232
}

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

242 243 244 245
func (c *Controller) ParseForm(obj interface{}) error {
	return ParseForm(c.Input(), obj)
}

246 247 248 249
func (c *Controller) GetString(key string) string {
	return c.Input().Get(key)
}

Y
yecrane 已提交
250
func (c *Controller) GetStrings(key string) []string {
A
fix #87  
astaxie 已提交
251 252
	r := c.Ctx.Request
	if r.Form == nil {
Y
yecrane 已提交
253 254
		return []string{}
	}
A
fix #87  
astaxie 已提交
255 256 257 258 259
	vs := r.Form[key]
	if len(vs) > 0 {
		return vs
	}
	return []string{}
Y
yecrane 已提交
260 261
}

262 263 264 265 266 267 268 269
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 已提交
270 271 272 273
func (c *Controller) GetFloat(key string) (float64, error) {
	return strconv.ParseFloat(c.Input().Get(key), 64)
}

A
astaxie 已提交
274 275 276 277
func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) {
	return c.Ctx.Request.FormFile(key)
}

A
astaxie 已提交
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
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 已提交
293 294
func (c *Controller) StartSession() session.SessionStore {
	if c.CruSession == nil {
295
		c.CruSession = c.Ctx.Input.CruSession
A
astaxie 已提交
296 297
	}
	return c.CruSession
X
xiemengjun 已提交
298
}
A
session  
astaxie 已提交
299

300
func (c *Controller) SetSession(name interface{}, value interface{}) {
A
astaxie 已提交
301 302 303 304
	if c.CruSession == nil {
		c.StartSession()
	}
	c.CruSession.Set(name, value)
A
session  
astaxie 已提交
305 306
}

307
func (c *Controller) GetSession(name interface{}) interface{} {
A
astaxie 已提交
308 309 310 311
	if c.CruSession == nil {
		c.StartSession()
	}
	return c.CruSession.Get(name)
A
session  
astaxie 已提交
312 313
}

314
func (c *Controller) DelSession(name interface{}) {
A
astaxie 已提交
315 316 317 318
	if c.CruSession == nil {
		c.StartSession()
	}
	c.CruSession.Delete(name)
A
session  
astaxie 已提交
319
}
A
fix #87  
astaxie 已提交
320

321 322 323 324 325
func (c *Controller) SessionRegenerateID() {
	c.CruSession = GlobalSessions.SessionRegenerateId(c.Ctx.ResponseWriter, c.Ctx.Request)
	c.Ctx.Input.CruSession = c.CruSession
}

A
astaxie 已提交
326 327 328 329
func (c *Controller) DestroySession() {
	GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request)
}

A
fix #87  
astaxie 已提交
330
func (c *Controller) IsAjax() bool {
A
astaxie 已提交
331
	return c.Ctx.Input.IsAjax()
A
fix #87  
astaxie 已提交
332
}
A
astaxie 已提交
333

334 335 336 337 338 339 340 341
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 已提交
342 343 344 345
	if len(parts) != 3 {
		return "", false
	}

346 347 348 349 350 351 352 353 354 355
	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 已提交
356
	res, _ := base64.URLEncoding.DecodeString(vs)
357 358 359
	return string(res), true
}

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

func (c *Controller) GoToFunc(funcname string) {
	if funcname[0] < 65 || funcname[0] > 90 {
		panic("GoToFunc should exported function")
	}
	c.gotofunc = funcname
}
415 416 417 418 419 420 421 422 423 424 425

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