1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-11 08:01:01 +00:00

1151 Commits

Author SHA1 Message Date
0692f92890 Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-09-02 14:42:02 +08:00
41adcf9966 change the version 1.4.0 to 1.4.1 2014-09-02 14:41:14 +08:00
e6b42a4070 Merge pull request #787 from francoishill/patch-21
Proposal for adding a ":" for stack trace printout
2014-08-31 08:52:32 +08:00
bc4780091b Proposal for adding a ":" for stack trace printout
Mainly useful for Sublime users with goto anything.
2014-08-30 21:27:21 +02:00
d8614e80e7 beego: update the Url to Path 2014-08-30 22:22:23 +08:00
c83a2a0925 modify the comments 2014-08-28 10:21:32 +08:00
50a21d60c1 apiauth add more comments & improve 2014-08-28 10:05:02 +08:00
5a087b28d2 aws api auth plugins 2014-08-28 00:25:50 +08:00
9b40271878 fix the less ` 2014-08-27 15:53:08 +08:00
770dc702f0 Merge pull request #782 from francoishill/patch-19
Extra field if username is empty
2014-08-27 15:36:07 +08:00
e146100a23 Merge pull request #783 from francoishill/patch-20
Typo in the printout of some level's prefixes
2014-08-27 15:35:31 +08:00
2d94f7797b Type in the printout of each level's prefix 2014-08-26 14:03:03 +02:00
61ce608847 Update smtp.go 2014-08-26 12:47:05 +02:00
2fe559701c fix the import 2014-08-26 15:43:18 +08:00
37fe175c26 Merge pull request #776 from devYu/master
udpate ini.go
2014-08-26 14:43:20 +08:00
fcc9d8c45f Merge pull request #778 from francoishill/patch-15
Update file.go
2014-08-26 14:42:52 +08:00
e51a9d6481 Merge pull request #779 from francoishill/patch-17
Print error if occurred in WriteMsg
2014-08-26 14:42:05 +08:00
e9a1daa3ee Merge pull request #780 from francoishill/patch-18
Allow mail with self-signed certificates
2014-08-26 14:41:36 +08:00
44ea260db1 Allow mail with self-signed certificates
For more information, refer to https://groups.google.com/forum/#!topic/golang-nuts/c9zEiH6ixyw
2014-08-26 06:52:18 +02:00
1d1ad69954 Print error if occurred in WriteMsg 2014-08-26 06:37:35 +02:00
59773dfabe Update file.go
New pull request for "Panic sometimes occurs at time 00h00 on windows, then the app crashes."
2014-08-26 06:25:59 +02:00
ccb61f0416 gofmt
Signed-off-by: devYu <devysq@gmail.com>
2014-08-25 21:31:53 +08:00
14629c214b 优化GetData
1. 去掉重复的ToLower
2. getData内部统一ToLower
3.调整getData中对空字符串判断位置
====
4. 待确定:在getData中是否有必要进行lock操作
2014-08-25 20:14:14 +08:00
a3888cef7f fix the comments for the \d 2014-08-25 19:48:02 +08:00
f6a1a6c9bf Merge pull request #773 from lei-cao/develop
Added data table admin ui
2014-08-25 07:32:07 +08:00
38ee43701d remove http: 2014-08-25 01:40:23 +08:00
421b796f1a Added data table admin ui 2014-08-25 01:37:11 +08:00
771179a3c6 Merge pull request #769 from JessonChan/develop
improve and typo fixed
2014-08-23 09:46:43 +08:00
c07b1d881b typo fixed 2014-08-23 07:07:12 +08:00
d8f2b05e08 improve the code and delete NO NEED URL CHECK 2014-08-23 07:02:47 +08:00
7b2fe824d5 typo fixed 2014-08-23 06:48:40 +08:00
02301caac1 modify desc 2014-08-23 06:47:42 +08:00
98c2307763 Merge pull request #767 from JessonChan/develop
bug fixed
2014-08-22 22:02:40 +08:00
485c2e865c bug fixed 2014-08-22 17:43:05 +08:00
b390667374 Merge remote-tracking branch 'upstream/develop' into develop
Conflicts:
	httplib/httplib.go
2014-08-22 17:39:57 +08:00
f684de2385 fixed bug-request dump too early 2014-08-22 17:12:46 +08:00
35b4022ee0 httplib: set the default proto 2014-08-22 17:03:56 +08:00
77294a5881 utils: fix the SliceIntersect 2014-08-22 16:50:07 +08:00
7b39bd7042 refactor 2014-08-22 16:43:42 +08:00
01e4084587 toolbox: fix the go routine asleep 2014-08-22 13:56:36 +08:00
bf429a3a20 Merge pull request #762 from pdf/pointer_field_support
Add support for basic type pointer fields
2014-08-22 13:41:58 +08:00
f8ff79d77d Merge pull request #764 from smallfish/master
Update README
2014-08-22 13:41:01 +08:00
e9487d3571 Update README 2014-08-22 13:32:54 +08:00
d7c3727f96 Add support for basic type pointer fields
Allows models like:

```
type User struct {
	Id    int64
	Name  string
	Email *string `orm:"null"`
}
```

This helps a lot when doing JSON marshalling/unmarshalling.

Pointer fields should always be declared with the NULL orm tag for sanity, this
probably requires documentation.
2014-08-22 14:25:32 +10:00
03eb1fc104 toolbox: add notify when add & delete task 2014-08-21 15:56:41 +08:00
0a967875da Merge pull request #757 from smallfish/develop
Add new function SetBasicAuth, and update README
2014-08-21 09:01:45 +08:00
6ae8bc1a16 Update README 2014-08-21 00:08:08 +08:00
e70537f8b3 Update README 2014-08-21 00:03:03 +08:00
9a583323a8 Add SetBasicAuth function for HTTP Auth 2014-08-20 23:36:58 +08:00
ff9c8d94e6 Merge pull request #753 from smallfish/develop
format comments
2014-08-20 16:40:26 +08:00
7f977a0c8c beego: change the colour 2014-08-20 11:54:25 +08:00
aaabeff44f change the route info 2014-08-20 10:59:38 +08:00
f85ac088c3 format comments 2014-08-19 16:48:30 +08:00
26da23266a Merge branch 'master' into develop 2014-08-19 09:49:07 +08:00
80274684e0 context: redirect should writer to response instantly 2014-08-19 09:48:21 +08:00
be005f9774 Merge pull request #750 from smallfish/develop
Update httplib support read data from response buffer, add some testcase...
2014-08-18 21:33:02 +08:00
c16b7be9ac rollback the ToFile func implement, and add testcase 2014-08-18 21:29:45 +08:00
de87529387 Update httplib support read data from response buffer, add some testcases 2014-08-18 21:01:49 +08:00
c4fa17921e comments for godocs 2014-08-18 16:44:40 +08:00
724137e605 Merge branch 'master' into develop 2014-08-18 16:42:16 +08:00
a144769515 update the documents & comments 2014-08-18 16:41:43 +08:00
05089be427 Merge pull request #749 from smallfish/master
rename SetAgent and ToXML, and update some testcase
2014-08-18 15:07:11 +08:00
7668c54d05 update testcase for httplib 2014-08-18 15:03:34 +08:00
86752a55b6 rename SetAgent and ToXML 2014-08-18 15:03:10 +08:00
e07d780dcf update the router info 2014-08-18 15:00:27 +08:00
e566322643 beego: change the version from 1.4.0 to 1.3.2 2014-08-18 14:46:43 +08:00
51ee1e77c2 beego: close the file when finish init 2014-08-18 14:42:17 +08:00
986e91b7d6 beego: update the debug info rules 2014-08-18 14:35:43 +08:00
8b8638c507 Merge pull request #748 from smallfish/master
Remove some unnecessary code
2014-08-17 21:36:10 +08:00
d27c5c8daf Remove some unnecessary code 2014-08-17 21:13:29 +08:00
86121deac4 Merge pull request #747 from francoishill/patch-12
Another change to match with logs/log.go (and RFC 5424 specs)
2014-08-16 10:10:16 +08:00
6b02e1e9d4 Merge pull request #746 from francoishill/patch-11
To match with logs/log.go (and RFC 5424 specs)
2014-08-16 10:09:32 +08:00
83696d95c3 Merge pull request #745 from francoishill/patch-10
Makes more sense to Use Debug instead of Info?
2014-08-16 09:40:01 +08:00
7be8114616 To match with logs/log.go (and RFC 5424 specs) 2014-08-15 21:30:59 +02:00
1dbbb89ad1 To match with logs/log.go (and RFC 5424 specs) 2014-08-15 21:12:57 +02:00
75904effd9 Makes more sense to Use Debug instead of Info? 2014-08-15 21:11:16 +02:00
cd9e614a71 plugins: basic auth & cors 2014-08-15 17:15:20 +08:00
c1234e7c6d fix the responseWriter 2014-08-15 15:24:46 +08:00
b611b9dab6 change the adminui link 2014-08-15 15:19:35 +08:00
cad3da337a toolbox: fix the program name 2014-08-15 15:17:11 +08:00
3969cd3b40 toolbox: improve the profile 2014-08-15 15:09:59 +08:00
f4867aad5a Merge pull request #744 from lei-cao/develop
ajax refresh gc message
2014-08-15 14:46:37 +08:00
17006cfb26 ajax refresh gc message 2014-08-15 14:24:55 +08:00
7d1b03ee5d toolbox: fix the test case 2014-08-15 00:14:30 +08:00
84a4379f0d Merge pull request #739 from lei-cao/develop
Added the UI for Admin monitor page
2014-08-14 23:57:09 +08:00
ab71201c96 Merge pull request #741 from smallfish/master
Update ctx.WriteString multiple buf output
2014-08-14 23:56:20 +08:00
c347dd9e7b Fix the comments and deleted the println 2014-08-14 23:50:15 +08:00
75e2611cc4 Update ctx.WriteString multiple buf output 2014-08-14 21:37:52 +08:00
d314d12c77 Added the UI for Admin monitor page 2014-08-14 17:35:23 +08:00
564c3bbeb5 migration: update the debug error 2014-08-14 13:44:05 +08:00
31f0ac4ce3 migration: update the params orders 2014-08-14 13:41:54 +08:00
57a9670b0a migration: reset the up state sql 2014-08-14 13:37:48 +08:00
14cd9e51ac revert the sort map for reset 2014-08-14 11:54:15 +08:00
886bb782a5 sort the reset 2014-08-14 11:44:10 +08:00
d2119f715c update the migrations 2014-08-14 11:39:59 +08:00
f98b1810ab update the reset 2014-08-14 10:56:49 +08:00
55a7711017 migration: skip reset 2014-08-14 10:19:55 +08:00
436edda926 Merge branch 'master' into develop 2014-08-13 20:39:14 +08:00
1f1190fbc3 Merge pull request #738 from smallfish/master
Add CustomAbort() for Controller, support status code and body
2014-08-13 20:16:36 +08:00
20463fa725 Add CustomAbort() for Controller, support status code and body 2014-08-13 17:26:22 +08:00
ae37f95239 migration: delete the unique key for name 2014-08-13 16:42:16 +08:00
da6726f3da Merge pull request #737 from ZhengYang/develop
change time format for migration module
2014-08-13 16:37:13 +08:00
32469cd69d change time format 2014-08-13 16:09:13 +08:00
e572f45296 update the error output 2014-08-13 14:50:32 +08:00
efcaa3d934 update the migration database time format 2014-08-13 11:16:19 +08:00
5ecfe0c335 beego hook change the path & fix the migration bug 2014-08-13 10:43:05 +08:00
d325a66fee update the error info 2014-08-12 16:35:59 +08:00
fe9017c819 update the description 2014-08-12 15:50:28 +08:00
b97279a74f update the migration 2014-08-12 15:49:30 +08:00
6a78898bb1 beego: fix the tree for addtree & add testcase 2014-08-12 15:09:12 +08:00
f201859fa7 beego: fix the addTree 2014-08-12 00:15:39 +08:00
52fdfc5665 beego: fix the tree addTree for regexp 2014-08-12 00:02:27 +08:00
8ed6d06572 fix the regexps bugs 2014-08-11 22:40:55 +08:00
b5a2347e1d Merge pull request #733 from liulixiang1988/develop
improve the 'geturl'
2014-08-11 22:25:53 +08:00
118e07158e improve the 'geturl'
If we have a url mapping like this:
`beego.Router(“/test”, &controllers.WeightController{},
"get:GetDetails”)`
when u use `UrlFor(“WeightController.GetDetails”, “foo”, 1, “bar”, 2 `,
it should return `/test?foo=1&bar=2` rather than `/test`.
2014-08-11 22:19:59 +08:00
7a376c32be delete forms 2014-08-11 22:13:57 +08:00
3d74a1a436 make the getconfig public
// Getconfig throw the Runmode
// [dev]
// name = astaixe
// IsEnable = false
// [prod]
// name = slene
// IsEnable = true
//
// usage:
// GetConfig("string", "name")
// GetConfig("bool", "IsEnable")
2014-08-11 22:13:18 +08:00
00eac0e4cb Merge pull request #729 from francoishill/patch-9
Update captcha.go
2014-08-11 21:41:22 +08:00
58ac0d5ea4 Update captcha.go
Captcha must be deleted if the user entered a "challenge" with a different length than the captcha.
2014-08-09 15:35:29 +02:00
2773fda883 session:change the driver from beego to bradfitz 2014-08-08 16:43:39 +08:00
99c03a2b9c fix the nil judge 2014-08-08 13:16:51 +08:00
b1b4dbb0e4 fix the nil judge 2014-08-08 12:02:44 +08:00
77e1f26dd4 Merge pull request #728 from nizsheanez/null_pointer_panic_improve
[orm] improve null pointer panic message
2014-08-08 09:01:51 +08:00
2820f630c8 config: add more method
DefaultString(key string, defaultval string) string      // support
section::key type in key string when using ini and json type;
Int,Int64,Bool,Float,DIY are same.
	DefaultStrings(key string, defaultval []string) []string //get string
slice
	DefaultInt(key string, defaultval int) int
	DefaultInt64(key string, defaultval int64) int64
	DefaultBool(key string, defaultval bool) bool
	DefaultFloat(key string, defaultval float64) float64
	DIY(key string) (interface{}, error)
	GetSection(section string) (map[string]string, error)
	SaveConfigFile(filename string) error
2014-08-07 17:24:21 +08:00
1f6c5599aa migration: version 1 2014-08-07 16:30:28 +08:00
df8c73b23a improve null pointer panic message 2014-08-07 12:14:10 +07:00
ea6982fcea beego: template fund when the start> len(bt) 2014-08-05 23:52:06 +08:00
a3f40234ca Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-08-05 08:56:34 +08:00
2c420573d4 fix #703 2014-08-05 08:56:04 +08:00
c0d3cc6fc5 beego: fix the domain string 2014-08-05 08:55:46 +08:00
3f7ecea089 beego: Display function move from toolbox to utils 2014-08-04 20:52:18 +08:00
f4147058fc fix when delete the commentsRouter.go 2014-08-04 17:39:14 +08:00
1fb24aca34 beego: commentsrouter use the workPath fix #708 2014-08-04 17:34:52 +08:00
885c0678ff move filter wrong http method 2014-08-04 16:21:34 +08:00
d7a5281bda session: support cookie domain 2014-08-04 16:21:06 +08:00
474a16a7a0 beego: improve the static file server 2014-08-04 15:31:27 +08:00
1d36b19cab Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-08-04 15:00:42 +08:00
deed251794 Merge pull request #720 from MicroMoon/patch-1
revise the comment of SliceIntersect
2014-08-02 23:28:08 +08:00
22ba252c8f fix the comment of SliceIntersect
The implementations of SliceDiff and SliceIntersect are the same.
However, the comments of them are opposite.
The former is (slice1 - slice2) while the latter is (slice2- slice1)
2014-08-02 17:54:57 +08:00
289f050c04 cache: fix comments the return err 2014-08-02 10:12:03 +08:00
bbd31131a4 beego:parse judge weather the commentsRouter exist 2014-08-02 10:11:45 +08:00
008ae39ff6 Merge branch 'master' into develop 2014-08-01 17:04:22 +08:00
0836b9e13f fix #718 2014-08-01 17:03:28 +08:00
7a39a3c52f middleware: error page set the content-type 2014-08-01 16:46:51 +08:00
509af636b1 Merge pull request #716 from 4eek/docfix-validations-HasErrors
Fixed docs: HasErrors is a method not a variable
2014-07-29 22:04:26 +08:00
daf8b1101f Fixed docs: HasErrors is a method not a variable 2014-07-29 15:05:04 +02:00
428aec1c24 Merge pull request #715 from MicroMoon/develop
revise a comment
2014-07-28 09:52:26 +08:00
a804d242d9 revise a comment
If it gets a msg from the buffer chan, it will write the message to
outputs.
2014-07-27 07:13:17 -04:00
7ddd20340b orm: delete the old docs 2014-07-26 23:25:59 +08:00
e7fcb824c1 utils: fix the safemap Items 2014-07-26 23:25:51 +08:00
ee9749d640 beego:fix #685
move XsrfToken& CheckXsrfCookie to context
2014-07-24 23:12:21 +08:00
d7090689e8 cache: change the memcache &redis driver
change the memcache to the newest
2014-07-24 22:54:56 +08:00
52d153da87 Merge pull request #710 from francoishill/patch-8
Update smtp.go (develop branch)
2014-07-22 13:45:24 +08:00
6ce55e8884 Update smtp.go (develop branch)
For mail servers that do not require Authentication we must pass NIL for the SendMail parameter 2 (the auth parameter). Otherwise it fails to send the mail.
2014-07-22 07:21:47 +02:00
91ee42ceeb beego:update the Abort Status 2014-07-18 15:38:29 +08:00
d17f107fc4 beego: fix #702 auto render 2014-07-18 13:29:54 +08:00
ae8bb8ce82 Merge pull request #701 from fuxiaohei/develop
code style simplify
2014-07-17 16:51:01 +08:00
38188098c5 fix testing fail 2014-07-17 16:48:10 +08:00
c372328f88 code style simplify 2014-07-17 16:22:52 +08:00
a6ced64441 code style simplify 2014-07-17 16:22:41 +08:00
69094b2786 remove koding link 2014-07-17 16:05:17 +08:00
08397fef45 Merge pull request #700 from fuxiaohei/develop
code style simplify
2014-07-17 15:58:37 +08:00
84da1c924a code style simplify 2014-07-17 15:56:06 +08:00
f733b5707a code style simplify 2014-07-17 15:49:40 +08:00
9d0798edc6 Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-07-17 15:46:28 +08:00
1858f3073b session: fix #688 2014-07-16 23:05:38 +08:00
e6d6419a65 beego: static file support robots.txt 2014-07-16 22:27:53 +08:00
e52386b52d code style simplify 2014-07-15 10:01:26 +08:00
024817aacb Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-07-15 09:47:49 +08:00
9d0ad3f974 code style simplify 2014-07-13 18:11:13 +08:00
6357c88d97 Merge pull request #694 from fuxiaohei/develop
code style simplify
2014-07-13 18:10:51 +08:00
9457e61a0c code style simplify 2014-07-12 22:12:06 +08:00
f657509a42 Merge pull request #693 from fuxiaohei/develop
code style simplify
2014-07-12 19:33:32 +08:00
20e05a3908 code style simplify 2014-07-12 16:03:14 +08:00
77c40e6f7b code style simplify 2014-07-12 15:51:47 +08:00
ea2ed90ab0 Merge remote-tracking branch 'remotes/astaxie/develop' into develop 2014-07-12 15:32:37 +08:00
15759f60ed Merge pull request #692 from fuxiaohei/develop
code style simplify
2014-07-12 14:18:30 +08:00
0e3e22efbe merge update 2014-07-12 09:57:43 +08:00
a906bf1174 code style simplify 2014-07-12 09:52:15 +08:00
a6379481cf Merge pull request #691 from FGM/logger-rfc5424
Issue #682: convert logs package to RFC5424 logging levels.
2014-07-11 21:35:56 +08:00
7d09ac252a Issue #682: convert logs package to RFC5424 logging levels. 2014-07-11 11:15:34 +02:00
0ff058bd25 code style simplify 2014-07-11 10:01:49 +08:00
dbe75f90d5 Merge pull request #689 from fuxiaohei/develop
code style simplify
2014-07-11 09:55:56 +08:00
ca1fd96ea5 Merge pull request #687 from fuxiaohei/develop
code style simplify
2014-07-10 16:48:04 +08:00
0b1b121db8 code style simplify 2014-07-10 13:38:05 +08:00
8683ee7c67 code style simplify 2014-07-10 13:28:54 +08:00
007db805be code style simplify 2014-07-10 13:04:18 +08:00
19c3a5b41c beego: improve the router debug infomation 2014-07-09 09:38:36 +08:00
e635e274d4 Merge branch 'master' into develop 2014-07-09 08:43:37 +08:00
46cde6e579 Merge pull request #683 from chrisport/develop
beego/context: Fix ignored Header in case SetStatus has been called before
2014-07-09 08:39:54 +08:00
3bb4d6f013 beego/context: Fix ignored Header in case SetStatus has been called before 2014-07-08 23:24:47 +03:00
cec151fda7 Merge pull request #674 from reterVision/master
Make redis cache timeout not trivial.
2014-07-07 09:49:54 +08:00
b6c4e27a75 Adjust the action order in Put function. 2014-07-05 22:30:51 +08:00
2933d1fedd Make redis cache timeout not trivial.
- Instead of using HASH for all the caches, use HASH + normal KEYs.
- HASH being used as a collection of all KEYs, this is useful when
  application wants to clear all keys.
2014-07-05 22:27:31 +08:00
cbffcaa7a8 Merge pull request #673 from sandysong/master
修正Detect Engine错误
2014-07-04 17:58:26 +08:00
707c951302 修正Detect Engine错误 2014-07-04 16:57:37 +08:00
fefd8ddb5b beego: update licence& fix #669 2014-07-03 23:40:21 +08:00
14dee37a21 beego: autorouter params 2014-07-01 16:55:23 +08:00
17a9c3c3a9 ORM:revert default value 2014-07-01 09:30:08 +08:00
8946f816f9 orm:change the models_test default value 2014-07-01 00:30:05 +08:00
36ba1e49b1 beego:change to 1.3.2 2014-07-01 00:06:35 +08:00
34936dde35 Merge branch 'master' into develop 2014-06-30 23:50:45 +08:00
aa004ed973 beego:form render textarea 2014-06-30 23:49:55 +08:00
53353fce56 beego:fix the :id & * mixed router 2014-06-30 23:49:11 +08:00
f92ce9af96 Merge pull request #654 from wb14123/cmd_default
Generate default value while run ORM cmd tool
2014-06-30 21:36:10 +08:00
8b021c8ea1 Merge pull request #664 from kioopi/renderform-textarea
Makes RenderForm use textarea-element when form type is `textarea`
2014-06-30 21:14:27 +08:00
34572193c6 Adds html5 input types to valid intput types of RenderForm. 2014-06-30 10:38:32 +02:00
4dde2c59ff fix the parser.go lastupdate 2014-06-30 15:57:36 +08:00
c83c17d917 Merge pull request #660 from ljyf5593/patch-1
beego: fix #657
2014-06-30 12:06:24 +08:00
4994d36b66 templateform.RenderForm now renders textarea
When RenderForm encounters a field with the structTag `form` value
type `textarea` it renders an actual <textarea> html tag.
2014-06-29 20:49:56 +02:00
a991b9dcde Removes unused FormType map from templatefunc 2014-06-29 20:49:35 +02:00
3fe9e6a28a extract func parseFormTag from templatefunc.RenderForm
Extracted a func `parseFormTag` that takes a reflect.StructField
and returns the different positional parts of the `form` structTag
with default values as documented in
http://beego.me/docs/mvc/view/view.md#renderform

This makes RenderForm shorter and makes it possible to test the
parsing separately.
2014-06-29 20:49:08 +02:00
62e9c89010 middleware: support i18n 2014-06-27 17:53:53 +08:00
ac96c2b15e beego: fix #657
路由地址不区分大小写问题
2014-06-27 11:06:29 +08:00
0f170a80da update the comments fix #658 2014-06-25 10:39:37 +08:00
9c5348f690 beego: autoroute 2014-06-23 15:52:30 +08:00
90f91b10c5 beego: change to beego 1.3.1 2014-06-23 15:31:51 +08:00
a5a6a30744 beego: fix the router rule for * 2014-06-23 15:28:53 +08:00
1f6e689e5d beego: fix #652 2014-06-23 15:28:29 +08:00
7b7a95677a Generate default value while run ORM cmd tool 2014-06-22 10:52:14 +08:00
af4f153830 beego: update the router rule for *
* not match the empty route
2014-06-21 11:44:24 +08:00
469f283b68 beego:fix captcha filter router 2014-06-19 20:29:36 +08:00
085c362ffb beego:fix router expge 2014-06-18 23:32:47 +08:00
c3a07555c4 beego:change version to 1.3.0 2014-06-18 15:15:45 +08:00
2b8e411174 merger master httplib 2014-06-18 15:04:08 +08:00
720a77c1f9 beego:rever docs 2014-06-18 11:15:43 +08:00
b943b74fc5 beego:init GlobalDocApi 2014-06-18 11:09:47 +08:00
67be7b532d beego:doc move to swagger 2014-06-18 10:36:20 +08:00
8be83fe488 Merge pull request #642 from JessonChan/develop
ignore nil time
2014-06-17 15:25:03 +08:00
cff632f553 beego: swagger EnableDocs 2014-06-16 16:05:19 +08:00
4990d88861 Merge pull request #648 from redaready/develop
update chat example
2014-06-14 12:13:50 +08:00
7075ad8a28 update chat example 2014-06-14 00:21:26 +02:00
0e278ae358 beego:format the admin print route 2014-06-13 00:14:30 +08:00
e38a23b30e beego:admin add print method 2014-06-13 00:08:43 +08:00
117904be73 beego:fix the some regexp routes to different func 2014-06-12 23:08:05 +08:00
3b807845f2 beego:addtree support regexp 2014-06-12 20:50:29 +08:00
00b710e168 beego:namespace sub router add url to pattern 2014-06-11 23:51:19 +08:00
e25fcffbc0 config:json add support array 2014-06-11 22:47:11 +08:00
c13141b8bf beego:fix when user defined function equal to HTTP 2014-06-11 22:45:54 +08:00
7a7ff735e3 Merge pull request #644 from chrisport/develop
config: fix error when json config starts with an array
2014-06-11 22:02:28 +08:00
3b934bb910 config: fix error when json config starts with an array 2014-06-11 11:33:32 +03:00
aa275fb5ce beego:fix #639 2014-06-11 13:26:45 +08:00
deb553be7f beego:confgi support difference run mode section
runmode = dev
appname = doraemon
[dev]
httpport = 8880
sessionon = true

[prod]
httpport = 8888
sessionon = true

[test]
httpport = 8080
sessionon = false
2014-06-11 12:00:50 +08:00
3db9633ebd remove websocket logic because not support handler 2014-06-11 11:12:17 +08:00
2f8a70d548 beego: router support param has _ 2014-06-11 09:33:35 +08:00
7c0d0900ac beego:fix static file router 2014-06-11 01:19:39 +08:00
6809c97611 beego: improve performance 2014-06-11 01:11:32 +08:00
675643c68d beego: run mode support test 2014-06-10 22:47:48 +08:00
06f4bf493d ignore nil time 2014-06-10 22:10:58 +08:00
4786fb0948 beego:fix typo NewControllerRegister 2014-06-10 20:12:57 +08:00
fdb5672b7a beego:delete debug information 2014-06-10 18:10:32 +08:00
107a7a21c0 beego: dev mode print request router & pattern 2014-06-10 18:09:07 +08:00
dbebf8df4b beego:namespace support nest
ns := NewNamespace("/v3",
		NSAutoRouter(&TestController{}),
		NSNamespace("/shop",
			NSGet("/order/:id", func(ctx *context.Context) {
				ctx.Output.Body([]byte(ctx.Input.Param(":id")))
			}),
		),
	)
2014-06-10 17:11:02 +08:00
f7b01aab13 beego: modify the filter sequence 2014-06-10 11:02:41 +08:00
2570f075d9 beego:change ControllerComments exported 2014-06-09 17:46:13 +08:00
21cb8ea4a3 beego:AST code 2014-06-09 17:33:04 +08:00
6c8a7f1382 beego: router change to method Tree 2014-06-09 10:11:37 +08:00
e00eab7f49 beego: change to tree 2014-06-08 20:24:07 +08:00
bfabcfcb6b beego:router tree 2014-06-08 20:24:07 +08:00
f06ba52ede Merge pull request #633 from dlt/develop
fixed typo on constant applicationXml
2014-06-07 01:10:42 +08:00
fcae000a79 fixed typo on constant applicationXml 2014-06-06 13:56:34 -03:00
3e4c015982 Merge pull request #631 from curvesoft/master
cookiejar support
2014-06-04 23:02:52 +08:00
d689be30e8 remove httplib_test.php 2014-06-04 22:12:37 +08:00
7b110a0b73 remove httplib_test.php 2014-06-04 22:09:43 +08:00
e3033b57a6 1.gofmt httplib.go httplib_test.go
2.replace test url to http://httpbin.org functions
2014-06-04 21:15:24 +08:00
bd537554ea 1.gofmt httplib.go httplib_test.go
2.replace test url to http://httpbin.org functions
2014-06-04 21:04:50 +08:00
ebb3b91df9 1、增加cookiejar支持
2、增加Setting结构,便于统一设置请求参数
3、增加服务端测试php脚本
2014-06-03 21:20:10 +08:00
a65ad1a4bc fix the time test case 2014-05-31 14:28:44 +08:00
bdc01f52a0 Merge pull request #626 from mvpmvh/michael
Michael
2014-05-31 14:25:40 +08:00
a673a85d4a added tests config/json_test that test missing key usecases. created a template function to fetch AppConfig values 2014-05-30 23:48:23 -05:00
61008fe75c udpated timezone in templatefunc_test. changed error message to be more descriptive when tests fail 2014-05-30 14:12:21 -05:00
5dee6b7d19 beego: fix the namespace cond 2014-05-28 10:23:31 +08:00
f6c7a6bd32 beego: improve the admin router print 2014-05-27 17:27:22 +08:00
d2eece9a39 session: #620 make the session never read empty 2014-05-27 15:45:35 +08:00
c3a23b28ee beego: improve the RandomCreateBytes #620
when rand.Read is failed. will use the math/rand to generate the rand
bytes
2014-05-27 15:29:43 +08:00
9083927c6a beego: enhance the XSRFKEY from 15 to 32 #620 2014-05-27 15:00:10 +08:00
3f7e91e6a4 beego:fix *.* router bug 2014-05-26 10:15:56 +08:00
a2a6f47afa beego: support other config provider 2014-05-25 22:37:38 +08:00
23229ef9ef beego: BeegoServerName & beego.Run
BeegoServerName change to beegoServer+Version
beego.Run(“:8089”)
2014-05-25 22:35:20 +08:00
0d17d974cd beego: update namespace 2014-05-23 15:56:25 +08:00
17104c25a2 beego: Refactoring Filter & add comments 2014-05-20 18:47:41 +08:00
8b374d7f90 beego: add benchmark 2014-05-20 18:20:44 +08:00
fa3234147a httplib:drone can't upload file 2014-05-20 17:34:52 +08:00
33ad6c1370 beego: remove app funciont & fix #590
config := tls.Config{
    ClientAuth: tls.RequireAndVerifyClientCert,
    Certificates: []tls.Certificate{cert},
    ClientCAs: pool,
}
config.Rand = rand.Reader

beego.BeeApp.Server. TLSConfig = &config
2014-05-20 17:28:06 +08:00
04290dfc68 beego: delete hotupdate 2014-05-20 16:41:39 +08:00
03080b3ef2 beego:1.2.0 2014-05-20 15:53:41 +08:00
3f2a712ba8 beego:change default port 2014-05-20 15:40:05 +08:00
f215aa4810 beego: change the error tips 2014-05-20 15:34:27 +08:00
18a02d7d60 beego:support https & http listen 2014-05-20 15:30:17 +08:00
3f4d750dc4 utils: improve the file grep 2014-05-20 14:32:06 +08:00
9f01aeed31 beego:remove unused code 2014-05-19 18:52:48 +08:00
b45f0b9bf6 beego: fix #478 2014-05-17 02:56:50 +08:00
cf04ade603 merger master 2014-05-17 02:29:41 +08:00
92f6181616 beego: change the version to 1.2.0 2014-05-17 02:26:52 +08:00
9270a0504a beego: admin support link 2014-05-17 02:26:52 +08:00
1da37f6ce1 beego: controller add ServeFormatted
ServeFormatted serve Xml OR Json, depending on the value of the Accept
header
2014-05-17 02:26:52 +08:00
ef6d9b9a94 session: support memcache interface 2014-05-17 02:26:52 +08:00
c265786251 session:support struct.
gob.Register(v)
2014-05-17 02:26:51 +08:00
c5c806b58e beego: XSRF support Controller level fix #610
default value is true when you Enable Global XSRF, also can control in
the prepare function to change the value.
2014-05-17 02:26:51 +08:00
e657dcfd5f beego: support namespace
ns := beego.NewNamespace("/v1/api/")
ns.Cond(func(ctx *context.Context)bool{
	    if ctx.Input.Domain() == "www.beego.me" {
	    	return true
	    }
	    return false
	})
.Filter("before", Authenticate)
.Router("/order",	&admin.OrderController{})
.Get("/version",func (ctx *context.Context) {
	ctx.Output.Body([]byte("1.0.0"))
})
.Post("/login",func (ctx *context.Context) {
	if ctx.Query("username") == "admin" && ctx.Query("username") ==
"password" {

	}
})
.Namespace(
	NewNamespace("/shop").
		Get("/order/:id", func(ctx *context.Context) {
		ctx.Output.Body([]byte(ctx.Input.Param(":id")))
	}),
)
2014-05-17 02:26:51 +08:00
2ed9b2bffd orm: add test for unexported struct field 2014-05-17 02:26:51 +08:00
55ad951bce beego: support more router
//design model
	beego.Get(router, beego.FilterFunc)
	beego.Post(router, beego.FilterFunc)
	beego.Put(router, beego.FilterFunc)
	beego.Head(router, beego.FilterFunc)
	beego.Options(router, beego.FilterFunc)
	beego.Delete(router, beego.FilterFunc)
	beego.Handler(router, http.Handler)

//example

beego.Get("/user", func(ctx *context.Context) {
	ctx.Output.Body([]byte("Get userlist"))
})

beego.Post("/user", func(ctx *context.Context) {
	ctx.Output.Body([]byte("add userlist"))
})

beego.Delete("/user/:id", func(ctx *context.Context) {
	ctx.Output.Body([]byte([]byte(ctx.Input.Param(":id")))
})

import (
    "http"
    "github.com/gorilla/rpc"
    "github.com/gorilla/rpc/json"
)

func init() {
    s := rpc.NewServer()
    s.RegisterCodec(json.NewCodec(), "application/json")
    s.RegisterService(new(HelloService), "")
    beego.Handler("/rpc", s)
}
2014-05-17 02:26:51 +08:00
ef815bf5fc config: fix the import issue 2014-05-17 02:26:51 +08:00
6082a0af3e bug fixed 2014-05-17 02:26:51 +08:00
be30fb7937 refator func 2014-05-17 02:26:51 +08:00
f4e7d63e65 httplib support to set the protocol version for incoming requests 2014-05-17 02:26:51 +08:00
14688f240f httplib:support file upload 2014-05-17 02:26:51 +08:00
dce09837b9 fix the typo 2014-05-17 02:26:50 +08:00
3b9a404138 beego: support other analisys & fix typo 2014-05-17 02:26:50 +08:00
a6f55b59cf beego: add link in the admin console 2014-05-17 02:26:50 +08:00
c188cbbcb4 update all files License 2014-05-17 02:26:50 +08:00
4245521660 fix #576 2014-05-17 02:26:50 +08:00
05e5baaa9f beego:add post test case 2014-05-17 02:26:50 +08:00
54b92e9599 context:add Bind function
// Bind data from request.Form[key] to dest
// like
/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=
astaxie
// var id int  beegoInput.Bind(&id, "id")  id ==123
// var isok bool  beegoInput.Bind(&isok, "isok")  id ==true
// var ft float64  beegoInput.Bind(&ft, "ft")  ft ==1.2
// ol := make([]int, 0, 2)  beegoInput.Bind(&ol, "ol")  ol ==[1 2]
// ul := make([]string, 0, 2)  beegoInput.Bind(&ul, "ul")  ul ==[str
array]
// user struct{Name}  beegoInput.Bind(&user, "user")  user ==
{Name:"astaxie"}
2014-05-17 02:26:50 +08:00
aa68ffecec beego: support not-empty value in router fix #555 2014-05-17 02:26:50 +08:00
78991c81ab make Maxage work 2014-05-17 02:26:49 +08:00
348ff13857 update the error message 2014-05-17 02:26:49 +08:00
52817fb668 allow unexported fields on model structs 2014-05-17 02:26:49 +08:00
2c59ff1cc6 beego: admin support link 2014-05-17 02:20:48 +08:00
6bdf0838ce beego: controller add ServeFormatted
ServeFormatted serve Xml OR Json, depending on the value of the Accept
header
2014-05-17 01:26:59 +08:00
31a63c5d50 session: support memcache interface 2014-05-17 01:19:47 +08:00
237aaadd65 session:support struct.
gob.Register(v)
2014-05-17 00:43:51 +08:00
34ddcef1dc beego: XSRF support Controller level fix #610
default value is true when you Enable Global XSRF, also can control in
the prepare function to change the value.
2014-05-17 00:12:25 +08:00
f6ce2656db beego: support namespace
ns := beego.NewNamespace("/v1/api/")
ns.Cond(func(ctx *context.Context)bool{
	    if ctx.Input.Domain() == "www.beego.me" {
	    	return true
	    }
	    return false
	})
.Filter("before", Authenticate)
.Router("/order",	&admin.OrderController{})
.Get("/version",func (ctx *context.Context) {
	ctx.Output.Body([]byte("1.0.0"))
})
.Post("/login",func (ctx *context.Context) {
	if ctx.Query("username") == "admin" && ctx.Query("username") ==
"password" {

	}
})
.Namespace(
	NewNamespace("/shop").
		Get("/order/:id", func(ctx *context.Context) {
		ctx.Output.Body([]byte(ctx.Input.Param(":id")))
	}),
)
2014-05-16 23:47:29 +08:00
b647026dff orm: add test for unexported struct field 2014-05-16 13:14:15 +08:00
568c0c47f0 Merge pull request #542 from kylemcc/develop
orm: allow unexported fields on model structs
2014-05-16 13:11:55 +08:00
2629de28f2 beego: support more router
//design model
	beego.Get(router, beego.FilterFunc)
	beego.Post(router, beego.FilterFunc)
	beego.Put(router, beego.FilterFunc)
	beego.Head(router, beego.FilterFunc)
	beego.Options(router, beego.FilterFunc)
	beego.Delete(router, beego.FilterFunc)
	beego.Handler(router, http.Handler)

//example

beego.Get("/user", func(ctx *context.Context) {
	ctx.Output.Body([]byte("Get userlist"))
})

beego.Post("/user", func(ctx *context.Context) {
	ctx.Output.Body([]byte("add userlist"))
})

beego.Delete("/user/:id", func(ctx *context.Context) {
	ctx.Output.Body([]byte([]byte(ctx.Input.Param(":id")))
})

import (
    "http"
    "github.com/gorilla/rpc"
    "github.com/gorilla/rpc/json"
)

func init() {
    s := rpc.NewServer()
    s.RegisterCodec(json.NewCodec(), "application/json")
    s.RegisterService(new(HelloService), "")
    beego.Handler("/rpc", s)
}
2014-05-16 10:18:19 +08:00
10d2c7c328 config: fix the import issue 2014-05-16 10:18:19 +08:00
af7ac98bd6 Merge pull request #609 from JessonChan/develop
[important] bug fixed
2014-05-15 11:47:07 +08:00
6f78f1d4b2 bug fixed 2014-05-15 11:34:44 +08:00
9f95fd3309 Merge pull request #608 from JessonChan/develop
refactor func
2014-05-14 20:47:01 +08:00
74c309cefd refator func 2014-05-14 20:08:51 +08:00
b6d63c84ae Merge pull request #605 from francoishill/patch-6
Update app.go
2014-05-14 13:12:15 +08:00
bc2f1fb79d Update app.go 2014-05-13 17:19:50 +02:00
29e113a48a Merge pull request #597 from tobyzxj/develop
httplib support to set the protocol version for incoming requests
2014-05-09 16:25:33 +08:00
3caf1896d6 httplib support to set the protocol version for incoming requests 2014-05-09 15:48:50 +08:00
d5d5f23756 httplib:support file upload 2014-05-08 16:58:08 +08:00
46641ef3b6 fix the typo 2014-05-08 10:51:29 +08:00
8ed459512f Merge pull request #591 from luosangnanka/master
Update Session package README.md
2014-05-05 16:43:53 +08:00
25768f0109 Update README.md
Update the json format of session for file, redis, mysql, cookie, there are errors in these json string, such as after the param `ProviderConfig` and there is a lost of `"` in the line 61 of the `gclifetime`.
2014-05-05 16:21:50 +08:00
a56f67e073 Update README.md
Update the json format of session for file, redis, mysql, there are errors in these json string, after `ProviderConfig` params.
2014-05-05 16:15:49 +08:00
8164f9821d Update README.md
Update the json format of session for redis, there is an error in that json string
2014-05-05 16:13:03 +08:00
b2a69f505c beego: support other analisys & fix typo 2014-04-28 18:07:30 +08:00
e307bd7ba9 beego:hotfix for multipart/form-data 2014-04-15 05:05:53 +08:00
b2bd829d39 beego: add link in the admin console 2014-04-15 05:03:20 +08:00
f9b8617fa3 context: fix multipart/form-data 2014-04-15 05:02:50 +08:00
6c6e4ecfbc update all files License 2014-04-12 13:18:18 +08:00
8bcf03c652 fix #576 2014-04-11 16:08:43 +08:00
1ea449aa3a beego:add post test case 2014-04-10 22:33:32 +08:00
a99802b7d1 beego:query data from Form & params 2014-04-10 22:21:08 +08:00
b212ec8dab beego:query data from Form & params 2014-04-10 22:20:46 +08:00
3e16feb1e2 beego: fix flash errors 2014-04-10 18:16:08 +08:00
e50cbecf80 beego: fix flash errors 2014-04-10 18:14:18 +08:00
127b85bcaa context:add Bind function
// Bind data from request.Form[key] to dest
// like
/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=
astaxie
// var id int  beegoInput.Bind(&id, "id")  id ==123
// var isok bool  beegoInput.Bind(&isok, "isok")  id ==true
// var ft float64  beegoInput.Bind(&ft, "ft")  ft ==1.2
// ol := make([]int, 0, 2)  beegoInput.Bind(&ol, "ol")  ol ==[1 2]
// ul := make([]string, 0, 2)  beegoInput.Bind(&ul, "ul")  ul ==[str
array]
// user struct{Name}  beegoInput.Bind(&user, "user")  user ==
{Name:"astaxie"}
2014-04-10 00:31:16 +08:00
89dde6cd9d Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-04-09 21:43:32 +08:00
5a52949761 beego: support not-empty value in router fix #555 2014-04-09 21:42:57 +08:00
a78162e9e4 Merge pull request #573 from linluxiang/master
Make the Maxage Config of cookie session work
2014-04-09 14:42:29 +08:00
931e6162ac Merge pull request #574 from dz1984/fix/cache_test
Fix/cache test
2014-04-09 14:42:01 +08:00
1bb876f2df make Maxage work 2014-04-09 13:18:44 +08:00
18f70e6ee4 update the error message 2014-04-09 12:39:12 +08:00
82ca85dc65 release new version 1.1.4 2014-04-08 17:49:12 +08:00
f9cc9e9eb3 beego: release new version 1.1.4 2014-04-08 17:45:57 +08:00
18fee2ad9a beego: fixed serious Directory Traversal 2014-04-08 17:43:25 +08:00
4124760706 beego: filter the static file's url 2014-04-07 14:20:30 +08:00
3fe4f8c362 toolbox: modify the godocs 2014-04-06 01:05:20 +08:00
5a863b45f4 beego: BeeAdminApp private 2014-04-06 01:02:10 +08:00
3ad30d48b5 beego: fix the godoc 2014-04-06 00:53:18 +08:00
3255a43568 beego: move staticServer to New file 2014-04-06 00:18:21 +08:00
73d757e3f4 context: improve the formParse 2014-04-06 00:08:03 +08:00
deb28dd873 fix session test case 2014-04-04 10:16:34 +08:00
7f394feab5 beego: hot fix for TestBeegoInit can't parsefile 2014-04-04 10:04:36 +08:00
8cbea70e07 beego: hot fix for TestBeegoInit can't parsefile 2014-04-04 10:04:22 +08:00
f222f5b238 beego: hot fix for console logs & go run can't find file. 2014-04-04 09:57:57 +08:00
3f1de576e4 fix go run hello.go & console log 2014-04-04 09:57:51 +08:00
f48ca96a7e beego: fix log output when SetLogger has error 2014-04-04 09:57:45 +08:00
9421a21037 beego: fix log output when SetLogger has error 2014-04-04 09:57:37 +08:00
5c06cd090c beego: hot fix for console logs & go run can't find file. 2014-04-04 09:56:25 +08:00
fc982feeb9 fix go run hello.go & console log 2014-04-04 09:49:57 +08:00
31de651053 beego: fix log output when SetLogger has error 2014-04-04 09:49:57 +08:00
f4d62d3193 beego: fix dependency of cache / session sub package 2014-04-04 08:31:22 +08:00
acbdeb62e8 beego: fix log output when SetLogger has error 2014-04-04 08:22:26 +08:00
d58e9e6e12 beego: move dependency module to sub package 2014-04-03 23:41:48 +08:00
6497f29ed7 version 1.1.2 release 2014-04-03 15:56:31 +08:00
1705b42546 beego: change version from 1.1.1 to 1.1.2 2014-04-03 15:54:37 +08:00
5505cc09ed beego: move init to a fund & add a new fund TestBeegoInit
support Test with everything init
2014-04-03 15:07:20 +08:00
12e1ab0f80 beego: setLogger return error 2014-04-02 23:45:44 +08:00
9c5ceb70cc Logs: modify StartLogger to private 2014-04-02 23:43:37 +08:00
bf0b1af64f add workPath don't chdir when go run or go test 2014-04-01 18:08:00 +08:00
9c959fba4d fix string 2014-03-29 14:59:55 +08:00
5588bfc35e support filter to get router. get runController & runMethod 2014-03-29 14:55:34 +08:00
2f4acf46c6 modify the template file 2014-03-27 08:49:57 +08:00
c7437d7590 fix Cookie for session 2014-03-26 13:51:35 +08:00
4f819dbd9a Add a function SetLogFuncCall to enable 2014-03-26 00:06:25 +08:00
3f5fee2dc6 Logs support file & filenum 2014-03-25 23:48:58 +08:00
c7f16b5d5a Merge pull request #551 from steamonimo/develop
session provider for postgresql
2014-03-25 21:28:42 +08:00
8d1268c0a9 session provider for postgresql
This provider is based on the mysql provider: sess_mysql.go
2014-03-25 12:45:23 +01:00
c921b0aa5d fix #533 change the function name 2014-03-21 14:33:11 +08:00
589f97130c add w.Rotate 2014-03-21 14:33:11 +08:00
443aaadcce fix #533 change the function name 2014-03-21 14:24:00 +08:00
ff1938054a add w.Rotate 2014-03-21 14:07:03 +08:00
d79c297880 rollback: set httponly default is false. 2014-03-20 09:51:25 +08:00
65631e0522 fix orm test 2014-03-19 10:00:26 +08:00
a879e412a1 #514 2014-03-19 09:46:09 +08:00
4785ac14d7 allow unexported fields on model structs 2014-03-18 18:00:07 -05:00
50bc1ef757 rollback: set httponly default is false. 2014-03-17 12:27:04 +08:00
7bacb25725 Merge pull request #538 from admpub/patch-2
validators bug fixed
2014-03-14 15:44:05 +08:00
ad8418720f bug fixed 2014-03-14 14:47:52 +08:00
b59dae6fb8 Merge pull request #537 from jfolkins/develop
(REF#519) Enhancement: Allow developer to set FlashName and FlashSeperator values
2014-03-14 14:22:56 +08:00
4951314837 added FlashName,FlashSeperator, & Tests 2014-03-13 22:34:22 -07:00
8188873216 omit the data init 2014-03-14 12:00:53 +08:00
5d392b76c7 Merge pull request #531 from unphp/develop
Update router.go
2014-03-14 10:08:47 +08:00
c6a34b8efd Merge branch 'develop' of github.com:astaxie/beego into develop 2014-03-13 23:32:03 +08:00
95e67ba2c2 orm now support custom builtin types as model struct field or query args fix #489 2014-03-13 23:31:47 +08:00
439b1afb85 Merge branch 'release/1.1.1' 2014-03-12 21:25:41 +08:00
745e9fb0fb change version to 1.1.1 2014-03-12 21:24:23 +08:00
769f7c751b fix static file route 2014-03-12 21:06:20 +08:00
a8c2deb014 Merge pull request #530 from cnphpbb/develop
Update beego.go - Slice types exist trap
2014-03-12 18:56:12 +08:00
624f6258ee fix read / 2014-03-12 18:29:45 +08:00
43c977ab62 Update router.go
To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter
2014-03-12 17:20:53 +08:00
6c92ca2a16 fix bug for static file like /static /static_js /static_css 2014-03-12 17:03:34 +08:00
0f015d75d2 Update beego.go - Modify GroupRouters all function
Modify the file beego.go. 
Type GroupRouters all function 
Slice types exist trap bug.
2014-03-12 16:50:13 +08:00
217c3a2e87 Merge pull request #508 from voidd/develop
Couchbase session provider
2014-03-12 16:23:41 +08:00
ff6120cb93 Merge branch 'develop' of https://github.com/astaxie/beego into develop
Conflicts:
	session/sess_file.go
2014-03-12 15:57:57 +08:00
53aaf3b4a9 orm add ResetModelCache api for test case 2014-03-12 15:56:05 +08:00
d5b5c18cf9 orm add GetDB api #433 2014-03-12 15:56:05 +08:00
cacdb3228d orm add operator between #518 2014-03-12 15:56:05 +08:00
d0949b64c6 fix issue#521, return error when init redis session 2014-03-12 15:56:05 +08:00
d49984d47d Fix basic auth plugin example.
NewBasicAuthenticator requires passing a second argument for Realm.
2014-03-12 15:56:05 +08:00
9f3af59250 add support for sql.Null* types
Change instructions for sqlite3 tests to use in memory db for much faster
2014-03-12 15:56:05 +08:00
57afd3d979 GetStrings action as GetString 2014-03-12 15:56:05 +08:00
f7430a2ce1 enhance the static file path. If user foget / path.Join will auto fix it. 2014-03-12 15:56:05 +08:00
7389f0507e fix #505 2014-03-12 15:56:04 +08:00
ee889e9975 skip cookie args when value is nil 2014-03-12 15:56:04 +08:00
d8b9db8d3e move SetSecureCookie / GetSecureCookie to *context.Context and alias in Controller 2014-03-12 15:56:04 +08:00
9b498feac7 update output.Cookie 2014-03-12 15:56:04 +08:00
69982c62c8 path default is /
httponly default true
seuce default not set
2014-03-12 15:56:04 +08:00
b405e19f56 delete MaxAge cookielifeTime replace 2014-03-12 15:56:04 +08:00
828235b4c1 httplib support set transport and proxy 2014-03-12 15:56:04 +08:00
430a0a971f orm insert skip auto_now_add when user custom a value 2014-03-12 15:56:04 +08:00
5be22a99a8 fix captcha urlPrefix 2014-03-12 15:56:04 +08:00
5d02b18db4 register interface to gob automatically 2014-03-12 15:56:04 +08:00
97b68bdd66 fix bug, can not remove session file 2014-03-12 15:56:04 +08:00
5583fa2054 orm add ResetModelCache api for test case 2014-03-10 20:52:04 +08:00
00a410ad1a orm add GetDB api #433 2014-03-10 20:50:54 +08:00
6ca30386b8 orm add operator between #518 2014-03-10 20:19:29 +08:00
03b17a2ca9 Merge pull request #522 from pengfei-xue/develop
fix issue#521, return error when init redis session
2014-03-07 16:45:34 +08:00
9957a867cd fix issue#521, return error when init redis session 2014-03-07 16:35:34 +08:00
f3ba41a991 Merge pull request #517 from zkirill/develop
Fix basic auth plugin example.
2014-03-03 09:58:40 +08:00
4befa1bc1b Fix basic auth plugin example.
NewBasicAuthenticator requires passing a second argument for Realm.
2014-03-02 17:44:23 -08:00
9e3ebc88c4 Merge pull request #513 from hobeone/develop
add support for sql.Null* types, thx hobeone
2014-02-28 12:17:58 +08:00
6e00cfb464 add support for sql.Null* types
Change instructions for sqlite3 tests to use in memory db for much faster
2014-02-27 19:53:35 -08:00
c358c18018 Merge pull request #511 from francoishill/patch-2
Update sess_file.go
2014-02-28 09:40:28 +08:00
adf2a590fc Update sess_file.go
Lock required to ensure the File sessions work correct.
2014-02-27 15:34:38 +02:00
edb8bac5bc Merge pull request #510 from jfolkins/fix_sessregenid
fix: added nil check on c.CruSession to prevent crash
2014-02-27 10:34:39 +08:00
47d7ac06b7 fix: added nil check on c.CruSession to prevent crash 2014-02-26 16:44:31 -08:00
d05270d2ec GetStrings action as GetString 2014-02-26 15:02:58 +08:00
04a19685ed enhance the static file path. If user foget / path.Join will auto fix it. 2014-02-26 14:44:41 +08:00
62555771d0 fix #505 2014-02-24 15:55:38 +08:00
9dc93cbab0 skip cookie args when value is nil 2014-02-22 14:40:18 +08:00
7f5fb871de move SetSecureCookie / GetSecureCookie to *context.Context and alias in Controller 2014-02-22 11:58:53 +08:00
03037170e1 update output.Cookie 2014-02-22 11:12:57 +08:00
002e0854ab path default is /
httponly default true
seuce default not set
2014-02-22 10:37:14 +08:00
2bc70f62ce delete MaxAge cookielifeTime replace 2014-02-22 01:04:47 +08:00
8bf0e67b79 httplib support set transport and proxy 2014-02-20 13:53:13 +08:00
b310be1fcf orm insert skip auto_now_add when user custom a value 2014-02-20 13:45:31 +08:00
a54353b51c fix captcha urlPrefix 2014-02-20 13:44:34 +08:00
04c2ba01bc Merge branch 'develop' of https://github.com/voidd/beego 2014-02-19 22:44:06 +04:00
296bcab425 couchbase session provider 2014-02-19 15:54:16 +04:00
060b321182 Merge pull request #497 from pengfei-xue/develop
register interface to gob automatically
2014-02-18 16:47:59 +08:00
05a0a4b046 register interface to gob automatically 2014-02-14 17:52:57 +08:00
8906d3e77c Merge pull request #494 from pengfei-xue/develop
fix bug, can not remove session file
2014-02-13 20:19:42 +08:00
e822642cb0 fix bug, can not remove session file 2014-02-13 18:24:05 +08:00
a38a4f0343 Merge pull request #492 from TimothyYe/master
Fix spelling mistake
2014-02-10 14:51:26 +08:00
e8a22660e4 Update error.go
Fix the spelling mistake of error page.
2014-02-10 12:55:53 +08:00
92196c602b Merge branch 'release/release1.1.0' 2014-02-10 11:39:03 +08:00
76222ac8d0 change 1.0.1 to 1.1.0 2014-02-10 11:33:53 +08:00
a184c23603 basic auth for plugin 2014-02-10 11:31:54 +08:00
1b778509c9 should copy the data direct. don't need range 2014-02-08 10:42:34 +08:00
c4250872ca controller data inherit the context's data 2014-02-07 17:25:56 +08:00
17dd72241b Merge pull request #491 from fuxiaohei/develop
add comments for testing, utils and validation packages
2014-02-07 16:25:05 +08:00
ce2984f09a add comments for testing, utils and validation packages 2014-02-07 16:07:31 +08:00
846d766499 Merge branch 'develop' of git://github.com/astaxie/beego 2014-02-07 15:34:01 +08:00
bbc71142d7 controller can controller whether render the template.
EnableReander default is true.
2014-02-07 00:38:58 +08:00
74804bc586 Merge pull request #490 from fuxiaohei/develop
add comments for session and toolbox package
2014-02-04 06:25:18 -08:00
1d08a54f44 add comments for toolbox packages 2014-01-29 19:12:00 +08:00
682544165f add comments for session packages, part 2 2014-01-29 18:15:09 +08:00
3f0ec5c0ca Merge branch 'develop' of git://github.com/astaxie/beego into develop 2014-01-29 01:06:49 +08:00
0e2872324f add comments for session packages, part 1 2014-01-29 01:05:56 +08:00
2fb575838d Merge pull request #474 from pengfei-xue/develop
fix bug, redis session doesnt work
2014-01-28 01:50:20 -08:00
ab8f8d532a Merge pull request #487 from cloudaice/log-feature
fixed bug: in logs package check if platform is windows
2014-01-27 20:50:25 -08:00
d93f112083 fixed bug: in logs package check if platform is windows 2014-01-28 11:26:43 +08:00
9384e87083 orm 1. add api: NewOrmWithDB, AddAliasWthDB; 2. RawSeter -> add api: RowsToMap, RowsToStruct; 3. RawSeter -> change api: Values, ValuesList, ValuesFlat add optional params comumns. 2014-01-27 01:48:00 +08:00
34eff4cc1f bugfix, delete the sid if it's values is empty
* regenerate sid, if the old key doesn't exists, set the new one directly
2014-01-25 10:55:49 +08:00
8296713ba4 Merge pull request #477 from kylemcc/read_or_create
Add a ReadOrCreate method:
2014-01-24 18:01:24 -08:00
d014ccfb8e bug fix, session stored in redis cannot be deleted 2014-01-23 19:28:58 +08:00
190039b6f8 Add a ReadOrCreate method:
m := &User{Name: "Kyle"}
// Returns a boolean indicating whether the object was created,
// the primary key of the object, or an error.
created, id, err := orm.ReadOrCreate(m, "Name")
2014-01-22 09:15:21 -06:00
edf7982567 Merge pull request #473 from cloudaice/logs-feature
diffrent level logs display diffrent color
2014-01-21 19:03:32 -08:00
1509a6b681 fix bug, redis session doesnt work 2014-01-21 18:48:16 +08:00
11e6c2829b diffrent level logs display diffrent color 2014-01-21 18:00:17 +08:00
38f93a7ba9 Merge pull request #470 from fuxiaohei/develop
add comments for orm and middleware packages.
2014-01-17 17:44:52 -08:00
6b5108ef92 Merge pull request #1 from fuxiaohei/develop
merge develop
2014-01-17 07:48:39 -08:00
828a306069 add comments for orm package, done 2014-01-17 23:28:54 +08:00
4c527dde65 add comments for orm packages, part 2 2014-01-17 17:25:17 +08:00
f5a5ebe16b add comments for orm packages, part 1 2014-01-17 17:04:15 +08:00
32799bc259 add comments for middleware packages, fix typo error 2014-01-17 16:03:01 +08:00
91d75e8925 add readme for captcha, and enhanced performance 2014-01-17 12:07:30 +08:00
3e40041219 Merge pull request #468 from cloudaice/patch-1
Update README.md
2014-01-16 17:28:13 -08:00
7d5ee0d692 Update README.md 2014-01-17 00:17:43 +08:00
91cbe1f29b add some comments for captcha 2014-01-16 21:34:59 +08:00
f419c12427 add captcha util 2014-01-16 20:53:35 +08:00
fee3c2b8f9 add Strings interface can return []string sep by ;
Example:
peers = one;Two;Three
2014-01-15 17:19:03 +08:00
b016102d34 add coding 2014-01-15 09:40:33 +08:00
c20e1ab1e2 Merge pull request #463 from NormanZhang/develop
Update SessionExist to close the db connection
2014-01-14 06:13:34 -08:00
dc767b65df Update SessionExist to close the db connection
close the mysql connection
2014-01-14 19:54:32 +08:00
63f19974cd Merge pull request #460 from pengfei-xue/develop
use connection pool for redis, support auto connection
2014-01-11 06:39:20 -08:00
6e9ba0ea7f fix SessionRegenerateID should release old SessionStore and release new SessionStore in router.go 2014-01-11 17:01:33 +08:00
3b99f37aa1 add a empty fake config Initialize AppConfig to avoid nil pointer runtime error. 2014-01-11 14:28:11 +08:00
e8f5c10488 Merge pull request #457 from luxuchu/patch-1
fix #453
2014-01-10 22:21:27 -08:00
cb55009c8b remove mutex 2014-01-10 20:31:43 +08:00
b64e70e7df use connection pool for redis cache 2014-01-10 18:31:15 +08:00
8d79f8387b #441 fix detect timezone in mysql 2014-01-10 16:50:03 +08:00
afadb3f6df Update beego.go 2014-01-10 13:31:08 +08:00
844412c302 fix #453 2014-01-09 21:37:50 +08:00
299cb9130b Merge pull request #454 from pengfei-xue/develop
support redis cache auto connection
2014-01-09 04:49:31 -08:00
0b42e5573b align memcache operations with redis 2014-01-09 18:50:30 +08:00
a369b15ef2 reset cache connection to nil, if err isio.EOF
* this will support auto-connection
2014-01-09 18:49:18 +08:00
e34f8c4634 add cookie test 2014-01-08 23:24:31 +08:00
d7f2c738c8 add attach file 2014-01-08 22:35:42 +08:00
d06c04277f support send mail 2014-01-08 22:31:26 +08:00
aa2fef0d36 update sessionRelease
1. mysql fix last access time not update
2. mysql & redid Release when data is empty
3. add maxlifetime distinct Gclifetime
2014-01-08 20:54:20 +08:00
b766f65c26 #436 support insert multi 2014-01-06 11:31:35 +08:00
6f3a759ba5 gmfim add lock. fix #445 2014-01-05 23:16:47 +08:00
338124e3fb fix #443 2014-01-05 15:43:48 +08:00
31bdb793cf make fix 2014-01-05 15:21:50 +08:00
9cbd475701 beego support new version session 2014-01-05 14:59:39 +08:00
481448fa90 modify session module
change a log
2014-01-05 14:48:36 +08:00
95c65de97c fix #440 2014-01-04 22:30:17 +08:00
ef79a2b484 fix #440 2014-01-04 00:04:15 +08:00
20cfece1ab Merge pull request #438 from Codonaut/error_page_improvements
Error page improvements
2014-01-02 07:17:49 -08:00
c433b7029f added back a <br> 2014-01-02 09:54:15 -05:00
f5cf2876dd Improved the language on the error pages 2014-01-02 09:53:09 -05:00
480aa521e5 fix #430 2014-01-01 20:50:06 +08:00
d57557dc55 add AutoRouterWithPrefix 2014-01-01 17:57:57 +08:00
803d91c077 support modules design!
// the follow code is write in modules:
// GR:=NewGroupRouters()
// GR.AddRouter("/login",&UserController,"get:Login")
// GR.AddRouter("/logout",&UserController,"get:Logout")
// GR.AddRouter("/register",&UserController,"get:Reg")
// the follow code is write in app:
// import "github.com/beego/modules/auth"
// AddRouterGroup("/admin", auth.GR)
2013-12-31 23:43:15 +08:00
62ee48dcbf Merge branch 'develop' of https://github.com/astaxie/beego into develop 2013-12-31 20:48:46 +08:00
1e57587fe9 support Hijacker #428 2013-12-31 20:47:48 +08:00
61c0b3e286 fix db locked 2013-12-31 09:55:29 +08:00
383a04f4c2 move initmime from beego.Run to hookfunc 2013-12-31 00:34:47 +08:00
eea272482b Merge pull request #425 from fuxiaohei/master
add comments in logs package.
2013-12-30 07:38:51 -08:00
94ad13c846 add comments in logs package 2013-12-30 23:32:57 +08:00
412a4a04de #384 2013-12-30 23:04:13 +08:00
e0e8fa6e2a fix #413 2013-12-30 22:51:54 +08:00
a1e29b0b75 Merge pull request #422 from pengfei-xue/devel
simplify condition test for trailing /
2013-12-30 04:58:50 -08:00
984b0cbf31 1. :all param default expr change from (.+) to (.*)
2. add hookfunc to support appstart hook
2013-12-30 15:06:51 +08:00
3118c6c23f Merge commit '7a3d05ebf3fd36ea7e534de64ad38c23367ac97f' 2013-12-30 11:37:20 +08:00
3a08eec1f9 simplify condition test for trailing / 2013-12-30 11:29:35 +08:00
ecfd11adb4 fix typo healthcheck url 2013-12-29 11:01:19 +08:00
95dc670eb4 fix #416 2013-12-28 23:06:20 +08:00
7a3d05ebf3 when pattern is /admin while the url is /admin/ should return 200. fix #416 2013-12-28 23:04:45 +08:00
62f54cbbee fix typo error 2013-12-28 20:14:36 +08:00
4d7f7ffa37 Merge pull request #418 from fuxiaohei/master
add comments for httplib package.
2013-12-27 16:52:29 -08:00
cb876268b5 add comments for httplib package. 2013-12-27 17:11:39 +08:00
094f2fbab8 Merge pull request #415 from fuxiaohei/master
add comments for context package.
2013-12-26 07:40:19 -08:00
2d77c4dc49 fix code with no need line 2013-12-26 00:44:49 +08:00
f535916fae add comments for context package. 2013-12-25 20:13:38 +08:00
673993fa2b Merge pull request #412 from fuxiaohei/master
add comment in config package.
2013-12-24 07:06:02 -08:00
6f3803ce8c Merge remote-tracking branch 'astaxie/master' 2013-12-24 21:59:37 +08:00
a1f6039d82 gofmt code 2013-12-24 21:59:00 +08:00
0183608a59 add comments for config package. 2013-12-24 21:57:33 +08:00
d0e2c5c67a config ini module when set section is not exist will panic! 2013-12-24 21:57:15 +08:00
5b1afcdb5a add timeout description for file and memory cache. 2013-12-24 21:56:48 +08:00
cfcfeb7b99 change version from 1.0.0 to 1.0.1 2013-12-24 21:35:20 +08:00
053e7a6aa6 Merge remote-tracking branch 'astaxie/master' 2013-12-24 21:09:17 +08:00
62188a37c6 Merge branch 'master' of https://github.com/astaxie/beego 2013-12-24 15:27:23 +08:00
0b659961ba clearly the router, If user set the third params, will not follow the RESTful method 2013-12-24 15:27:00 +08:00
37aa2dc19f Merge pull request #409 from RickyLin/master
Added LayoutSections property to Controller
2013-12-23 23:21:42 -08:00
ba94479efd Merge remote-tracking branch 'astaxie/master' 2013-12-24 13:05:09 +08:00
4a3902432a Added LayoutSections property to Controller
users can define 0 or multipe sections in the Layout template file so
all kinds of content such as scripts, css can go to proper sections in
the generated html file.
2013-12-24 00:13:24 +08:00
fc19f8f183 Merge pull request #408 from jinguoli/master
convert to unix eol
2013-12-23 05:16:14 -08:00
8c9320725b convert to unix eol 2013-12-23 11:46:11 +08:00
0eaa58e0ca Merge pull request #406 from wangdx/patch-2
name → Name
2013-12-22 04:09:56 -08:00
2add87b829 name → Name
typo
2013-12-22 17:57:45 +08:00
ba3a9bee4c Merge remote-tracking branch 'astaxie/master' 2013-12-22 16:25:08 +08:00
5260a60ad2 Merge pull request #405 from wangdx/patch-1
RESTFul → RESTful
2013-12-21 23:58:20 -08:00
eb5d11c01b Update README.md
RESTFul → RESTful
2013-12-22 15:56:15 +08:00
f96eec6dea fix a code broken when documenting 2013-12-22 15:31:49 +08:00
e858f903a3 Merge pull request #404 from fuxiaohei/master
add comments in cache package
2013-12-21 23:28:43 -08:00
4037f952ec fix a code broken when documenting 2013-12-22 15:23:43 +08:00
6cb3f588c7 fix comments typo in beego package/ 2013-12-22 15:09:33 +08:00
8b0929b4bc add comments in cache package files. 2013-12-22 13:35:02 +08:00
fc339fc3e0 orm detect mysql timezone #403 2013-12-21 21:00:29 +08:00
2c868a9557 make datePatterns hide 2013-12-21 20:44:34 +08:00
8ba6dbb9a0 Merge pull request #402 from fuxiaohei/master
add api comments in file memzipfile.go,reload.go,router.go,template.go and templatefunc.go
2013-12-21 03:55:10 -08:00
ff18ae2562 add api comments in file memzipfile.go,reload.go,router.go,template.go and templatefunc.go, fix spelling error GetInitListner as GetInitListener. 2013-12-21 13:19:24 +08:00
57781d1001 when panic show the request url 2013-12-21 01:20:35 +08:00
419c3fc772 remove contextBuffer fix #396 2013-12-21 00:34:59 +08:00
2ad399db05 Merge pull request #400 from fuxiaohei/master
add api comments
2013-12-20 07:23:12 -08:00
eb9b958309 gofmt commented go file 2013-12-20 23:15:00 +08:00
ce332713c4 gofmt commented go files 2013-12-20 22:35:16 +08:00
e18b9f0321 Merge pull request #399 from Max-Liu/master
repaired the wrong IP when using in localhost (Mac os x)
2013-12-20 06:19:23 -08:00
b459cf2347 add api comments in file config.go, controller.go, filter.go and flash.go 2013-12-20 21:16:26 +08:00
933e98e4f2 add api comments in file beego.go 2013-12-20 19:36:54 +08:00
3f3bf299a6 add api comments in file app.go 2013-12-20 19:20:13 +08:00
b08a4a86c1 repaired the wrong IP when using in localhost (Mac os x)
it returns “[“ when using beego in local host(Mac os x 10.9) , it
appears that Request.remoAddr returns “[::1]:****”
2013-12-20 18:56:02 +08:00
3f0e55de56 reverse pull request 397, it not a bug. Just should this way 2013-12-20 13:20:09 +08:00
235d2740c7 Merge pull request #397 from pengfei-xue/devel
fix routing bug
2013-12-19 21:06:54 -08:00
00020139c5 fix routing bug 2013-12-20 11:38:29 +08:00
7aa307bd24 fix: re-parse config in windows 2013-12-20 11:21:48 +08:00
1c434dc6f4 Merge pull request #394 from fuxiaohei/master
add api comments in file admin.go
2013-12-19 07:51:22 -08:00
8cc8b022ee add api comments in file admin.go 2013-12-19 23:46:06 +08:00
05e197ffff fix #391 2013-12-19 22:02:25 +08:00
d718d9c8ee beego 1.0 release 2013-12-19 21:09:45 +08:00
f752c98d81 Merge branch 'master' of https://github.com/astaxie/beego 2013-12-19 19:52:52 +08:00
7c3d1d3402 fix type HasTemplateEXt to HasTemplateExt 2013-12-19 19:52:01 +08:00
764bbd9897 #390 should use filepath.Dir instead of path.Dir for. 2013-12-19 19:04:57 +08:00
67b9c63528 Merge pull request #390 from 1fei/master
更新logs的初始化部分
2013-12-19 02:54:37 -08:00
b87e122ac4 Update smtp.go 2013-12-19 18:16:06 +08:00
dcaff38cb9 Update file.go 2013-12-19 18:15:46 +08:00
b68c814c50 Update console.go
使用json直接初始化系统变量,代码更简单,并且便于后期扩展
2013-12-19 18:15:00 +08:00
9076ce7d17 Update conn.go
使用json直接初始化相关变量,代码更简单。如果把协议的首字母改大写会更简单,但不好改谢大的协议。
2013-12-19 18:13:10 +08:00
c9ccb9cc71 Merge pull request #389 from 1fei/master
Update httplib.go
2013-12-19 01:10:08 -08:00
0269a66940 Update httplib.go
httplib针对的是客户端的http请求,所以应该用"Cookie",而不是"Set_Cookie"
2013-12-19 16:29:46 +08:00
e481671814 _method also must support user defined router 2013-12-19 13:07:43 +08:00
53b2a29b44 when method is POST then to parse form 2013-12-19 12:50:47 +08:00
cb49be7815 fix router Put and Delete method when post method with _method 2013-12-19 12:47:54 +08:00
68d4c2c0d8 fix: AppPath is wrong. Move BeeLogger to config.go init. 2013-12-19 12:22:30 +08:00
de0113ae6a add comments & change channel from 100 to 1000 2013-12-19 00:43:29 +08:00
7242bc862e improve main login performance 2013-12-18 23:48:43 +08:00
bc0d4ac7cb set admin default to false. default the admin console is shut down. 2013-12-18 23:23:00 +08:00
b346617dc1 context.output now need reponsewriter 2013-12-18 22:35:52 +08:00
b8ed790858 recycling memory buffer in context 2013-12-18 22:33:21 +08:00
48cefc6767 improve performance change reflect to interface 2013-12-18 21:32:25 +08:00
079a41136e when call middle.Exception should set w.WriteHeader first 2013-12-18 21:00:45 +08:00
e01fbd497c when call abort show the err:http: multiple response.WriteHeader calls 2013-12-18 20:53:23 +08:00
9edf3143e1 fix autorouter params 2013-12-18 10:00:52 +08:00
00065f2b08 fix mime bug !! 2013-12-18 00:05:04 +08:00
0869df5588 delete GoToFunc & add GetControllerAndAction 2013-12-17 20:39:35 +08:00
54c89c0d63 Merge branch 'master' of github.com:astaxie/beego 2013-12-17 16:46:56 +08:00
1c52f6834a fix bug in postgres 2013-12-17 16:46:14 +08:00
c3bc2bedc0 add methodName to fix #380 & arrangement the router 2013-12-17 11:19:18 +08:00
7b27b7fed0 change SopRun to a variable 2013-12-17 08:53:20 +08:00
3a996c132d Merge pull request #383 from pengfei-xue/devel
delete write body from context.Redirect
2013-12-16 16:22:28 -08:00
65b9011bc1 delete write body from context.Redirect 2013-12-17 08:18:01 +08:00
b9fdbdf7b5 use StopRun to terminate the execution 2013-12-16 23:22:15 +08:00
436f9a7468 move session init before static 2013-12-16 22:56:35 +08:00
f8708d01bf update abort & error show & sessionRelease in defer 2013-12-16 22:54:29 +08:00
7fd18ba639 modify in the autorouter's Render #377 2013-12-16 11:46:54 +08:00
55f638ac78 Merge pull request #377 from vadimi/master
Panic template execution errors to show error pages accordingly
2013-12-15 19:44:54 -08:00
31f862c526 Panic template execution errors to show error pages accordingly 2013-12-15 13:17:27 -05:00
505fca93c3 change version from 0.9.9 to 1.0.0 RC1 2013-12-15 21:54:57 +08:00
849dbddc5a filterfunc name 2013-12-15 21:44:58 +08:00
d24fbe8426 add tips for listconf 2013-12-15 21:33:44 +08:00
f841b5962b Merge pull request #375 from pengfei-xue/devel
add do_filter func to reduce duplicated code
2013-12-15 05:25:46 -08:00
f26c19052d add listconf fix #351 2013-12-15 21:22:50 +08:00
72af5ce582 add do_filter func to reduce duplicated code 2013-12-15 21:02:28 +08:00
aea3c68c98 add debug print func! fix #278 2013-12-15 20:32:32 +08:00
8368360aec Merge pull request #372 from francoishill/master
Gzip support for static files, see https://github.com/smithfox/beego
2013-12-15 00:39:18 -08:00
a5029cc4ca Merge pull request #370 from slene/master
fix set maxlifetime, close conn
2013-12-15 00:30:37 -08:00
9995168f9a Update router.go 2013-12-14 20:35:57 +02:00
02eacb8e95 Update config.go 2013-12-14 20:34:27 +02:00
2a4459b98b Create memzipfile.go 2013-12-14 20:33:12 +02:00
d48b7e0101 #369 set maxlifetime, close conn 2013-12-14 23:33:24 +08:00
495033b977 fix #366 2013-12-13 21:25:32 +08:00
9c164408d3 Merge pull request #365 from yaofangou/master
add convertor for cache
2013-12-12 18:43:41 -08:00
9f2327ee57 Merge remote-tracking branch 'astaxie.beego/master' 2013-12-13 10:07:21 +08:00
68f2a5ddc7 add convertor for cache 2013-12-13 10:00:24 +08:00
b1374b6a08 move safemap to utils & add many helpful slice function 2013-12-12 22:40:29 +08:00
19119e99f7 move utils to utils libs & func move to templatefunc 2013-12-12 22:25:08 +08:00
d603a6714d add more tips for admin 2013-12-12 21:51:25 +08:00
c8724d40d2 Merge pull request #362 from kyle-wang/master
rename some function name under /utils
2013-12-12 05:51:06 -08:00
9ff937591b rename some function name under /utils
change the function name to common english word
2013-12-12 19:30:44 +08:00
3c92cce9f4 update httplib to support setcookie 2013-12-12 15:23:17 +08:00
fd78ea5c4a set cookie to zero 2013-12-12 10:43:13 +08:00
c3c49e9172 Merge pull request #360 from shxsun/master
add some func to beego/utils
2013-12-11 18:08:04 -08:00
a43a1be0b4 add some useful func 2013-12-11 22:18:45 +08:00
98d1f79ca4 Merge pull request #359 from wangkechun/patch-1
Update README.md
2013-12-11 04:52:44 -08:00
c3fcc72969 Update README.md 2013-12-11 20:50:41 +08:00
efd285a6c4 update httplib support https 2013-12-10 22:01:50 +08:00
3a0b2e3b95 beego config module json support get data like key:🔑:key 2013-12-10 18:09:58 +08:00
34ba7a8e6c update file cache 2013-12-10 14:10:22 +08:00
b733b98408 for init if doesn't have app.conf will not panic 2013-12-10 00:17:22 +08:00
b97d9896a4 update config to change section . to :: 2013-12-09 23:54:35 +08:00
52ebaece73 add other utils 2013-12-09 16:01:24 +08:00
690ecb8168 Merge pull request #353 from shxsun/master
initial utils dir
2013-12-08 21:45:11 -08:00
830ccb6660 initial utils dir 2013-12-09 13:30:19 +08:00
4fa625dea6 default app HttpAddr should none 2013-12-07 17:22:57 +08:00
ce8a1be8d9 change syscall package to os.Kill 2013-12-07 16:52:39 +08:00
1ea18adce8 add default mime type to fix #341 2013-12-07 15:16:32 +08:00
35d15b8977 fix #350 2013-12-07 14:47:20 +08:00
48ad0202bf update some tips 2013-12-07 13:26:28 +08:00
2220968a01 Merge pull request #347 from odiel/master
Adding template functions to generate JS and CSS tags
2013-12-06 08:08:22 -08:00
1611476288 Update template.go
Adding assets_js, assets_css template functions
2013-12-06 10:36:56 -05:00
0bdd400fe7 Update utils.go
Adding Assets functions for the template, to generate HTML tags for JS and CSS
2013-12-06 10:36:12 -05:00
8cf34fce98 Merge pull request #344 from vadimi/master
Improve unhandled error handling in prod mode
2013-12-05 21:55:37 -08:00
d79977977d Improve unhandled error handling in prod mode 2013-12-06 00:47:34 -05:00
de70732cd4 typo 2013-12-06 13:46:14 +08:00
32d9d13dc7 Restore SimpleServerError method 2013-12-06 00:44:54 -05:00
7125bd8faa support json comments 2013-12-06 13:43:08 +08:00
7196d6ede3 Revert "Improve unhandled error handling in prod mode"
This reverts commit c2079276eb.
2013-12-06 13:37:36 +08:00
54ef08c039 Merge pull request #342 from vadimi/master
Improve unhandled error handling in prod mode
2013-12-05 21:31:18 -08:00
c2079276eb Improve unhandled error handling in prod mode 2013-12-05 20:57:02 -05:00
b6bf712195 Merge pull request #338 from FightingMan/master
miswrite a type of word
2013-12-05 03:54:21 -08:00
27a02082a3 miswrite a type of word 2013-12-05 19:20:08 +08:00
39b2e07dc8 Merge pull request #336 from vadimi/master
Fixed some typos
2013-12-04 19:36:52 -08:00
12a37f71be Fixed some typos 2013-12-04 22:27:29 -05:00
9f3058caad Merge pull request #335 from pengfei-xue/devel
beeapi example is broken
2013-12-04 19:14:51 -08:00
4181ea8620 beeapi example is broken 2013-12-05 10:29:04 +08:00
cfc604b53d Merge pull request #333 from pengfei-xue/devel
panic if parse failed
2013-12-04 17:54:36 -08:00
420e816bed panic if parse failed 2013-12-04 23:53:36 +08:00
fb6312a303 reverse from pull 330 2013-12-04 17:03:49 +08:00
3c91360d72 dictinct system pkg and third pkg 2013-12-03 21:37:39 +08:00
983f20642c Merge pull request #330 from pengfei-xue/devel
panic if parse config failed
2013-12-03 04:33:08 -08:00
12ade02f2d panic if parse config failed 2013-12-03 19:56:30 +08:00
ec745693dc Merge pull request #327 from pengfei-xue/devel
remove duplicated config initialization
2013-12-03 00:57:27 -08:00
9cac7504c9 fixup! fix typo, use camel notation for all keys 2013-12-03 16:37:39 +08:00
75d09d138a fix typo, use camel notation for all keys 2013-12-03 13:23:33 +08:00
07312e240c remove duplicated config initialization 2013-12-03 13:15:32 +08:00
9c4414f995 Merge pull request #323 from pengfei-xue/devel
case insensitive for section and key for ini config
2013-11-28 19:29:37 -08:00
8e7fe8bb66 case insensitive for section and key for ini config 2013-11-29 10:17:35 +08:00
0ba36763f5 Merge pull request #322 from pengfei-xue/devel
there is no need to check if b.params is nil
2013-11-27 23:05:05 -08:00
3fce78c7cd there is no need to check if b.params is nil 2013-11-28 13:52:24 +08:00
a83a92cdab Merge pull request #321 from smallfish/master
update logic for check ini comments
2013-11-27 21:10:13 -08:00
23ff7af0b7 update logic for check ini comments 2013-11-28 11:56:13 +08:00
9cc2e7237b add httplib readme 2013-11-28 11:26:17 +08:00
63b82c438d support section
if iniconf.String("demo.key1") != "asta" {
+		t.Fatal("get demo.key1 error")
+	}
+	if iniconf.String("demo.key2") != "xie" {
+		t.Fatal("get demo.key2 error")
+	}
2013-11-27 23:55:26 +08:00
ba5e393e99 add flush & read all chan data 2013-11-27 17:50:10 +08:00
690d77e985 orm test add extra custom JsonField 2013-11-26 21:34:40 +08:00
bcc8f60677 orm add test about CustomField 2013-11-26 18:32:12 +08:00
6f93b2bcbe fix _ "github.com/go-sql-driver/mysql" 2013-11-26 17:16:35 +08:00
70dc9d7e14 update docs 2013-11-26 17:14:27 +08:00
01d87591e4 session & orm register called twice for driver mysql 2013-11-26 17:12:25 +08:00
ff1b8588e0 #313 2013-11-26 17:09:23 +08:00
54fb49ed95 fix #315 2013-11-26 16:47:50 +08:00
fbd1c3f8b0 Merge pull request #318 from pengfei-xue/devel
add server host:port info when starting app
2013-11-25 23:56:45 -08:00
e823b6ea95 add server host:port info when starting app 2013-11-26 15:30:59 +08:00
b16ef12ac0 fix test 2013-11-26 14:13:23 +08:00
f9e732b5ce fix param to params 2013-11-26 11:16:22 +08:00
ae2e25f4c2 fix #316 2013-11-26 11:05:49 +08:00
7b405e9af7 fix type 2013-11-26 11:05:12 +08:00
e09e642a83 Merge pull request #317 from pengfei-xue/master
beego.Context.Abort return immediately
2013-11-25 18:30:38 -08:00
76c0636125 beego.Context.Abort return immediately
* add common 4XX/5XX HTTP exceptions
2013-11-26 08:46:46 +08:00
c7a0298546 Merge pull request #314 from shavac/master
add function InsertFilter to replace AddFilter by using int const to determine filter position
2013-11-25 00:56:29 -08:00
b4fb657efd eliminated improper comments 2013-11-25 16:15:48 +08:00
8c3b936c60 replace filterPos to pos 2013-11-25 16:04:02 +08:00
47fc32ba47 add func InsertFilter(pattern string, pos int, filter FilterFunc) *App to replace AddFilter
pos can be const:
    BeforeRouter = iota
    AfterStatic
    BeforeExec
    AfterExec
    FinishRouter
2013-11-25 15:59:40 +08:00
e5904443d9 fix #302 2013-11-24 19:29:48 +08:00
4ee6cc3022 Merge pull request #312 from bronze1man/pr-orm-querytable-nil-ptr-struct
[orm] QueryTable with nil ptr struct
2013-11-24 01:08:00 -08:00
ceb4aa9e25 [orm] QueryTable with nil ptr struct 2013-11-24 14:26:32 +08:00
a0dff9148a change third repo from other to beego 2013-11-21 22:19:19 +08:00
c94189668f Merge pull request #306 from pengfei-xue/patch-1
http.ResponseWriter doesn't implement  WriteCloser interface
2013-11-21 05:31:00 -08:00
0122addd00 http.ResponseWriter doesn't implement WriteCloser interface
The removed code will never be executed, remove it
2013-11-21 18:39:16 +08:00
f52faf63f7 fix #304 2013-11-21 15:59:16 +08:00
b9fb88f537 add test for task 2013-11-20 23:53:54 +08:00
54185df46e change admin to toolbox & support task 2013-11-20 21:18:00 +08:00
16352579f3 #294 2013-11-18 13:21:19 +08:00
c5eabcf469 add file cache, thanks to gouki 2013-11-18 11:22:06 +08:00
b5b53b3849 add more feture in admin
1,增加QPS的限制
2,增加任务
3,增加healthcheck
2013-11-17 23:10:21 +08:00
ea513002c5 admin filter finish to all router include static file
so if your web is need auth or release the resoure you can writer the
finish filter
2013-11-15 21:51:36 +08:00
9776bb8a03 improve the admin module 2013-11-15 21:45:51 +08:00
969464f8ad delete back info 2013-11-15 18:25:36 +08:00
a9b6a0b81a improve the output fomate 2013-11-15 18:21:26 +08:00
2758c6da14 UrlMap fix to StatisticsMap 2013-11-15 18:12:36 +08:00
097bcb3b5b Improve monitoring management module 2013-11-15 18:08:53 +08:00
18335194bc fix runrouter is nil 2013-11-13 21:37:17 +08:00
6c13bdde25 support profile & statistics in another port 2013-11-13 21:11:09 +08:00
a981dab536 fix #286
the process exit and the channel info can't flush to console
2013-11-13 21:11:08 +08:00
6632c21d62 Merge pull request #287 from francoishill/patch-1
Update template.go
2013-11-12 16:15:53 -08:00
a5b5ae899c Update template.go
We can specify "TemplateLeft" and "TemplateRight" but they did not work as these two changes are required.
2013-11-12 20:22:42 +02:00
ac6108a87d add more json test info 2013-11-11 21:25:03 +08:00
2f75445520 when runmode is dev it will show warning ingo
if have a attack url, the info is
2013-11-10 23:42:07 +08:00
43057a2fcb fix #284 2013-11-10 23:26:28 +08:00
9446563e5b add util func to get the url fix #282
UrlFor(endpoint string, values ...string) string
2013-11-10 23:05:07 +08:00
167ad203cb orm #276 2013-11-08 22:19:01 +08:00
558738ade8 JSON CallBack类型的链接,这类出现在几乎各大Web 2.0网站中。修补这类安全问题很简单,只要在目标网页开头部分强制加一个空格即可,这样BOM头就无效了。 2013-11-08 20:54:06 +08:00
0fb7d4babb add UrlFor function but still improve 2013-11-08 18:20:08 +08:00
91c7635d0e fix #283 2013-11-08 18:00:07 +08:00
2a81595c3e fix #280 2013-11-08 17:23:56 +08:00
9e4d886a6c filter http method fix #279 2013-11-07 22:10:54 +08:00
b644665952 orm set relation column name #259 2013-11-06 22:05:10 +08:00
9492e4131b support reverse m2m relation 2013-11-06 21:08:12 +08:00
2abda0954b fix #277 2013-11-06 16:51:33 +08:00
dbf6ca2b4c fix #277 2013-11-06 16:12:10 +08:00
bca6a33325 fix #140 2013-11-05 23:41:01 +08:00
c8f86652a3 fix #248 2013-11-05 22:23:48 +08:00
076bd0b440 #254 add controller func SessionRegenerateID 2013-11-05 22:07:09 +08:00
a443a798e3 fix #254 2013-11-05 21:59:35 +08:00
23d79b8b05 #254 add SessionHashFunc SessionHashKey SessionCookieLifeTime 2013-11-03 21:41:07 +08:00
9ccdb31003 fix #236 2013-11-02 23:54:17 +08:00
0adb864219 fix #270
同时支持全部小写和驼峰写法
2013-11-02 22:45:08 +08:00
d39954c935 Merge branch 'master' of github.com:astaxie/beego 2013-11-02 00:52:05 +08:00
89c03870c8 orm fix sqlite3 time convert 2013-11-02 00:51:53 +08:00
1d44018128 orm fix for support custom field 2013-11-02 00:50:08 +08:00
d835b0c80f fix #235 2013-11-02 00:16:10 +08:00
bc862e526d Merge pull request #271 from shavac/master
minor fix,if path does not include ':', beego will crash of "index out of range"
2013-11-01 04:57:07 -07:00
a4df6e403c fix #265 2013-11-01 18:32:03 +08:00
290a9d184d minor fix,if path does not include : 2013-11-01 12:34:16 +08:00
00abdcd0a1 Merge pull request #269 from shavac/master
improve StaticDir config file parser.
2013-10-31 20:02:53 -07:00
73a2081ae7 improve StaticDir config file parser.New style is like "css:static/css js:static/js" 2013-11-01 08:23:57 +08:00
c6167ef184 fix #260 2013-10-30 23:02:53 +08:00
4bcbc588fc fix #260 2013-10-29 23:11:23 +08:00
5f4fe6d868 fix #260 2013-10-29 23:05:48 +08:00
7131dec3af add go1.2 template funcs eq, ge, gt, le, lt, ne to beego 2013-10-29 22:35:04 +08:00
0ddde6fa9f fix #252 2013-10-29 22:03:43 +08:00
5d54acba30 fix isajax 2013-10-29 15:45:45 +08:00
c1b2e1d0ca fix #238 2013-10-28 23:30:16 +08:00
9fc4cd8958 #238 add GetProvider 2013-10-28 23:25:30 +08:00
742c432fd1 fix #252 2013-10-28 23:09:45 +08:00
3ac5eec301 fix #253 2013-10-28 22:38:50 +08:00
060631e952 fix #241#192 2013-10-28 22:19:37 +08:00
57165f2fb5 fix #247 2013-10-28 21:44:07 +08:00
5940fd4bbd fix build error 2013-10-21 22:08:41 +08:00
13180c5a17 fix #249 2013-10-21 22:06:30 +08:00
6ea8dd5999 fix #255 2013-10-18 17:37:34 +08:00
2795ac6e6b update panic 2013-10-17 21:14:00 +08:00
83e6079ff7 fix MinSize / MaxSize / Length should use Rune Count 2013-10-16 22:49:06 +08:00
d043ebcdd3 orm support complete m2m operation api / auto load related api 2013-10-14 22:31:35 +08:00
e11c40eee4 fix #244 2013-10-12 16:56:42 +08:00
5d55ca19be orm add Exist func 2013-10-12 06:57:14 +08:00
3f91dfbca3 Merge branch 'master' of github.com:astaxie/beego 2013-10-09 20:29:25 +08:00
bf3830b6f0 orm add atomic set value 2013-10-09 20:28:54 +08:00
278f8eb13e add ValidFormer interface, can custom valid 2013-10-09 20:26:23 +08:00
658a671b79 all panic use Error 2013-10-09 11:37:16 +08:00
bf836a2126 fix #233
thanks for the good suggestion
2013-09-30 11:30:22 +08:00
35a136bcee fix #232 2013-09-29 15:38:28 +08:00
aaf1490ff5 fix router 2013-09-28 23:37:05 +08:00
a62ed10ab3 add supoort AppController
http://play.golang.org/p/MZptHZeYUx
2013-09-28 23:30:36 +08:00
e79d756d06 #230 2013-09-28 22:23:00 +08:00
904b37032c Merge pull request #227 from sdjc/patch-4
Update config.go
2013-09-28 07:09:59 -07:00
8a37c30f35 fix #226 2013-09-28 21:45:52 +08:00
c4edc13413 fix #230 2013-09-28 21:36:36 +08:00
9ddbab59bb Update config.go 2013-09-27 08:37:17 +08:00
1eb87c5c59 fix #225 2013-09-26 22:31:39 +08:00
fb1439dfb9 Merge pull request #224 from smithfox/patch-1
fix #217
2013-09-26 06:09:03 -07:00
d393c329c3 protect parts's len 2013-09-26 21:06:37 +08:00
bed0fe2218 fix #217 2013-09-26 18:33:01 +08:00
02c2e16253 Strengthens the session's function 2013-09-26 18:07:00 +08:00
59a67720b4 xsrf's defaut time set to 0 & fix ts not use 2013-09-25 23:07:54 +08:00
93e1206d60 xsrf change to randstr and cookie set to security cookie 2013-09-25 23:05:53 +08:00
2249d745d9 session support secure set 2013-09-25 23:05:53 +08:00
f9ed74a9b2 Merge pull request #219 from markstory/readme-fixes
Fix typos in the readme file.
2013-09-25 06:42:15 -07:00
65ec2db4e8 Fix typos in the readme file. 2013-09-25 09:41:11 -04:00
001e33f310 fix #213 2013-09-22 19:20:40 +08:00
09aca2528a orm RegisterDataBase remove default maxIdle/maxConn 2013-09-22 17:51:37 +08:00
beecc5072e fix #209 2013-09-22 14:35:01 +08:00
797bd98269 fix #210 2013-09-22 14:32:18 +08:00
4a3d32dc1f support auto get session from input fix #211 2013-09-22 11:43:22 +08:00
048be29fcd add w.started fix #208 2013-09-22 11:17:18 +08:00
4ce584c5a6 fix #201 2013-09-22 11:12:37 +08:00
3c1d23bc62 #207 2013-09-19 23:40:55 +08:00
95307a3d78 miss filter * 2013-09-19 23:14:47 +08:00
a85d91b4bd fix #206 2013-09-19 23:04:05 +08:00
a616087cde orm now can specify engine for mysql. add api SetMaxIdleConns/SetMaxOpenConns(go 1.2) 2013-09-16 09:50:32 +08:00
198d6320dd fix xsrf cookie path should use root 2013-09-13 22:42:15 +08:00
1596aa7a34 orm support use any numeric type set QuerySeter.Limit value 2013-09-13 18:06:44 +08:00
8e8d39d3cb fix memory cache Put method should set value when key exist. 2013-09-13 18:04:22 +08:00
42958ddd41 update version from 0.9.0 to 0.9.9 2013-09-13 17:45:26 +08:00
0476da503e improve template 2013-09-13 17:41:31 +08:00
943fe971f1 support template 2013-09-13 13:57:40 +08:00
9d84969bf6 fix #153
已经支持了任意定义变量的路由形式,具体的使用请参考:

func TestManyRoute(t *testing.T) {

	r, _ := http.NewRequest("GET", "/beego32-12.html", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegistor()
	handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{})
	handler.ServeHTTP(w, r)

	id := r.URL.Query().Get(":id")
	page := r.URL.Query().Get(":page")

	if id != "32" {
		t.Errorf("url param set to [%s]; want [%s]", id, "32")
	}
	if page != "12" {
		t.Errorf("url param set to [%s]; want [%s]", page, "12")
	}
}
2013-09-13 11:22:14 +08:00
3745bb7279 orm.Read support specify condition fields, orm.Update and QuerySeter All/One support omit fields. 2013-09-12 19:04:39 +08:00
55fe3ba52f update tempalte's regexp 2013-09-12 18:20:29 +08:00
de2dd6e070 update template's testcase 2013-09-12 17:32:11 +08:00
ffb347a2bb support nest template 2013-09-12 17:20:32 +08:00
19862725f7 support template 2013-09-12 15:24:08 +08:00
f502f84423 fix #189 2013-09-12 13:44:24 +08:00
e788fb7239 add an DateParse function, parse datestring to time.Time use php date format 2013-09-11 23:47:01 +08:00
72f5961dd8 delete docs move to github.com/beego/beedoc 2013-09-11 18:04:10 +08:00
9f6b803a10 update middleware & beego's error 2013-09-11 17:00:39 +08:00
0da2059f79 update info 2013-09-11 16:31:18 +08:00
e904d26e08 modify middleware 2013-09-11 16:23:41 +08:00
1ff0a31d3f add router test 2013-09-11 15:49:12 +08:00
e7f08946d1 improve the performance 2013-09-11 15:16:09 +08:00
ea9c2cebfd validation add more info to ValidationError, and put all messages tmpl to a map 2013-09-10 21:51:25 +08:00
02a03cec33 add logs readme 2013-09-10 18:44:29 +08:00
31fbd52462 add chat example 2013-09-10 18:38:40 +08:00
a88750d2b3 support websocket 2013-09-10 17:56:06 +08:00
da91565354 add iswebsocket method 2013-09-10 17:02:56 +08:00
44f5c208b6 update example 2013-09-10 15:08:32 +08:00
83e26fdc9b fix config bug 2013-09-10 14:47:48 +08:00
4a329db792 Merge pull request #187 from Unknwon/master
compatiable for old version
2013-09-09 16:47:23 -07:00
1f8022d4ad compatiable for old version 2013-09-09 16:43:48 -04:00
63da329468 go on modify api 2013-09-10 00:17:49 +08:00
acadea6afa update xml 2013-09-10 00:02:22 +08:00
bd61dd9ffc change a log about new version 2013-09-10 00:00:18 +08:00
5b9ae54441 update input 2013-09-10 00:00:17 +08:00
41dd6e580d orm 1. complete QueryRow/QueryRows api 2. QuerySeter.All support *[]Type and *[]*Type 2013-09-09 22:33:41 +08:00
22d2de9fc7 orm fix 1. support Limit when use QuerySeter.One 2. return Local time 2013-09-06 19:03:47 +08:00
6064a7ab2a orm RawSeter readValues set nil when get NULL 2013-08-30 12:38:32 +08:00
8f5ca303b5 orm fix when use uint as pk 2013-08-30 12:32:05 +08:00
dc8f932009 update doc's link 2013-08-28 17:09:01 +08:00
4922237847 sleep some time for send mail 2013-08-28 15:55:34 +08:00
2424618163 delete default console & add bench test 2013-08-28 15:48:48 +08:00
b96f7e2ef2 Merge pull request #174 from Xelom/FixBra
Changed ObejctController to ObjectController in beeapi
2013-08-27 18:54:52 -07:00
cde38fbe1a Changed ObejctController to ObjectController 2013-08-27 20:05:35 +03:00
56a1603e1f Changed ObejctController to ObjectController 2013-08-27 20:05:15 +03:00
f92973794e finish logs module 2013-08-27 23:49:04 +08:00
49bbca0ce3 orm Improve syncdb 2013-08-27 12:33:27 +08:00
6686d9235c orm fix create index 2013-08-25 13:50:50 +08:00
7c72b2dca7 orm fix syncdb 2013-08-25 11:35:12 +08:00
4c061feddf orm support custom multi unique / index 2013-08-22 21:19:58 +08:00
02d2990576 orm all docs wiil update to beego.me 2013-08-22 18:35:35 +08:00
94f7ba8bdf orm add uint uint32 uint64 auto increment support 2013-08-22 18:35:26 +08:00
7d7c9825e9 change to asta 2013-08-22 14:27:23 +08:00
dee542df42 fix yaml 2013-08-22 13:46:22 +08:00
f2b4c29f83 Merge pull request #168 from fanngyuan/master
添加用于测试的request,后面我会在bee工具里添加一个单元测试的例子。
2013-08-21 21:05:39 -07:00
28652c5d21 Merge pull request #167 from zxcvdavid/patch-1
file name fixed
2013-08-21 21:05:07 -07:00
b104bfeb93 remove comment 2013-08-22 11:05:54 +08:00
8af4ba8980 file name fixed
file name fixed :P
2013-08-22 09:15:45 +08:00
87f8fb0750 finish config module support ini/json/xml/yaml 2013-08-22 00:07:33 +08:00
eaffabf388 add client 2013-08-21 22:47:58 +08:00
ac8853dfd3 move from beehttp to context 2013-08-21 17:59:31 +08:00
d2be74a4f2 add beehttp module 2013-08-21 13:24:44 +08:00
17a99cfa81 maybe can't delete cookie error in same browser 2013-08-21 11:31:32 +08:00
fd677c45aa fixed 2013-08-20 14:20:38 +08:00
769735c207 fix #166 2013-08-20 12:04:50 +08:00
b114f258d6 orm update docs 2013-08-19 22:37:53 +08:00
c38abf35da orm support auto create db 2013-08-19 22:37:39 +08:00
1fedaf21ec jsonp set header to javascript 2013-08-19 21:34:34 +08:00
0b74db64eb fix typo for templateLeft 2013-08-19 13:02:18 +08:00
f2222ba3c6 support fastcgi 2013-08-19 11:23:53 +08:00
3cb8a96041 support unix socket fix #151
You can set
HttpAddr="/tmp/beego.sock"
HttpPort=0
2013-08-19 11:21:02 +08:00
4f538e7fd2 fix #163
you can set the delimiter like this

beego.TemplateLeft="{#"
beego.TemplateRight="#}"

or can set the config file like app.ini

templateleft={#
templateright=#}
2013-08-19 10:41:09 +08:00
e02f9a9931 fix #161 2013-08-19 10:26:16 +08:00
aecf4af7f2 orm update docs 2013-08-17 07:41:22 +08:00
618cbf1e66 orm string type default will use varchar(255) 2013-08-16 22:24:10 +08:00
c81bbf9801 orm now use a filed named Id as default auto primary key, you can ignore orm:"auto" setting 2013-08-16 21:57:39 +08:00
f02b286ad4 orm make offset simple, can use any numeric type 2013-08-16 20:01:18 +08:00
0372b817d1 ServeJson ServeJsonp ServeXml and choice 2013-08-16 15:49:16 +08:00
a3363b0605 json change from Indent to none 2013-08-15 22:27:26 +08:00
51e625debf Merge pull request #156 from benlovell/spelling
Fix some minor spelling mistakes and typos
2013-08-14 00:17:35 -07:00
1977d87d55 Merge branch 'master' of https://github.com/astaxie/beego into spelling 2013-08-14 09:07:51 +02:00
9cbefacf52 Fixes some minor spelling mistakes and typos 2013-08-14 09:06:40 +02:00
42f1d1aeef change version 0.8 to 0.9 2013-08-14 10:53:22 +08:00
f4b3e7e4d2 orm small update 2013-08-13 19:33:43 +08:00
c6a436ed5d orm docs update 2013-08-13 19:28:37 +08:00
27b84841a7 orm add full regular go type support, such as int8, uint8, byte, rune. add date/datetime timezone support very well. 2013-08-13 17:17:19 +08:00
deb00809a5 orm add missed SetMaxIdleConns 2013-08-12 13:24:45 +08:00
eb06435f23 change position GOMAXPROCS to init that user can set own GOMAXPROCS 2013-08-12 12:46:59 +08:00
328f4566e4 Merge pull request #150 from miraclesu/form
Support custom label for renderform
2013-08-11 20:04:25 -07:00
a37b2bdfb0 Support custom label for renderform 2013-08-12 06:54:36 +08:00
50f3bd5835 add filter after 2013-08-12 00:14:42 +08:00
1f3ae3d682 Improve performance 2013-08-11 23:27:53 +08:00
ca1354e77f Merge pull request #147 from miraclesu/form
ignore struct field if form tag value is '-'
2013-08-11 07:44:28 -07:00
459b97858c renderform ignore struct field if form tag value is '-' 2013-08-11 22:42:35 +08:00
18c09bb2ed orm update docs 2013-08-11 22:27:54 +08:00
45345fa782 orm add postgres support 2013-08-11 22:27:45 +08:00
5c859466ef ignore struct field if form tag value is '-' 2013-08-11 22:21:31 +08:00
449fbe82f6 Update README.md 2013-08-11 13:26:28 +08:00
6c41e6dd78 orm add sqlite3 support, may be support postgres in next commit 2013-08-11 00:15:26 +08:00
9631c663d5 fix #145
this.DestroySession()
2013-08-10 23:58:25 +08:00
bbef213155 fix #144 2013-08-10 21:44:27 +08:00
bc060c95f8 Merge pull request #143 from miraclesu/form
Add renderform template function
2013-08-10 06:28:06 -07:00
9e1d5036f7 Add renderform template function 2013-08-10 16:33:46 +08:00
e47b2b677d Update ParserForm for new form tag style 2013-08-10 11:42:25 +08:00
38f6f8eef7 update TestParseForm 2013-08-10 10:29:29 +08:00
115b1d03db fix #138 2013-08-09 23:41:03 +08:00
0833d4baf8 fix #132 2013-08-09 22:19:05 +08:00
f2b359d8e8 orm full remove orm.Manager for simple use, add struct tag - for skip struct field 2013-08-09 20:14:18 +08:00
402932aa6e ... haha 2013-08-09 14:04:33 +08:00
f1e2372a56 orm update docs about debug log queries 2013-08-09 13:53:04 +08:00
45aa071261 orm add queries debug logger 2013-08-09 13:20:19 +08:00
8563000235 orm operator args now support multi types eg: []int []*int *int, Model *Model 2013-08-08 22:34:18 +08:00
9047d21ec5 orm fix, def a string in model but use int in db may cause nil pointer error 2013-08-08 22:15:27 +08:00
74a95f6cbf docs update 2013-08-08 11:07:08 +08:00
a17dcf4991 fix docs link 2013-08-07 23:39:18 +08:00
fc528c51a3 update docs 2013-08-07 23:35:45 +08:00
ad2965bbf9 update docs 2013-08-07 23:28:14 +08:00
37f8c6a04a zh docs update 2013-08-07 19:11:57 +08:00
46668b811f some fix / add test 2013-08-07 19:11:44 +08:00
10f4e822c3 add XSRFExpire 2013-08-07 11:22:23 +08:00
b191e96f51 Merge pull request #125 from miraclesu/valid
Change tag valid func default key
2013-08-06 08:22:56 -07:00
f9a31ea00a EnableXSRF 2013-08-06 23:21:52 +08:00
97d99fcef2 Change tag valid func default key 2013-08-06 23:15:20 +08:00
2fa534ff26 delete model move to orm 2013-08-06 16:40:23 +08:00
e47a147c3b serverJson Supoort 中文编码 2013-08-06 16:37:41 +08:00
4ecb9cc30b move httplib from beego to beego/httplib safemap support get all items 2013-08-06 16:13:45 +08:00
64ef8ad62b fix new version for memcahe client 2013-08-06 16:04:35 +08:00
6f2cd326bf Merge pull request #122 from pricees/master
Added Items() to return items from BeeCache
2013-08-04 23:15:37 -07:00
339346e307 Added Items() to return items from BeeCache 2013-08-05 00:47:37 -05:00
a611480b94 fix #121 2013-08-05 00:03:47 +08:00
fd3c8834da autorouter when /admin 301 to /admin/ 2013-08-04 23:13:29 +08:00
3d481178d7 improve router 2013-08-04 23:06:48 +08:00
d0cb112f4b fix test func name 2013-08-03 22:22:37 +08:00
c58445c772 add httplib support like http.client 2013-08-03 22:20:09 +08:00
f7dd376596 fix it , 2013-08-03 18:18:09 +08:00
5b3b6f7f48 add NewFlash func 2013-08-03 18:17:00 +08:00
0c2af58b8d fix fomat 2013-08-03 18:02:28 +08:00
0e0040e78d fix # 2013-08-03 18:00:57 +08:00
8ba5ea0ecf flash support 2013-08-03 17:55:53 +08:00
dbfd844ff2 beego support flash data 2013-08-03 17:55:53 +08:00
452478e779 Merge branch 'master' of github.com:astaxie/beego 2013-08-01 15:52:33 +08:00
6e06720e84 zh docs update 2013-08-01 15:52:05 +08:00
51baa35df1 now object crud is simple 2013-08-01 15:51:53 +08:00
6e2972673e Merge pull request #116 from lqixv/master
更新了部分部分文字
2013-08-01 00:25:19 -07:00
0ac7e342f0 Merge pull request #118 from miraclesu/test
Refactor template visit & Add template test
2013-07-31 22:29:04 -07:00
2a9852fa94 Add template test 2013-08-01 12:10:56 +08:00
250cbf593b fix values name 2013-08-01 12:09:17 +08:00
6fbdbaae80 Refactor template 2013-08-01 11:57:29 +08:00
5ccdaeb09e zh docs update 2013-08-01 09:23:44 +08:00
b0b64eb404 some change 2013-08-01 09:23:32 +08:00
831eeca7c8 zh docs update 2013-07-31 22:11:35 +08:00
2c5e062c2b some fix 2013-07-31 22:11:22 +08:00
c83d03c298 fix #117 2013-07-31 21:36:10 +08:00
485d89d5c8 tpl tolower 2013-07-30 22:45:50 +08:00
a997ca746f fix router's /path/ 2013-07-30 22:38:01 +08:00
572e281566 fix router's bug 2013-07-30 22:33:36 +08:00
8674b81b3a fix router & tpl tolower 2013-07-30 22:17:16 +08:00
bce35c708a init orm project, beta, unstable 2013-07-30 20:32:38 +08:00
ccbf116fd6 Merge pull request #115 from Xuyuanp/master
笔误
2013-07-30 05:17:44 -07:00
f26d81200b 笔误 2013-07-30 18:52:54 +08:00
71173aa010 Merge pull request #114 from Xuyuanp/master
bee command
2013-07-30 02:42:09 -07:00
4ee3d6aad4 bee command 2013-07-30 17:29:22 +08:00
4ffe988c30 update docs 2013-07-28 20:17:29 +08:00
df354acf97 Merge pull request #113 from miraclesu/valid
Support Match validate function for tag
2013-07-28 04:47:44 -07:00
6662eef2fd Support Match validate function for tag 2013-07-28 19:22:09 +08:00
dcdfaf36f1 Accept parameters more types 2013-07-28 16:59:35 +08:00
ae7e31717a Merge pull request #112 from miraclesu/form
Refactor ParseForm
2013-07-28 01:31:27 -07:00
fe7ecc377a Refactor ParseForm 2013-07-28 13:51:01 +08:00
29b1c8e1cb Merge pull request #111 from marswj/master
统一文档和代码中RunMode
2013-07-27 20:33:00 -07:00
ae906eed8f Update Quickstart.md 2013-07-28 11:14:54 +08:00
07ce3fb8ea Update Quickstart.md 2013-07-28 11:14:04 +08:00
1d7d6c6f99 Merge pull request #109 from miraclesu/valid
Add some validate functions
2013-07-27 06:12:23 -07:00
f490141217 Add some validate functions 2013-07-27 20:40:15 +08:00
0e748c6871 parse url to params
/object/login/2009/07/11
parse to ObjectController  Login function
params:map[0:2009 1:07 2:11]
2013-07-27 10:55:10 +08:00
f7e7fab6f2 support autorouter 2013-07-27 10:25:14 +08:00
fbde7df487 Merge pull request #106 from miraclesu/form
Fix utils test fail
2013-07-26 01:59:44 -07:00
914b6fa966 Fix utils test fail 2013-07-26 16:56:25 +08:00
69096b09f3 update zh docs 2013-07-26 16:41:11 +08:00
da5dd4d173 Merge pull request #105 from miraclesu/form
Add ParseForm Function & utils test
2013-07-26 01:36:13 -07:00
6b5dc3b7d5 Remove MarkDown test 2013-07-26 16:31:01 +08:00
d31ac49ead Merge branch 'master' of https://github.com/astaxie/beego into form 2013-07-26 16:29:22 +08:00
60afcd069a Add utils test 2013-07-26 16:29:00 +08:00
a39139c610 Update Quickstart.md
纠偏:从上面的例子中,并不能知道 DelSession(name string) 方法。
2013-07-26 09:09:09 +08:00
42370b5eb8 Update Quickstart.md
更正某些错别字,或语义不清
2013-07-26 08:31:46 +08:00
b99a09d73b Add two test cases for ParseForm 2013-07-25 22:50:29 +08:00
b8e06f6365 Add ParseForm function for *Controller 2013-07-25 22:27:25 +08:00
f552338822 Add ParseForm function 2013-07-25 22:25:12 +08:00
6373379da6 Merge pull request #103 from eXthen/master
I guess write would be better
2013-07-25 06:28:10 -07:00
ff11bcdb7c I guess write would be better 2013-07-25 19:04:26 +08:00
c2bb6b3068 Update log.go 2013-07-25 18:57:40 +08:00
259617f68d remove markdown 2013-07-25 16:40:37 +08:00
ab08aa9c9e MethodByName 2013-07-25 16:08:18 +08:00
7c610ee7c9 fix reflect find methodByName 2013-07-25 16:00:42 +08:00
23deaedd39 add to beego 2013-07-25 15:50:16 +08:00
538d39a704 add some tips 2013-07-25 15:46:47 +08:00
b961abb52b update docs 2013-07-25 15:41:25 +08:00
b32b12b208 add some comments 2013-07-25 15:40:33 +08:00
e88c2be013 update docs & update beego's version 2013-07-25 15:37:38 +08:00
d5ddd0a9dd support user define function
+//Add("/user",&UserController{})
+//Add("/api/list",&RestController{},"*:ListFood")
+//Add("/api/create",&RestController{},"post:CreateFood")
+//Add("/api/update",&RestController{},"put:UpdateFood")
+//Add("/api/delete",&RestController{},"delete:DeleteFood")
+//Add("/api",&RestController{},"get,post:ApiFunc")
+//Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc")
2013-07-25 15:17:09 +08:00
dff36a18a2 Merge pull request #101 from miraclesu/valid
Valid
2013-07-23 21:44:35 -07:00
fb78d83ec3 Merge branch 'master' of https://github.com/astaxie/beego into valid 2013-07-24 12:37:07 +08:00
d23700b919 update README 2013-07-24 12:36:46 +08:00
92db56c0cb add struct tag support 2013-07-24 12:20:42 +08:00
4c6163baa0 add funcmap 2013-07-24 01:20:24 +08:00
f46388fa63 setcookie set to unique. fix multi setcookie 2013-07-23 21:54:45 +08:00
aba1728bc3 add some util funcs 2013-07-23 14:42:14 +08:00
ddb9ed39a5 add validation README 2013-07-22 17:40:32 +08:00
a242f61b8e Merge pull request #100 from miraclesu/valid
Valid
2013-07-21 19:26:43 -07:00
d19de30d9c add test 2013-07-21 23:46:18 +08:00
6d05163c9f add validation funcs 2013-07-21 01:37:24 +08:00
a41cd17092 add validators 2013-07-19 16:49:28 +08:00
ec7324e972 Merge pull request #98 from Unknwon/master
Fixed bug: error page cannot show correct corresponding status code
2013-07-18 01:20:16 -07:00
7f5dd13422 Fixed bug: error page cannot show correct corresponding status code 2013-07-18 14:42:45 +08:00
7f4ad7ff46 fix #91 2013-07-16 19:05:44 +08:00
60200689f4 fix setcookie time type 2013-07-11 10:57:34 +08:00
af3797e16c Merge pull request #93 from Unknwon/master
Sync documentation of English with Chinese version.
2013-07-10 07:12:07 -07:00
d4743fb10d Sync documentation of English with Chinese version 2013-07-10 22:06:28 +08:00
38b083e117 add docs about how to write api application 2013-07-09 16:43:03 +08:00
fece5adc2a add example for api application 2013-07-09 13:59:47 +08:00
7bfb4126d7 support copy requestbody 2013-07-08 23:12:31 +08:00
2abe584bc5 support restful router 2013-07-08 18:35:10 +08:00
ee9223b1b9 fix #18
func (this *MainController) Get() {
this.GoToFunc("Test")
}

func (this *MainController) Test() {
this.Ctx.WriteString("testtest")
}
2013-07-08 17:35:09 +08:00
d2a16ff8f6 fix #26 add xsrf function 2013-07-08 16:17:08 +08:00
f1e5059682 fix #69 refer to http://www.php.net/manual/zh/function.setcookie.php 2013-07-08 15:13:51 +08:00
11977f4f77 fix #90 2013-07-07 17:58:50 +08:00
461eac46b9 fix #89 2013-07-07 17:45:39 +08:00
75af664511 change r.ParseMultipartForm position 2013-07-04 23:41:35 +08:00
174298b497 fix cache's bug expird is not changed by get method 2013-07-04 13:02:11 +08:00
9b392a0601 update to hotupdate's connet timeout 2013-07-03 16:58:15 +08:00
bf9de3bcf6 add HttpServerTimeOut setting 2013-07-03 15:29:54 +08:00
189df1280c fix #87 2013-07-02 09:45:12 +08:00
8807c327d1 fix log delete 2013-06-29 14:39:02 +08:00
d627ec013e add config to countol if enable hotupdate 2013-06-28 22:09:08 +08:00
d0bbc67b27 Merge pull request #86 from slene/master
fix logrotate
2013-06-28 06:18:51 -07:00
453557948e fix logrotate close fd before rename file, add a MuxWriter for Logger 2013-06-27 22:04:01 +08:00
4033692dcb Merge pull request #85 from Unknwon/master
sync quickstart.
2013-06-26 18:45:40 -07:00
236f28c53c sync quickstart. 2013-06-27 00:24:06 +08:00
b2bfed8937 fix close err 2013-06-26 23:34:32 +08:00
71adbdd7d7 add mutex 2013-06-26 22:16:03 +08:00
573df2e747 add isclose call close many times 2013-06-26 22:13:25 +08:00
aa9cb6d052 delete session's cookie Expires 2013-06-25 23:08:47 +08:00
8358e0ff48 Merge pull request #83 from shxsun/master
In go version 1.0.3 will call build error
2013-06-25 07:08:35 -07:00
1687ec85de fix build problem 2013-06-25 21:40:42 +08:00
d5fc0a4bda Merge pull request #82 from matrixik/master
Change: SetRotateMaxDay => SetRotateMaxDays
2013-06-25 04:08:51 -07:00
3bcff77947 Change: SetRotateMaxDay => SetRotateMaxDays 2013-06-25 12:54:51 +02:00
1521842d7a add static global function & add seocms example 2013-06-25 18:49:08 +08:00
b04813e472 fix #80
add a function to delete default StaticPath
2013-06-25 17:21:19 +08:00
9e41d93184 delete strcut map
I think if user should set field in controller, there's no need to have
thie feature
2013-06-25 16:44:53 +08:00
309f2e199b add tag to 0.7.0 2013-06-25 11:42:18 +08:00
26baf0f283 update docs for logs & DirectoryIndex 2013-06-25 11:36:25 +08:00
23ff422826 if exist file then read lines err 2013-06-25 11:19:03 +08:00
d99ae3e3a2 add logrotate's setting for rotate 2013-06-25 08:13:06 +08:00
4b09175d27 fix error info tempalte's title 2013-06-24 23:59:06 +08:00
512ddf8a70 fix small bug for not return 2013-06-24 23:56:53 +08:00
73d45b150c log support file & logrotate 2013-06-24 23:24:33 +08:00
c5a23d5cde fix #81 2013-06-24 23:24:33 +08:00
3578bb3287 fix bug substr 2013-06-24 23:24:33 +08:00
40c7b47c24 Merge pull request #76 from matrixik/patch-1
Update: README
2013-06-22 07:49:03 -07:00
f56e7737ce Update: README 2013-06-22 13:17:50 +02:00
86ddfe5375 Merge pull request #75 from matrixik/master
Small addition to supervisord en docs
2013-06-21 07:38:40 -07:00
1c4d0d72ca Update: supervisord en docs 2013-06-21 15:47:58 +02:00
20e101a42d Merge pull request #74 from matrixik/master
Clean and fix docs markdown
2013-06-21 01:59:53 -07:00
10417dd6d5 Update: clean and fix README markdown 2013-06-21 10:40:58 +02:00
541d7f71b9 Update: clean and fix docs markdown 2013-06-21 10:35:46 +02:00
fdb94aeba0 Fix: install en docs links 2013-06-21 08:59:23 +02:00
67511deea7 Merge pull request #73 from matrixik/master
Some docs updates
2013-06-20 07:28:19 -07:00
43f588b5c1 Fix: session README 2013-06-20 15:25:39 +03:00
cc6c84b317 Update: session README 2013-06-20 14:22:06 +02:00
b68a37a115 Some updates to cache README 2013-06-20 13:58:35 +02:00
b886ed59a5 Update cache docs 2013-06-20 14:37:26 +03:00
3475685233 add Server info 2013-06-20 15:49:43 +08:00
0abe3e2157 add build status 2013-06-19 22:27:27 +08:00
25cf08159a Merge pull request #72 from Unknwon/master
documentation for hot update
2013-06-18 03:08:37 -07:00
28d0738cd1 doc_en:hot update 2013-06-18 18:02:51 +08:00
6a260ca76d Merge pull request #71 from yecrane/master
form获取相同名字的对象数组值
2013-06-17 01:03:04 -07:00
27896c5700 Update controller.go
form获取相同名字的对象数组值
2013-06-17 16:12:35 +09:00
384fa8fda9 Merge pull request #70 from luw2007/luw2007
fix jsonp, use Buffer
2013-06-13 18:50:26 -07:00
lw
5d531d3da6 fix jsonp, use Buffer 2013-06-14 09:39:56 +08:00
ea756b68c4 Merge pull request #68 from luw2007/luw2007
feture: add jsonp
2013-06-13 02:43:33 -07:00
lw
06e002cf2c feture: add jsonp 2013-06-13 17:17:54 +08:00
73f0f4f31f Merge pull request #59 from lunny/master
Add a basic map for url params to controller properties.
2013-06-12 08:48:43 -07:00
b2abb4eb80 fix #66 2013-06-11 23:31:40 +08:00
951881b96d update typo 2013-06-07 23:36:01 +08:00
832f160cc8 update 2013-06-07 23:33:45 +08:00
2bbd21ab07 update docs for hotupdate 2013-06-07 22:45:58 +08:00
455367769e support hot-update 2013-06-07 17:48:14 +08:00
f20ad0916f bug fix 2013-06-04 19:45:48 +08:00
be9290364d Merge pull request #62 from coderhaoxin/master
add .gitignore file
2013-06-01 00:39:52 -07:00
4577bfb980 add git ignore file 2013-06-01 13:31:14 +08:00
65041aae71 remove nonuse parseForm and change the StructMap's params 2013-05-31 14:07:06 +08:00
8b7cba037e add url params -> controller's member feature 2013-05-28 16:43:23 +08:00
2dee30183d add structmap function for controller 2013-05-28 13:58:42 +08:00
4fc49b5803 Merge pull request #58 from yecrane/master
修改redis重启后不会自动连接
2013-05-22 07:28:34 -07:00
67695fac9e Update sess_redis.go
修改redis重启后不会自动连接
2013-05-22 22:26:26 +08:00
22510bc272 Merge pull request #56 from yecrane/master
修改redis连接池,设置key,获取key的方式
2013-05-14 18:52:33 -07:00
a2dd859e55 Update sess_redis.go 2013-05-14 23:55:50 +08:00
4d06fec52e Merge pull request #55 from Unknwon/master
sync qucikstart.md to ZH version.
2013-05-13 22:23:32 -07:00
0e916a00f9 sync. quick start.md with ZH version. 2013-05-14 01:08:20 -04:00
ea36f80470 fix typo 2013-05-14 11:35:07 +08:00
1745dd2261 Merge pull request #54 from slene/master
fix session file write bug
2013-05-13 18:31:05 -07:00
6d3486e2a6 fix session file write bug 2013-05-14 07:33:00 +08:00
196 changed files with 33650 additions and 4649 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.idea
.DS_Store
*.swp
*.swo

16
.go_style Normal file
View File

@ -0,0 +1,16 @@
{
"file_line": 500,
"func_line": 80,
"params_num":4,
"results_num":3,
"formated": true,
"pkg_name": true,
"camel_name":true,
"ignore":[
"a/*",
"b/*/c/*.go"
],
"fatal":[
"formated"
]
}

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
Copyright 2014 astaxie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,32 +1,37 @@
## beego
beego is a Go Framework which is inspired from tornado and sinatra.
## Beego
It is a simply & powerful web framework.
[![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest)
[![GoDoc](https://godoc.org/github.com/astaxie/beego?status.svg)](https://godoc.org/github.com/astaxie/beego)
Have this features:
beego is an open-source, high-performance, modularity, full-stack web framework.
* RESTFul support
* MVC architecture
* Session support memory/file/redis/mysql
* Cache support memory/redis/memcache
* Global Config
* Intelligent routing
* thread safe map
* Friendly error display
* Useful template functions
More info [beego.me](http://beego.me)
[English Documents](https://github.com/astaxie/beego/tree/master/docs/en)
## Installation
[API](http://gowalker.org/github.com/astaxie/beego)
go get github.com/astaxie/beego
[中文文档](https://github.com/astaxie/beego/tree/master/docs/zh)
## Features
* RESTful support
* MVC architecture
* Modularity
* Auto API documents
* Annotation router
* Namespace
* Powerful develop tools
* Full stack for Web & API
## Documentation
* [English](http://beego.me/docs/intro/)
* [中文文档](http://beego.me/docs/intro/)
## Community
* [http://beego.me/community](http://beego.me/community)
## LICENSE
beego is licensed under the Apache Licence, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0.html).
## Use Case
- API documentation [gowalker](https://github.com/Unknwon/gowalker)
- CMS [toropress](https://github.com/insionng/toropress)

465
admin.go Normal file
View File

@ -0,0 +1,465 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"text/template"
"time"
"github.com/astaxie/beego/toolbox"
"github.com/astaxie/beego/utils"
)
// BeeAdminApp is the default adminApp used by admin module.
var beeAdminApp *adminApp
// FilterMonitorFunc is default monitor filter when admin module is enable.
// if this func returns, admin module records qbs for this request by condition of this function logic.
// usage:
// func MyFilterMonitor(method, requestPath string, t time.Duration) bool {
// if method == "POST" {
// return false
// }
// if t.Nanoseconds() < 100 {
// return false
// }
// if strings.HasPrefix(requestPath, "/astaxie") {
// return false
// }
// return true
// }
// beego.FilterMonitorFunc = MyFilterMonitor.
var FilterMonitorFunc func(string, string, time.Duration) bool
func init() {
beeAdminApp = &adminApp{
routers: make(map[string]http.HandlerFunc),
}
beeAdminApp.Route("/", adminIndex)
beeAdminApp.Route("/qps", qpsIndex)
beeAdminApp.Route("/prof", profIndex)
beeAdminApp.Route("/healthcheck", healthcheck)
beeAdminApp.Route("/task", taskStatus)
beeAdminApp.Route("/listconf", listConf)
FilterMonitorFunc = func(string, string, time.Duration) bool { return true }
}
// AdminIndex is the default http.Handler for admin module.
// it matches url pattern "/".
func adminIndex(rw http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(indexTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
data := make(map[interface{}]interface{})
tmpl.Execute(rw, data)
}
// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter.
// it's registered with url pattern "/qbs" in admin module.
func qpsIndex(rw http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(qpsTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
data := make(map[interface{}]interface{})
data["Content"] = toolbox.StatisticsMap.GetMap()
tmpl.Execute(rw, data)
}
// ListConf is the http.Handler of displaying all beego configuration values as key/value pair.
// it's registered with url pattern "/listconf" in admin module.
func listConf(rw http.ResponseWriter, r *http.Request) {
r.ParseForm()
command := r.Form.Get("command")
if command != "" {
data := make(map[interface{}]interface{})
switch command {
case "conf":
m := make(map[string]interface{})
m["AppName"] = AppName
m["AppPath"] = AppPath
m["AppConfigPath"] = AppConfigPath
m["StaticDir"] = StaticDir
m["StaticExtensionsToGzip"] = StaticExtensionsToGzip
m["HttpAddr"] = HttpAddr
m["HttpPort"] = HttpPort
m["HttpTLS"] = EnableHttpTLS
m["HttpCertFile"] = HttpCertFile
m["HttpKeyFile"] = HttpKeyFile
m["RecoverPanic"] = RecoverPanic
m["AutoRender"] = AutoRender
m["ViewsPath"] = ViewsPath
m["RunMode"] = RunMode
m["SessionOn"] = SessionOn
m["SessionProvider"] = SessionProvider
m["SessionName"] = SessionName
m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
m["SessionSavePath"] = SessionSavePath
m["SessionHashFunc"] = SessionHashFunc
m["SessionHashKey"] = SessionHashKey
m["SessionCookieLifeTime"] = SessionCookieLifeTime
m["UseFcgi"] = UseFcgi
m["MaxMemory"] = MaxMemory
m["EnableGzip"] = EnableGzip
m["DirectoryIndex"] = DirectoryIndex
m["HttpServerTimeOut"] = HttpServerTimeOut
m["ErrorsShow"] = ErrorsShow
m["XSRFKEY"] = XSRFKEY
m["EnableXSRF"] = EnableXSRF
m["XSRFExpire"] = XSRFExpire
m["CopyRequestBody"] = CopyRequestBody
m["TemplateLeft"] = TemplateLeft
m["TemplateRight"] = TemplateRight
m["BeegoServerName"] = BeegoServerName
m["EnableAdmin"] = EnableAdmin
m["AdminHttpAddr"] = AdminHttpAddr
m["AdminHttpPort"] = AdminHttpPort
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(configTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
data["Content"] = m
tmpl.Execute(rw, data)
case "router":
content := make(map[string]interface{})
var fields = []string{
fmt.Sprintf("Router Pattern"),
fmt.Sprintf("Methods"),
fmt.Sprintf("Controller"),
}
content["Fields"] = fields
methods := []string{}
methodsData := make(map[string]interface{})
for method, t := range BeeApp.Handlers.routers {
resultList := new([][]string)
printTree(resultList, t)
methods = append(methods, method)
methodsData[method] = resultList
}
content["Data"] = methodsData
content["Methods"] = methods
data["Content"] = content
data["Title"] = "Routers"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(routerAndFilterTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
tmpl.Execute(rw, data)
case "filter":
content := make(map[string]interface{})
var fields = []string{
fmt.Sprintf("Router Pattern"),
fmt.Sprintf("Filter Function"),
}
content["Fields"] = fields
filterTypes := []string{}
filterTypeData := make(map[string]interface{})
if BeeApp.Handlers.enableFilter {
var filterType string
if bf, ok := BeeApp.Handlers.filters[BeforeRouter]; ok {
filterType = "Before Router"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf {
var result = []string{
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
filterTypeData[filterType] = resultList
}
if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
filterType = "Before Exec"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf {
var result = []string{
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
filterTypeData[filterType] = resultList
}
if bf, ok := BeeApp.Handlers.filters[AfterExec]; ok {
filterType = "After Exec"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf {
var result = []string{
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
filterTypeData[filterType] = resultList
}
if bf, ok := BeeApp.Handlers.filters[FinishRouter]; ok {
filterType = "Finish Router"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf {
var result = []string{
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
filterTypeData[filterType] = resultList
}
}
content["Data"] = filterTypeData
content["Methods"] = filterTypes
data["Content"] = content
data["Title"] = "Filters"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(routerAndFilterTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
tmpl.Execute(rw, data)
default:
rw.Write([]byte("command not support"))
}
} else {
}
}
func printTree(resultList *[][]string, t *Tree) {
for _, tr := range t.fixrouters {
printTree(resultList, tr)
}
if t.wildcard != nil {
printTree(resultList, t.wildcard)
}
for _, l := range t.leaves {
if v, ok := l.runObject.(*controllerInfo); ok {
if v.routerType == routerTypeBeego {
var result = []string{
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf("%s", v.methods),
fmt.Sprintf("%s", v.controllerType),
}
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeRESTFul {
var result = []string{
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf("%s", v.methods),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeHandler {
var result = []string{
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf(""),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
}
}
}
}
// ProfIndex is a http.Handler for showing profile command.
// it's in url pattern "/prof" in admin module.
func profIndex(rw http.ResponseWriter, r *http.Request) {
r.ParseForm()
command := r.Form.Get("command")
format := r.Form.Get("format")
data := make(map[string]interface{})
var result bytes.Buffer
if command != "" {
toolbox.ProcessInput(command, &result)
data["Content"] = result.String()
if format == "json" && command == "gc summary" {
dataJson, err := json.Marshal(data)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
rw.Header().Set("Content-Type", "application/json")
rw.Write(dataJson)
return
}
data["Title"] = command
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(profillingTpl))
if command == "gc summary" {
tmpl = template.Must(tmpl.Parse(gcAjaxTpl))
} else {
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
}
tmpl.Execute(rw, data)
} else {
}
}
// Healthcheck is a http.Handler calling health checking and showing the result.
// it's in "/healthcheck" pattern in admin module.
func healthcheck(rw http.ResponseWriter, req *http.Request) {
data := make(map[interface{}]interface{})
var result = []string{}
fields := []string{
fmt.Sprintf("Name"),
fmt.Sprintf("Message"),
fmt.Sprintf("Status"),
}
resultList := new([][]string)
content := make(map[string]interface{})
for name, h := range toolbox.AdminCheckList {
if err := h.Check(); err != nil {
result = []string{
fmt.Sprintf("error"),
fmt.Sprintf("%s", name),
fmt.Sprintf("%s", err.Error()),
}
} else {
result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("%s", name),
fmt.Sprintf("OK"),
}
}
*resultList = append(*resultList, result)
}
content["Fields"] = fields
content["Data"] = resultList
data["Content"] = content
data["Title"] = "Health Check"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(healthCheckTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
tmpl.Execute(rw, data)
}
// TaskStatus is a http.Handler with running task status (task name, status and the last execution).
// it's in "/task" pattern in admin module.
func taskStatus(rw http.ResponseWriter, req *http.Request) {
data := make(map[interface{}]interface{})
// Run Task
req.ParseForm()
taskname := req.Form.Get("taskname")
if taskname != "" {
if t, ok := toolbox.AdminTaskList[taskname]; ok {
err := t.Run()
if err != nil {
data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
}
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is %s", taskname, t.GetStatus())}
} else {
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
}
}
// List Tasks
content := make(map[string]interface{})
resultList := new([][]string)
var result = []string{}
var fields = []string{
fmt.Sprintf("Task Name"),
fmt.Sprintf("Task Spec"),
fmt.Sprintf("Task Function"),
fmt.Sprintf(""),
}
for tname, tk := range toolbox.AdminTaskList {
result = []string{
fmt.Sprintf("%s", tname),
fmt.Sprintf("%s", tk.GetStatus()),
fmt.Sprintf("%s", tk.GetPrev().String()),
}
*resultList = append(*resultList, result)
}
content["Fields"] = fields
content["Data"] = resultList
data["Content"] = content
data["Title"] = "Tasks"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(tasksTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
tmpl.Execute(rw, data)
}
// adminApp is an http.HandlerFunc map used as beeAdminApp.
type adminApp struct {
routers map[string]http.HandlerFunc
}
// Route adds http.HandlerFunc to adminApp with url pattern.
func (admin *adminApp) Route(pattern string, f http.HandlerFunc) {
admin.routers[pattern] = f
}
// Run adminApp http server.
// Its addr is defined in configuration file as adminhttpaddr and adminhttpport.
func (admin *adminApp) Run() {
if len(toolbox.AdminTaskList) > 0 {
toolbox.StartTask()
}
addr := AdminHttpAddr
if AdminHttpPort != 0 {
addr = fmt.Sprintf("%s:%d", AdminHttpAddr, AdminHttpPort)
}
for p, f := range admin.routers {
http.Handle(p, f)
}
err := http.ListenAndServe(addr, nil)
if err != nil {
BeeLogger.Critical("Admin ListenAndServe: ", err)
}
}

355
adminui.go Normal file

File diff suppressed because one or more lines are too long

104
app.go Normal file
View File

@ -0,0 +1,104 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"fmt"
"net"
"net/http"
"net/http/fcgi"
"time"
"github.com/astaxie/beego/context"
)
// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)
// App defines beego application with a new PatternServeMux.
type App struct {
Handlers *ControllerRegistor
Server *http.Server
}
// NewApp returns a new beego application.
func NewApp() *App {
cr := NewControllerRegister()
app := &App{Handlers: cr, Server: &http.Server{}}
return app
}
// Run beego application.
func (app *App) Run() {
addr := HttpAddr
if HttpPort != 0 {
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
}
BeeLogger.Info("Running on %s", addr)
var (
err error
l net.Listener
)
endRunning := make(chan bool, 1)
if UseFcgi {
if HttpPort == 0 {
l, err = net.Listen("unix", addr)
} else {
l, err = net.Listen("tcp", addr)
}
if err != nil {
BeeLogger.Critical("Listen: ", err)
}
err = fcgi.Serve(l, app.Handlers)
} else {
app.Server.Addr = addr
app.Server.Handler = app.Handlers
app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second
app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second
if EnableHttpTLS {
go func() {
time.Sleep(20 * time.Microsecond)
if HttpsPort != 0 {
app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
}
err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
if err != nil {
BeeLogger.Critical("ListenAndServeTLS: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}()
}
if EnableHttpListen {
go func() {
app.Server.Addr = addr
err := app.Server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}()
}
}
<-endRunning
}

578
beego.go
View File

@ -1,208 +1,428 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// beego is an open-source, high-performance, modularity, full-stack web framework
//
// package main
//
// import "github.com/astaxie/beego"
//
// func main() {
// beego.Run()
// }
//
// more infomation: http://beego.me
package beego
import (
"fmt"
"github.com/astaxie/beego/session"
"html/template"
"net"
"net/http"
"net/http/fcgi"
"os"
"path"
"runtime"
"path/filepath"
"strconv"
"strings"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/session"
)
const VERSION = "0.6.0"
// beego web framework version.
const VERSION = "1.4.1"
var (
BeeApp *App
AppName string
AppPath string
AppConfigPath string
StaticDir map[string]string
TemplateCache map[string]*template.Template
HttpAddr string
HttpPort int
RecoverPanic bool
AutoRender bool
PprofOn bool
ViewsPath string
RunMode string //"dev" or "prod"
AppConfig *Config
//related to session
GlobalSessions *session.Manager //GlobalSessions
SessionOn bool // wheather auto start session,default is false
SessionProvider string // default session provider memory mysql redis
SessionName string // sessionName cookie's name
SessionGCMaxLifetime int64 // session's gc maxlifetime
SessionSavePath string // session savepath if use mysql/redis/file this set to the connectinfo
UseFcgi bool
MaxMemory int64
EnableGzip bool // enable gzip
)
type hookfunc func() error //hook function to run
var hooks []hookfunc //hook function slice to store the hookfunc
func init() {
os.Chdir(path.Dir(os.Args[0]))
BeeApp = NewApp()
AppPath, _ = os.Getwd()
StaticDir = make(map[string]string)
TemplateCache = make(map[string]*template.Template)
HttpAddr = ""
HttpPort = 8080
AppName = "beego"
RunMode = "dev" //default runmod
AutoRender = true
RecoverPanic = true
PprofOn = false
ViewsPath = "views"
SessionOn = false
SessionProvider = "memory"
SessionName = "beegosessionID"
SessionGCMaxLifetime = 3600
SessionSavePath = ""
UseFcgi = false
MaxMemory = 1 << 26 //64MB
EnableGzip = false
StaticDir["/static"] = "static"
AppConfigPath = path.Join(AppPath, "conf", "app.conf")
ParseConfig()
type groupRouter struct {
pattern string
controller ControllerInterface
mappingMethods string
}
type App struct {
Handlers *ControllerRegistor
// RouterGroups which will store routers
type GroupRouters []groupRouter
// Get a new GroupRouters
func NewGroupRouters() GroupRouters {
return make(GroupRouters, 0)
}
// New returns a new PatternServeMux.
func NewApp() *App {
cr := NewControllerRegistor()
app := &App{Handlers: cr}
return app
}
func (app *App) Run() {
addr := fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
var err error
if UseFcgi {
l, e := net.Listen("tcp", addr)
if e != nil {
BeeLogger.Fatal("Listen: ", e)
// Add Router in the GroupRouters
// it is for plugin or module to register router
func (gr *GroupRouters) AddRouter(pattern string, c ControllerInterface, mappingMethod ...string) {
var newRG groupRouter
if len(mappingMethod) > 0 {
newRG = groupRouter{
pattern,
c,
mappingMethod[0],
}
err = fcgi.Serve(l, app.Handlers)
} else {
err = http.ListenAndServe(addr, app.Handlers)
}
if err != nil {
BeeLogger.Fatal("ListenAndServe: ", err)
}
}
func (app *App) Router(path string, c ControllerInterface) *App {
app.Handlers.Add(path, c)
return app
}
func (app *App) Filter(filter http.HandlerFunc) *App {
app.Handlers.Filter(filter)
return app
}
func (app *App) FilterParam(param string, filter http.HandlerFunc) *App {
app.Handlers.FilterParam(param, filter)
return app
}
func (app *App) FilterPrefixPath(path string, filter http.HandlerFunc) *App {
app.Handlers.FilterPrefixPath(path, filter)
return app
}
func (app *App) SetViewsPath(path string) *App {
ViewsPath = path
return app
}
func (app *App) SetStaticPath(url string, path string) *App {
StaticDir[url] = path
return app
}
func (app *App) ErrorLog(ctx *Context) {
BeeLogger.Printf("[ERR] host: '%s', request: '%s %s', proto: '%s', ua: '%s', remote: '%s'\n", ctx.Request.Host, ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.Proto, ctx.Request.UserAgent(), ctx.Request.RemoteAddr)
}
func (app *App) AccessLog(ctx *Context) {
BeeLogger.Printf("[ACC] host: '%s', request: '%s %s', proto: '%s', ua: '%s', remote: '%s'\n", ctx.Request.Host, ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.Proto, ctx.Request.UserAgent(), ctx.Request.RemoteAddr)
}
func RegisterController(path string, c ControllerInterface) *App {
BeeApp.Router(path, c)
return BeeApp
}
func Router(path string, c ControllerInterface) *App {
BeeApp.Router(path, c)
return BeeApp
}
func RouterHandler(path string, c http.Handler) *App {
BeeApp.Handlers.AddHandler(path, c)
return BeeApp
}
func Errorhandler(err string, h http.HandlerFunc) *App {
ErrorMaps[err] = h
return BeeApp
}
func SetViewsPath(path string) *App {
BeeApp.SetViewsPath(path)
return BeeApp
}
func SetStaticPath(url string, path string) *App {
StaticDir[url] = path
return BeeApp
}
func Filter(filter http.HandlerFunc) *App {
BeeApp.Filter(filter)
return BeeApp
}
func FilterParam(param string, filter http.HandlerFunc) *App {
BeeApp.FilterParam(param, filter)
return BeeApp
}
func FilterPrefixPath(path string, filter http.HandlerFunc) *App {
BeeApp.FilterPrefixPath(path, filter)
return BeeApp
}
func Run() {
if AppConfigPath != path.Join(AppPath, "conf", "app.conf") {
err := ParseConfig()
if err != nil {
if RunMode == "dev" {
Warn(err)
}
newRG = groupRouter{
pattern,
c,
"",
}
}
if PprofOn {
BeeApp.Router(`/debug/pprof`, &ProfController{})
BeeApp.Router(`/debug/pprof/:pp([\w]+)`, &ProfController{})
*gr = append(*gr, newRG)
}
func (gr *GroupRouters) AddAuto(c ControllerInterface) {
newRG := groupRouter{
"",
c,
"",
}
*gr = append(*gr, newRG)
}
// AddGroupRouter with the prefix
// it will register the router in BeeApp
// the follow code is write in modules:
// GR:=NewGroupRouters()
// GR.AddRouter("/login",&UserController,"get:Login")
// GR.AddRouter("/logout",&UserController,"get:Logout")
// GR.AddRouter("/register",&UserController,"get:Reg")
// the follow code is write in app:
// import "github.com/beego/modules/auth"
// AddRouterGroup("/admin", auth.GR)
func AddGroupRouter(prefix string, groups GroupRouters) *App {
for _, v := range groups {
if v.pattern == "" {
BeeApp.Handlers.AddAutoPrefix(prefix, v.controller)
} else if v.mappingMethods != "" {
BeeApp.Handlers.Add(prefix+v.pattern, v.controller, v.mappingMethods)
} else {
BeeApp.Handlers.Add(prefix+v.pattern, v.controller)
}
}
return BeeApp
}
// Router adds a patterned controller handler to BeeApp.
// it's an alias method of App.Router.
// usage:
// simple router
// beego.Router("/admin", &admin.UserController{})
// beego.Router("/admin/index", &admin.ArticleController{})
//
// regex router
//
// beego.Router("/api/:id([0-9]+)", &controllers.RController{})
//
// custom rules
// beego.Router("/api/list",&RestController{},"*:ListFood")
// beego.Router("/api/create",&RestController{},"post:CreateFood")
// beego.Router("/api/update",&RestController{},"put:UpdateFood")
// beego.Router("/api/delete",&RestController{},"delete:DeleteFood")
func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
BeeApp.Handlers.Add(rootpath, c, mappingMethods...)
return BeeApp
}
// Router add list from
// usage:
// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
// type BankAccount struct{
// beego.Controller
// }
//
// register the function
// func (b *BankAccount)Mapping(){
// b.Mapping("ShowAccount" , b.ShowAccount)
// b.Mapping("ModifyAccount", b.ModifyAccount)
//}
//
// //@router /account/:id [get]
// func (b *BankAccount) ShowAccount(){
// //logic
// }
//
//
// //@router /account/:id [post]
// func (b *BankAccount) ModifyAccount(){
// //logic
// }
//
// the comments @router url methodlist
// url support all the function Router's pattern
// methodlist [get post head put delete options *]
func Include(cList ...ControllerInterface) *App {
BeeApp.Handlers.Include(cList...)
return BeeApp
}
// RESTRouter adds a restful controller handler to BeeApp.
// its' controller implements beego.ControllerInterface and
// defines a param "pattern/:objectId" to visit each resource.
func RESTRouter(rootpath string, c ControllerInterface) *App {
Router(rootpath, c)
Router(path.Join(rootpath, ":objectId"), c)
return BeeApp
}
// AutoRouter adds defined controller handler to BeeApp.
// it's same to App.AutoRouter.
// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
// visit the url /main/list to exec List function or /main/page to exec Page function.
func AutoRouter(c ControllerInterface) *App {
BeeApp.Handlers.AddAuto(c)
return BeeApp
}
// AutoPrefix adds controller handler to BeeApp with prefix.
// it's same to App.AutoRouterWithPrefix.
// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
func AutoPrefix(prefix string, c ControllerInterface) *App {
BeeApp.Handlers.AddAutoPrefix(prefix, c)
return BeeApp
}
// register router for Get method
// usage:
// beego.Get("/", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Get(rootpath string, f FilterFunc) *App {
BeeApp.Handlers.Get(rootpath, f)
return BeeApp
}
// register router for Post method
// usage:
// beego.Post("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Post(rootpath string, f FilterFunc) *App {
BeeApp.Handlers.Post(rootpath, f)
return BeeApp
}
// register router for Delete method
// usage:
// beego.Delete("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Delete(rootpath string, f FilterFunc) *App {
BeeApp.Handlers.Delete(rootpath, f)
return BeeApp
}
// register router for Put method
// usage:
// beego.Put("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Put(rootpath string, f FilterFunc) *App {
BeeApp.Handlers.Put(rootpath, f)
return BeeApp
}
// register router for Head method
// usage:
// beego.Head("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Head(rootpath string, f FilterFunc) *App {
BeeApp.Handlers.Head(rootpath, f)
return BeeApp
}
// register router for Options method
// usage:
// beego.Options("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Options(rootpath string, f FilterFunc) *App {
BeeApp.Handlers.Options(rootpath, f)
return BeeApp
}
// register router for Patch method
// usage:
// beego.Patch("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Patch(rootpath string, f FilterFunc) *App {
BeeApp.Handlers.Patch(rootpath, f)
return BeeApp
}
// register router for all method
// usage:
// beego.Any("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Any(rootpath string, f FilterFunc) *App {
BeeApp.Handlers.Any(rootpath, f)
return BeeApp
}
// register router for own Handler
// usage:
// beego.Handler("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
BeeApp.Handlers.Handler(rootpath, h, options...)
return BeeApp
}
// ErrorHandler registers http.HandlerFunc to each http err code string.
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
func Errorhandler(err string, h http.HandlerFunc) *App {
middleware.Errorhandler(err, h)
return BeeApp
}
// SetViewsPath sets view directory path in beego application.
func SetViewsPath(path string) *App {
ViewsPath = path
return BeeApp
}
// SetStaticPath sets static directory path and proper url pattern in beego application.
// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public".
func SetStaticPath(url string, path string) *App {
if !strings.HasPrefix(url, "/") {
url = "/" + url
}
url = strings.TrimRight(url, "/")
StaticDir[url] = path
return BeeApp
}
// DelStaticPath removes the static folder setting in this url pattern in beego application.
func DelStaticPath(url string) *App {
delete(StaticDir, url)
return BeeApp
}
// InsertFilter adds a FilterFunc with pattern condition and action constant.
// The pos means action constant including
// beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
func InsertFilter(pattern string, pos int, filter FilterFunc) *App {
BeeApp.Handlers.InsertFilter(pattern, pos, filter)
return BeeApp
}
// The hookfunc will run in beego.Run()
// such as sessionInit, middlerware start, buildtemplate, admin start
func AddAPPStartHook(hf hookfunc) {
hooks = append(hooks, hf)
}
// Run beego application.
// beego.Run() default run on HttpPort
// beego.Run(":8089")
// beego.Run("127.0.0.1:8089")
func Run(params ...string) {
if len(params) > 0 && params[0] != "" {
strs := strings.Split(params[0], ":")
if len(strs) > 0 && strs[0] != "" {
HttpAddr = strs[0]
}
if len(strs) > 1 && strs[1] != "" {
HttpPort, _ = strconv.Atoi(strs[1])
}
}
initBeforeHttpRun()
if EnableAdmin {
go beeAdminApp.Run()
}
BeeApp.Run()
}
func initBeforeHttpRun() {
// if AppConfigPath not In the conf/app.conf reParse config
if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") {
err := ParseConfig()
if err != nil && AppConfigPath != filepath.Join(workPath, "conf", "app.conf") {
// configuration is critical to app, panic here if parse failed
panic(err)
}
}
// do hooks function
for _, hk := range hooks {
err := hk()
if err != nil {
panic(err)
}
}
if SessionOn {
GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime, SessionSavePath)
var err error
sessionConfig := AppConfig.String("sessionConfig")
if sessionConfig == "" {
sessionConfig = `{"cookieName":"` + SessionName + `",` +
`"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
`"providerConfig":"` + SessionSavePath + `",` +
`"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
`"sessionIDHashFunc":"` + SessionHashFunc + `",` +
`"sessionIDHashKey":"` + SessionHashKey + `",` +
`"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
`"domain":"` + SessionDomain + `",` +
`"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
}
GlobalSessions, err = session.NewManager(SessionProvider,
sessionConfig)
if err != nil {
panic(err)
}
go GlobalSessions.GC()
}
err := BuildTemplate(ViewsPath)
if err != nil {
if RunMode == "dev" {
Warn(err)
}
}
runtime.GOMAXPROCS(runtime.NumCPU())
registerErrorHander()
BeeApp.Run()
middleware.VERSION = VERSION
middleware.AppName = AppName
middleware.RegisterErrorHandler()
if EnableDocs {
Get("/docs", serverDocs)
Get("/docs/*", serverDocs)
}
//init mime
AddAPPStartHook(initMime)
}
// this function is for test package init
func TestBeegoInit(apppath string) {
AppPath = apppath
RunMode = "test"
AppConfigPath = filepath.Join(AppPath, "conf", "app.conf")
err := ParseConfig()
if err != nil && !os.IsNotExist(err) {
// for init if doesn't have app.conf will not panic
Info(err)
}
os.Chdir(AppPath)
initBeforeHttpRun()
}
func init() {
hooks = make([]hookfunc, 0)
}

128
cache.go
View File

@ -1,128 +0,0 @@
package beego
import (
"errors"
"fmt"
"strconv"
"sync"
"time"
)
var (
DefaultEvery int = 60 // 1 minute
)
type BeeItem struct {
val interface{}
Lastaccess time.Time
expired int
}
func (itm *BeeItem) Access() interface{} {
itm.Lastaccess = time.Now()
return itm.val
}
type BeeCache struct {
lock sync.RWMutex
dur time.Duration
items map[string]*BeeItem
Every int // Run an expiration check Every seconds
}
// NewDefaultCache returns a new FileCache with sane defaults.
func NewBeeCache() *BeeCache {
cache := BeeCache{dur: time.Since(time.Now()),
Every: DefaultEvery}
return &cache
}
func (bc *BeeCache) Get(name string) interface{} {
bc.lock.RLock()
defer bc.lock.RUnlock()
itm, ok := bc.items[name]
if !ok {
return nil
}
return itm.Access()
}
func (bc *BeeCache) Put(name string, value interface{}, expired int) error {
bc.lock.Lock()
defer bc.lock.Unlock()
t := BeeItem{val: value, Lastaccess: time.Now(), expired: expired}
if _, ok := bc.items[name]; ok {
return errors.New("the key is exist")
} else {
bc.items[name] = &t
}
return nil
}
func (bc *BeeCache) Delete(name string) (ok bool, err error) {
bc.lock.Lock()
defer bc.lock.Unlock()
if _, ok = bc.items[name]; !ok {
return
}
delete(bc.items, name)
_, valid := bc.items[name]
if valid {
ok = false
}
return
}
func (bc *BeeCache) IsExist(name string) bool {
bc.lock.RLock()
defer bc.lock.RUnlock()
_, ok := bc.items[name]
return ok
}
// Start activates the file cache; it will
func (bc *BeeCache) Start() error {
dur, err := time.ParseDuration(fmt.Sprintf("%ds", bc.Every))
if err != nil {
return err
}
bc.dur = dur
bc.items = make(map[string]*BeeItem, 0)
go bc.vaccuum()
return nil
}
func (bc *BeeCache) vaccuum() {
if bc.Every < 1 {
return
}
for {
<-time.After(time.Duration(bc.dur))
if bc.items == nil {
return
}
for name, _ := range bc.items {
bc.item_expired(name)
}
}
}
// item_expired returns true if an item is expired.
func (bc *BeeCache) item_expired(name string) bool {
bc.lock.Lock()
defer bc.lock.Unlock()
itm, ok := bc.items[name]
if !ok {
return true
}
dur := time.Now().Sub(itm.Lastaccess)
sec, err := strconv.Atoi(fmt.Sprintf("%0.0f", dur.Seconds()))
if err != nil {
delete(bc.items, name)
return true
} else if sec >= itm.expired {
delete(bc.items, name)
return true
}
return false
}

51
cache/README.md vendored
View File

@ -1,52 +1,59 @@
## cache
cache is a golang cache manager. It can use cache for many adapters. The repo is inspired by `database/sql` .
cache is a Go cache manager. It can use many cache adapters. The repo is inspired by `database/sql` .
##How to install
## How to install?
go get github.com/astaxie/beego/cache
##how many adapter support
Now this cache support memory/redis/memcache
## how to use it
first you must import it
## What adapters are supported?
As of now this cache support memory, Memcache and Redis.
## How to use it?
First you must import it
import (
"github.com/astaxie/beego/cache"
)
then init an Cache(memory adapter)
Then init a Cache (example with memory adapter)
bm, err := NewCache("memory", `{"interval":60}`)
bm, err := cache.NewCache("memory", `{"interval":60}`)
use it like this:
Use it like this:
bm.Put("astaxie", 1, 10)
bm.Get("astaxie")
bm.IsExist("astaxie")
bm.Delete("astaxie")
## memory adapter
memory adapter config like this:
## Memory adapter
Configure memory adapter like this:
{"interval":60}
interval means the gc time. The cache will every interval time to check wheather have item expired.
interval means the gc time. The cache will check at each time interval, whether item has expired.
## memcache adapter
memory adapter use the vitess's [memcache](code.google.com/p/vitess/go/memcache) client.
the config like this:
## Memcache adapter
Memcache adapter use the vitess's [Memcache](http://code.google.com/p/vitess/go/memcache) client.
Configure like this:
{"conn":"127.0.0.1:11211"}
## redis adapter
redis adapter use the [redigo](github.com/garyburd/redigo/redis) client.
## Redis adapter
the config like this:
Redis adapter use the [redigo](http://github.com/garyburd/redigo/redis) client.
{"conn":":6039"}
Configure like this:
{"conn":":6039"}

70
cache/cache.go vendored
View File

@ -1,15 +1,65 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Usage:
//
// import(
// "github.com/astaxie/beego/cache"
// )
//
// bm, err := cache.NewCache("memory", `{"interval":60}`)
//
// Use it like this:
//
// bm.Put("astaxie", 1, 10)
// bm.Get("astaxie")
// bm.IsExist("astaxie")
// bm.Delete("astaxie")
//
// more docs http://beego.me/docs/module/cache.md
package cache
import (
"fmt"
)
// Cache interface contains all behaviors for cache adapter.
// usage:
// cache.Register("file",cache.NewFileCache()) // this operation is run in init method of file.go.
// c,err := cache.NewCache("file","{....}")
// c.Put("key",value,3600)
// v := c.Get("key")
//
// c.Incr("counter") // now is 1
// c.Incr("counter") // now is 2
// count := c.Get("counter").(int)
type Cache interface {
// get cached value by key.
Get(key string) interface{}
Put(key string, val interface{}, timeout int) error
// set cached value with key and expire time.
Put(key string, val interface{}, timeout int64) error
// delete cached value by key.
Delete(key string) error
// increase cached int value by key, as a counter.
Incr(key string) error
// decrease cached int value by key, as a counter.
Decr(key string) error
// check if cached value exists or not.
IsExist(key string) bool
// clear all cache.
ClearAll() error
// start gc routine based on config string settings.
StartAndGC(config string) error
}
@ -22,18 +72,24 @@ func Register(name string, adapter Cache) {
if adapter == nil {
panic("cache: Register adapter is nil")
}
if _, dup := adapters[name]; dup {
if _, ok := adapters[name]; ok {
panic("cache: Register called twice for adapter " + name)
}
adapters[name] = adapter
}
//config is json {"interval":360}
func NewCache(adapterName, config string) (Cache, error) {
// Create a new cache driver by adapter name and config string.
// config need to be correct JSON as string: {"interval":360}.
// it will start gc automatically.
func NewCache(adapterName, config string) (adapter Cache, e error) {
adapter, ok := adapters[adapterName]
if !ok {
return nil, fmt.Errorf("cache: unknown adaptername %q (forgotten import?)", adapterName)
e = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
return
}
adapter.StartAndGC(config)
return adapter, nil
err := adapter.StartAndGC(config)
if err != nil {
adapter = nil
}
return
}

83
cache/cache_test.go vendored
View File

@ -1,3 +1,17 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache
import (
@ -5,8 +19,8 @@ import (
"time"
)
func Test_cache(t *testing.T) {
bm, err := NewCache("memory", `{"interval":60}`)
func TestCache(t *testing.T) {
bm, err := NewCache("memory", `{"interval":20}`)
if err != nil {
t.Error("init err")
}
@ -21,7 +35,7 @@ func Test_cache(t *testing.T) {
t.Error("get err")
}
time.Sleep(70 * time.Second)
time.Sleep(30 * time.Second)
if bm.IsExist("astaxie") {
t.Error("check err")
@ -31,8 +45,71 @@ func Test_cache(t *testing.T) {
t.Error("set Error", err)
}
if err = bm.Incr("astaxie"); err != nil {
t.Error("Incr Error", err)
}
if v := bm.Get("astaxie"); v.(int) != 2 {
t.Error("get err")
}
if err = bm.Decr("astaxie"); err != nil {
t.Error("Decr Error", err)
}
if v := bm.Get("astaxie"); v.(int) != 1 {
t.Error("get err")
}
bm.Delete("astaxie")
if bm.IsExist("astaxie") {
t.Error("delete err")
}
}
func TestFileCache(t *testing.T) {
bm, err := NewCache("file", `{"CachePath":"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)
if err != nil {
t.Error("init err")
}
if err = bm.Put("astaxie", 1, 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie") {
t.Error("check err")
}
if v := bm.Get("astaxie"); v.(int) != 1 {
t.Error("get err")
}
if err = bm.Incr("astaxie"); err != nil {
t.Error("Incr Error", err)
}
if v := bm.Get("astaxie"); v.(int) != 2 {
t.Error("get err")
}
if err = bm.Decr("astaxie"); err != nil {
t.Error("Decr Error", err)
}
if v := bm.Get("astaxie"); v.(int) != 1 {
t.Error("get err")
}
bm.Delete("astaxie")
if bm.IsExist("astaxie") {
t.Error("delete err")
}
//test string
if err = bm.Put("astaxie", "author", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie") {
t.Error("check err")
}
if v := bm.Get("astaxie"); v.(string) != "author" {
t.Error("get err")
}
}

112
cache/conv.go vendored Normal file
View File

@ -0,0 +1,112 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache
import (
"fmt"
"strconv"
)
// convert interface to string.
func GetString(v interface{}) string {
switch result := v.(type) {
case string:
return result
case []byte:
return string(result)
default:
if v != nil {
return fmt.Sprintf("%v", result)
}
}
return ""
}
// convert interface to int.
func GetInt(v interface{}) int {
switch result := v.(type) {
case int:
return result
case int32:
return int(result)
case int64:
return int(result)
default:
if d := GetString(v); d != "" {
value, _ := strconv.Atoi(d)
return value
}
}
return 0
}
// convert interface to int64.
func GetInt64(v interface{}) int64 {
switch result := v.(type) {
case int:
return int64(result)
case int32:
return int64(result)
case int64:
return result
default:
if d := GetString(v); d != "" {
value, _ := strconv.ParseInt(d, 10, 64)
return value
}
}
return 0
}
// convert interface to float64.
func GetFloat64(v interface{}) float64 {
switch result := v.(type) {
case float64:
return result
default:
if d := GetString(v); d != "" {
value, _ := strconv.ParseFloat(d, 64)
return value
}
}
return 0
}
// convert interface to bool.
func GetBool(v interface{}) bool {
switch result := v.(type) {
case bool:
return result
default:
if d := GetString(v); d != "" {
value, _ := strconv.ParseBool(d)
return value
}
}
return false
}
// convert interface to byte slice.
func getByteArray(v interface{}) []byte {
switch result := v.(type) {
case []byte:
return result
case string:
return []byte(result)
default:
return nil
}
}

158
cache/conv_test.go vendored Normal file
View File

@ -0,0 +1,158 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache
import (
"testing"
)
func TestGetString(t *testing.T) {
var t1 = "test1"
if "test1" != GetString(t1) {
t.Error("get string from string error")
}
var t2 = []byte("test2")
if "test2" != GetString(t2) {
t.Error("get string from byte array error")
}
var t3 int = 1
if "1" != GetString(t3) {
t.Error("get string from int error")
}
var t4 int64 = 1
if "1" != GetString(t4) {
t.Error("get string from int64 error")
}
var t5 float64 = 1.1
if "1.1" != GetString(t5) {
t.Error("get string from float64 error")
}
if "" != GetString(nil) {
t.Error("get string from nil error")
}
}
func TestGetInt(t *testing.T) {
var t1 int = 1
if 1 != GetInt(t1) {
t.Error("get int from int error")
}
var t2 int32 = 32
if 32 != GetInt(t2) {
t.Error("get int from int32 error")
}
var t3 int64 = 64
if 64 != GetInt(t3) {
t.Error("get int from int64 error")
}
var t4 = "128"
if 128 != GetInt(t4) {
t.Error("get int from num string error")
}
if 0 != GetInt(nil) {
t.Error("get int from nil error")
}
}
func TestGetInt64(t *testing.T) {
var i int64 = 1
var t1 int = 1
if i != GetInt64(t1) {
t.Error("get int64 from int error")
}
var t2 int32 = 1
if i != GetInt64(t2) {
t.Error("get int64 from int32 error")
}
var t3 int64 = 1
if i != GetInt64(t3) {
t.Error("get int64 from int64 error")
}
var t4 = "1"
if i != GetInt64(t4) {
t.Error("get int64 from num string error")
}
if 0 != GetInt64(nil) {
t.Error("get int64 from nil")
}
}
func TestGetFloat64(t *testing.T) {
var f float64 = 1.11
var t1 float32 = 1.11
if f != GetFloat64(t1) {
t.Error("get float64 from float32 error")
}
var t2 float64 = 1.11
if f != GetFloat64(t2) {
t.Error("get float64 from float64 error")
}
var t3 = "1.11"
if f != GetFloat64(t3) {
t.Error("get float64 from string error")
}
var f2 float64 = 1
var t4 int = 1
if f2 != GetFloat64(t4) {
t.Error("get float64 from int error")
}
if 0 != GetFloat64(nil) {
t.Error("get float64 from nil error")
}
}
func TestGetBool(t *testing.T) {
var t1 = true
if true != GetBool(t1) {
t.Error("get bool from bool error")
}
var t2 = "true"
if true != GetBool(t2) {
t.Error("get bool from string error")
}
if false != GetBool(nil) {
t.Error("get bool from nil error")
}
}
func TestGetByteArray(t *testing.T) {
var b = []byte("test")
var t1 = []byte("test")
if !byteArrayEquals(b, getByteArray(t1)) {
t.Error("get byte array from byte array error")
}
var t2 = "test"
if !byteArrayEquals(b, getByteArray(t2)) {
t.Error("get byte array from string error")
}
if nil != getByteArray(nil) {
t.Error("get byte array from nil error")
}
}
func byteArrayEquals(a []byte, b []byte) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}

265
cache/file.go vendored Normal file
View File

@ -0,0 +1,265 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache
import (
"bytes"
"crypto/md5"
"encoding/gob"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"reflect"
"strconv"
"time"
)
func init() {
Register("file", NewFileCache())
}
// FileCacheItem is basic unit of file cache adapter.
// it contains data and expire time.
type FileCacheItem struct {
Data interface{}
Lastaccess int64
Expired int64
}
var (
FileCachePath string = "cache" // cache directory
FileCacheFileSuffix string = ".bin" // cache file suffix
FileCacheDirectoryLevel int = 2 // cache file deep level if auto generated cache files.
FileCacheEmbedExpiry int64 = 0 // cache expire time, default is no expire forever.
)
// FileCache is cache adapter for file storage.
type FileCache struct {
CachePath string
FileSuffix string
DirectoryLevel int
EmbedExpiry int
}
// Create new file cache with no config.
// the level and expiry need set in method StartAndGC as config string.
func NewFileCache() *FileCache {
// return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix}
return &FileCache{}
}
// Start and begin gc for file cache.
// the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}
func (fc *FileCache) StartAndGC(config string) error {
var cfg map[string]string
json.Unmarshal([]byte(config), &cfg)
if _, ok := cfg["CachePath"]; !ok {
cfg["CachePath"] = FileCachePath
}
if _, ok := cfg["FileSuffix"]; !ok {
cfg["FileSuffix"] = FileCacheFileSuffix
}
if _, ok := cfg["DirectoryLevel"]; !ok {
cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel)
}
if _, ok := cfg["EmbedExpiry"]; !ok {
cfg["EmbedExpiry"] = strconv.FormatInt(FileCacheEmbedExpiry, 10)
}
fc.CachePath = cfg["CachePath"]
fc.FileSuffix = cfg["FileSuffix"]
fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"])
fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"])
fc.Init()
return nil
}
// Init will make new dir for file cache if not exist.
func (fc *FileCache) Init() {
app := filepath.Dir(os.Args[0])
fc.CachePath = filepath.Join(app, fc.CachePath)
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
}
}
// get cached file name. it's md5 encoded.
func (fc *FileCache) getCacheFileName(key string) string {
m := md5.New()
io.WriteString(m, key)
keyMd5 := hex.EncodeToString(m.Sum(nil))
cachePath := fc.CachePath
switch fc.DirectoryLevel {
case 2:
cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4])
case 1:
cachePath = filepath.Join(cachePath, keyMd5[0:2])
}
if ok, _ := exists(cachePath); !ok { // todo : error handle
_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle
}
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix))
}
// Get value from file cache.
// if non-exist or expired, return empty string.
func (fc *FileCache) Get(key string) interface{} {
fileData, err := File_get_contents(fc.getCacheFileName(key))
if err != nil {
return ""
}
var to FileCacheItem
Gob_decode(fileData, &to)
if to.Expired < time.Now().Unix() {
return ""
}
return to.Data
}
// Put value into file cache.
// timeout means how long to keep this file, unit of ms.
// if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever.
func (fc *FileCache) Put(key string, val interface{}, timeout int64) error {
gob.Register(val)
item := FileCacheItem{Data: val}
if timeout == FileCacheEmbedExpiry {
item.Expired = time.Now().Unix() + (86400 * 365 * 10) // ten years
} else {
item.Expired = time.Now().Unix() + timeout
}
item.Lastaccess = time.Now().Unix()
data, err := Gob_encode(item)
if err != nil {
return err
}
return File_put_contents(fc.getCacheFileName(key), data)
}
// Delete file cache value.
func (fc *FileCache) Delete(key string) error {
filename := fc.getCacheFileName(key)
if ok, _ := exists(filename); ok {
return os.Remove(filename)
}
return nil
}
// Increase cached int value.
// fc value is saving forever unless Delete.
func (fc *FileCache) Incr(key string) error {
data := fc.Get(key)
var incr int
if reflect.TypeOf(data).Name() != "int" {
incr = 0
} else {
incr = data.(int) + 1
}
fc.Put(key, incr, FileCacheEmbedExpiry)
return nil
}
// Decrease cached int value.
func (fc *FileCache) Decr(key string) error {
data := fc.Get(key)
var decr int
if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 {
decr = 0
} else {
decr = data.(int) - 1
}
fc.Put(key, decr, FileCacheEmbedExpiry)
return nil
}
// Check value is exist.
func (fc *FileCache) IsExist(key string) bool {
ret, _ := exists(fc.getCacheFileName(key))
return ret
}
// Clean cached files.
// not implemented.
func (fc *FileCache) ClearAll() error {
return nil
}
// check file exist.
func exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
// Get bytes to file.
// if non-exist, create this file.
func File_get_contents(filename string) (data []byte, e error) {
f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
if e != nil {
return
}
defer f.Close()
stat, e := f.Stat()
if e != nil {
return
}
data = make([]byte, stat.Size())
result, e := f.Read(data)
if e != nil || int64(result) != stat.Size() {
return nil, e
}
return
}
// Put bytes to file.
// if non-exist, create this file.
func File_put_contents(filename string, content []byte) error {
fp, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer fp.Close()
_, err = fp.Write(content)
return err
}
// Gob encodes file cache item.
func Gob_encode(data interface{}) ([]byte, error) {
buf := bytes.NewBuffer(nil)
enc := gob.NewEncoder(buf)
err := enc.Encode(data)
if err != nil {
return nil, err
}
return buf.Bytes(), err
}
// Gob decodes file cache item.
func Gob_decode(data []byte, to *FileCacheItem) error {
buf := bytes.NewBuffer(data)
dec := gob.NewDecoder(buf)
return dec.Decode(&to)
}

102
cache/memcache.go vendored
View File

@ -1,102 +0,0 @@
package cache
import (
"code.google.com/p/vitess/go/memcache"
"encoding/json"
"errors"
)
type MemcacheCache struct {
c *memcache.Connection
conninfo string
}
func NewMemCache() *MemcacheCache {
return &MemcacheCache{}
}
func (rc *MemcacheCache) Get(key string) interface{} {
if rc.c == nil {
rc.c = rc.connectInit()
}
v, _, err := rc.c.Get(key)
if err != nil {
return nil
}
var contain interface{}
contain = v
return contain
}
func (rc *MemcacheCache) Put(key string, val interface{}, timeout int) error {
if rc.c == nil {
rc.c = rc.connectInit()
}
v, ok := val.(string)
if !ok {
return errors.New("val must string")
}
stored, err := rc.c.Set(key, 0, uint64(timeout), []byte(v))
if err == nil && stored == false {
return errors.New("stored fail")
}
return err
}
func (rc *MemcacheCache) Delete(key string) error {
if rc.c == nil {
rc.c = rc.connectInit()
}
_, err := rc.c.Delete(key)
return err
}
func (rc *MemcacheCache) IsExist(key string) bool {
if rc.c == nil {
rc.c = rc.connectInit()
}
v, _, err := rc.c.Get(key)
if err != nil {
return false
}
if len(v) == 0 {
return false
} else {
return true
}
return true
}
func (rc *MemcacheCache) ClearAll() error {
if rc.c == nil {
rc.c = rc.connectInit()
}
err := rc.c.FlushAll()
return err
}
func (rc *MemcacheCache) StartAndGC(config string) error {
var cf map[string]string
json.Unmarshal([]byte(config), &cf)
if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key")
}
rc.conninfo = cf["conn"]
rc.c = rc.connectInit()
if rc.c == nil {
return errors.New("dial tcp conn error")
}
return nil
}
func (rc *MemcacheCache) connectInit() *memcache.Connection {
c, err := memcache.Connect(rc.conninfo)
if err != nil {
return nil
}
return c
}
func init() {
Register("memcache", NewMemCache())
}

163
cache/memcache/memcache.go vendored Normal file
View File

@ -0,0 +1,163 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// package memcahe for cache provider
//
// depend on github.com/bradfitz/gomemcache/memcache
//
// go install github.com/bradfitz/gomemcache/memcache
//
// Usage:
// import(
// _ "github.com/astaxie/beego/cache/memcache"
// "github.com/astaxie/beego/cache"
// )
//
// bm, err := cache.NewCache("memcache", `{"conn":"127.0.0.1:11211"}`)
//
// more docs http://beego.me/docs/module/cache.md
package memcache
import (
"encoding/json"
"errors"
"strings"
"github.com/bradfitz/gomemcache/memcache"
"github.com/astaxie/beego/cache"
)
// Memcache adapter.
type MemcacheCache struct {
conn *memcache.Client
conninfo []string
}
// create new memcache adapter.
func NewMemCache() *MemcacheCache {
return &MemcacheCache{}
}
// get value from memcache.
func (rc *MemcacheCache) Get(key string) interface{} {
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
return err
}
}
if item, err := rc.conn.Get(key); err == nil {
return string(item.Value)
}
return nil
}
// put value to memcache. only support string.
func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error {
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
return err
}
}
v, ok := val.(string)
if !ok {
return errors.New("val must string")
}
item := memcache.Item{Key: key, Value: []byte(v), Expiration: int32(timeout)}
return rc.conn.Set(&item)
}
// delete value in memcache.
func (rc *MemcacheCache) Delete(key string) error {
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
return err
}
}
return rc.conn.Delete(key)
}
// increase counter.
func (rc *MemcacheCache) Incr(key string) error {
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
return err
}
}
_, err := rc.conn.Increment(key, 1)
return err
}
// decrease counter.
func (rc *MemcacheCache) Decr(key string) error {
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
return err
}
}
_, err := rc.conn.Decrement(key, 1)
return err
}
// check value exists in memcache.
func (rc *MemcacheCache) IsExist(key string) bool {
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
return false
}
}
_, err := rc.conn.Get(key)
if err != nil {
return false
}
return true
}
// clear all cached in memcache.
func (rc *MemcacheCache) ClearAll() error {
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
return err
}
}
return rc.conn.FlushAll()
}
// start memcache adapter.
// config string is like {"conn":"connection info"}.
// if connecting error, return.
func (rc *MemcacheCache) StartAndGC(config string) error {
var cf map[string]string
json.Unmarshal([]byte(config), &cf)
if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key")
}
rc.conninfo = strings.Split(cf["conn"], ";")
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
return err
}
}
return nil
}
// connect to memcache and keep the connection.
func (rc *MemcacheCache) connectInit() error {
rc.conn = memcache.New(rc.conninfo...)
return nil
}
func init() {
cache.Register("memcache", NewMemCache())
}

153
cache/memory.go vendored
View File

@ -1,64 +1,83 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"sync"
"time"
)
var (
// clock time of recycling the expired cache items in memory.
DefaultEvery int = 60 // 1 minute
)
// Memory cache item.
type MemoryItem struct {
val interface{}
Lastaccess time.Time
expired int
}
func (itm *MemoryItem) Access() interface{} {
itm.Lastaccess = time.Now()
return itm.val
expired int64
}
// Memory cache adapter.
// it contains a RW locker for safe map storage.
type MemoryCache struct {
lock sync.RWMutex
dur time.Duration
items map[string]*MemoryItem
Every int // Run an expiration check Every seconds
Every int // run an expiration check Every clock time
}
// NewDefaultCache returns a new FileCache with sane defaults.
// NewMemoryCache returns a new MemoryCache.
func NewMemoryCache() *MemoryCache {
cache := MemoryCache{items: make(map[string]*MemoryItem)}
return &cache
}
// Get cache from memory.
// if non-existed or expired, return nil.
func (bc *MemoryCache) Get(name string) interface{} {
bc.lock.RLock()
defer bc.lock.RUnlock()
itm, ok := bc.items[name]
if !ok {
return nil
}
return itm.Access()
}
func (bc *MemoryCache) Put(name string, value interface{}, expired int) error {
bc.lock.Lock()
defer bc.lock.Unlock()
t := MemoryItem{val: value, Lastaccess: time.Now(), expired: expired}
if _, ok := bc.items[name]; ok {
return errors.New("the key is exist")
} else {
bc.items[name] = &t
if itm, ok := bc.items[name]; ok {
if (time.Now().Unix() - itm.Lastaccess.Unix()) > itm.expired {
go bc.Delete(name)
return nil
}
return itm.val
}
return nil
}
// Put cache to memory.
// if expired is 0, it will be cleaned by next gc operation ( default gc clock is 1 minute).
func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error {
bc.lock.Lock()
defer bc.lock.Unlock()
bc.items[name] = &MemoryItem{
val: value,
Lastaccess: time.Now(),
expired: expired,
}
return nil
}
/// Delete cache in memory.
func (bc *MemoryCache) Delete(name string) error {
bc.lock.Lock()
defer bc.lock.Unlock()
@ -66,13 +85,80 @@ func (bc *MemoryCache) Delete(name string) error {
return errors.New("key not exist")
}
delete(bc.items, name)
_, valid := bc.items[name]
if valid {
if _, ok := bc.items[name]; ok {
return errors.New("delete key error")
}
return nil
}
// Increase cache counter in memory.
// it supports int,int64,int32,uint,uint64,uint32.
func (bc *MemoryCache) Incr(key string) error {
bc.lock.RLock()
defer bc.lock.RUnlock()
itm, ok := bc.items[key]
if !ok {
return errors.New("key not exist")
}
switch itm.val.(type) {
case int:
itm.val = itm.val.(int) + 1
case int64:
itm.val = itm.val.(int64) + 1
case int32:
itm.val = itm.val.(int32) + 1
case uint:
itm.val = itm.val.(uint) + 1
case uint32:
itm.val = itm.val.(uint32) + 1
case uint64:
itm.val = itm.val.(uint64) + 1
default:
return errors.New("item val is not int int64 int32")
}
return nil
}
// Decrease counter in memory.
func (bc *MemoryCache) Decr(key string) error {
bc.lock.RLock()
defer bc.lock.RUnlock()
itm, ok := bc.items[key]
if !ok {
return errors.New("key not exist")
}
switch itm.val.(type) {
case int:
itm.val = itm.val.(int) - 1
case int64:
itm.val = itm.val.(int64) - 1
case int32:
itm.val = itm.val.(int32) - 1
case uint:
if itm.val.(uint) > 0 {
itm.val = itm.val.(uint) - 1
} else {
return errors.New("item val is less than 0")
}
case uint32:
if itm.val.(uint32) > 0 {
itm.val = itm.val.(uint32) - 1
} else {
return errors.New("item val is less than 0")
}
case uint64:
if itm.val.(uint64) > 0 {
itm.val = itm.val.(uint64) - 1
} else {
return errors.New("item val is less than 0")
}
default:
return errors.New("item val is not int int64 int32")
}
return nil
}
// check cache exist in memory.
func (bc *MemoryCache) IsExist(name string) bool {
bc.lock.RLock()
defer bc.lock.RUnlock()
@ -80,6 +166,7 @@ func (bc *MemoryCache) IsExist(name string) bool {
return ok
}
// delete all cache in memory.
func (bc *MemoryCache) ClearAll() error {
bc.lock.Lock()
defer bc.lock.Unlock()
@ -87,11 +174,11 @@ func (bc *MemoryCache) ClearAll() error {
return nil
}
// Start activates the file cache; it will
// start memory cache. it will check expiration in every clock time.
func (bc *MemoryCache) StartAndGC(config string) error {
var cf map[string]int
json.Unmarshal([]byte(config), &cf)
if _, ok := cf["every"]; !ok {
if _, ok := cf["interval"]; !ok {
cf = make(map[string]int)
cf["interval"] = DefaultEvery
}
@ -105,12 +192,13 @@ func (bc *MemoryCache) StartAndGC(config string) error {
return nil
}
// check expiration.
func (bc *MemoryCache) vaccuum() {
if bc.Every < 1 {
return
}
for {
<-time.After(time.Duration(bc.dur) * time.Second)
<-time.After(bc.dur)
if bc.items == nil {
return
}
@ -128,12 +216,7 @@ func (bc *MemoryCache) item_expired(name string) bool {
if !ok {
return true
}
dur := time.Now().Sub(itm.Lastaccess)
sec, err := strconv.Atoi(fmt.Sprintf("%0.0f", dur.Seconds()))
if err != nil {
delete(bc.items, name)
return true
} else if sec >= itm.expired {
if time.Now().Unix()-itm.Lastaccess.Unix() >= itm.expired {
delete(bc.items, name)
return true
}

97
cache/redis.go vendored
View File

@ -1,97 +0,0 @@
package cache
import (
"encoding/json"
"errors"
"github.com/garyburd/redigo/redis"
)
var (
DefaultKey string = "beecacheRedis"
)
type RedisCache struct {
c redis.Conn
conninfo string
key string
}
func NewRedisCache() *RedisCache {
return &RedisCache{key: DefaultKey}
}
func (rc *RedisCache) Get(key string) interface{} {
if rc.c == nil {
rc.c = rc.connectInit()
}
v, err := rc.c.Do("HGET", rc.key, key)
if err != nil {
return nil
}
return v
}
func (rc *RedisCache) Put(key string, val interface{}, timeout int) error {
if rc.c == nil {
rc.c = rc.connectInit()
}
_, err := rc.c.Do("HSET", rc.key, key, val)
return err
}
func (rc *RedisCache) Delete(key string) error {
if rc.c == nil {
rc.c = rc.connectInit()
}
_, err := rc.c.Do("HDEL", rc.key, key)
return err
}
func (rc *RedisCache) IsExist(key string) bool {
if rc.c == nil {
rc.c = rc.connectInit()
}
v, err := redis.Bool(rc.c.Do("HEXISTS", rc.key, key))
if err != nil {
return false
}
return v
}
func (rc *RedisCache) ClearAll() error {
if rc.c == nil {
rc.c = rc.connectInit()
}
_, err := rc.c.Do("DEL", rc.key)
return err
}
func (rc *RedisCache) StartAndGC(config string) error {
var cf map[string]string
json.Unmarshal([]byte(config), &cf)
if _, ok := cf["key"]; !ok {
cf["key"] = DefaultKey
}
if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key")
}
rc.key = cf["key"]
rc.conninfo = cf["conn"]
rc.c = rc.connectInit()
if rc.c == nil {
return errors.New("dial tcp conn error")
}
return nil
}
func (rc *RedisCache) connectInit() redis.Conn {
c, err := redis.Dial("tcp", rc.conninfo)
if err != nil {
return nil
}
return c
}
func init() {
Register("redis", NewRedisCache())
}

182
cache/redis/redis.go vendored Normal file
View File

@ -0,0 +1,182 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// package redis for cache provider
//
// depend on github.com/garyburd/redigo/redis
//
// go install github.com/garyburd/redigo/redis
//
// Usage:
// import(
// _ "github.com/astaxie/beego/cache/redis"
// "github.com/astaxie/beego/cache"
// )
//
// bm, err := cache.NewCache("redis", `{"conn":"127.0.0.1:11211"}`)
//
// more docs http://beego.me/docs/module/cache.md
package redis
import (
"encoding/json"
"errors"
"time"
"github.com/garyburd/redigo/redis"
"github.com/astaxie/beego/cache"
)
var (
// the collection name of redis for cache adapter.
DefaultKey string = "beecacheRedis"
)
// Redis cache adapter.
type RedisCache struct {
p *redis.Pool // redis connection pool
conninfo string
key string
}
// create new redis cache with default collection name.
func NewRedisCache() *RedisCache {
return &RedisCache{key: DefaultKey}
}
// actually do the redis cmds
func (rc *RedisCache) do(commandName string, args ...interface{}) (reply interface{}, err error) {
c := rc.p.Get()
defer c.Close()
return c.Do(commandName, args...)
}
// Get cache from redis.
func (rc *RedisCache) Get(key string) interface{} {
if v, err := rc.do("GET", key); err == nil {
return v
}
return nil
}
// put cache to redis.
func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error {
var err error
if _, err = rc.do("SET", key, val); err != nil {
return err
}
if _, err = rc.do("HSET", rc.key, key, true); err != nil {
return err
}
_, err = rc.do("EXPIRE", key, timeout)
return err
}
// delete cache in redis.
func (rc *RedisCache) Delete(key string) error {
var err error
if _, err = rc.do("DEL", key); err != nil {
return err
}
_, err = rc.do("HDEL", rc.key, key)
return err
}
// check cache's existence in redis.
func (rc *RedisCache) IsExist(key string) bool {
v, err := redis.Bool(rc.do("EXISTS", key))
if err != nil {
return false
}
if v == false {
if _, err = rc.do("HDEL", rc.key, key); err != nil {
return false
}
}
return v
}
// increase counter in redis.
func (rc *RedisCache) Incr(key string) error {
_, err := redis.Bool(rc.do("INCRBY", key, 1))
return err
}
// decrease counter in redis.
func (rc *RedisCache) Decr(key string) error {
_, err := redis.Bool(rc.do("INCRBY", key, -1))
return err
}
// clean all cache in redis. delete this redis collection.
func (rc *RedisCache) ClearAll() error {
cachedKeys, err := redis.Strings(rc.do("HKEYS", rc.key))
if err != nil {
return err
}
for _, str := range cachedKeys {
if _, err = rc.do("DEL", str); err != nil {
return err
}
}
_, err = rc.do("DEL", rc.key)
return err
}
// start redis cache adapter.
// config is like {"key":"collection key","conn":"connection info"}
// the cache item in redis are stored forever,
// so no gc operation.
func (rc *RedisCache) StartAndGC(config string) error {
var cf map[string]string
json.Unmarshal([]byte(config), &cf)
if _, ok := cf["key"]; !ok {
cf["key"] = DefaultKey
}
if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key")
}
rc.key = cf["key"]
rc.conninfo = cf["conn"]
rc.connectInit()
c := rc.p.Get()
defer c.Close()
return c.Err()
}
// connect to redis.
func (rc *RedisCache) connectInit() {
dialFunc := func() (c redis.Conn, err error) {
c, err = redis.Dial("tcp", rc.conninfo)
return
}
// initialize a new pool
rc.p = &redis.Pool{
MaxIdle: 3,
IdleTimeout: 180 * time.Second,
Dial: dialFunc,
}
}
func init() {
cache.Register("redis", NewRedisCache())
}

85
cache/redis/redis_test.go vendored Normal file
View File

@ -0,0 +1,85 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package redis
import (
"testing"
"time"
"github.com/garyburd/redigo/redis"
"github.com/astaxie/beego/cache"
)
func TestRedisCache(t *testing.T) {
bm, err := cache.NewCache("redis", `{"conn": "127.0.0.1:6379"}`)
if err != nil {
t.Error("init err")
}
if err = bm.Put("astaxie", 1, 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie") {
t.Error("check err")
}
time.Sleep(10 * time.Second)
if bm.IsExist("astaxie") {
t.Error("check err")
}
if err = bm.Put("astaxie", 1, 10); err != nil {
t.Error("set Error", err)
}
if v, _ := redis.Int(bm.Get("astaxie"), err); v != 1 {
t.Error("get err")
}
if err = bm.Incr("astaxie"); err != nil {
t.Error("Incr Error", err)
}
if v, _ := redis.Int(bm.Get("astaxie"), err); v != 2 {
t.Error("get err")
}
if err = bm.Decr("astaxie"); err != nil {
t.Error("Decr Error", err)
}
if v, _ := redis.Int(bm.Get("astaxie"), err); v != 1 {
t.Error("get err")
}
bm.Delete("astaxie")
if bm.IsExist("astaxie") {
t.Error("delete err")
}
//test string
if err = bm.Put("astaxie", "author", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie") {
t.Error("check err")
}
if v, _ := redis.String(bm.Get("astaxie"), err); v != "author" {
t.Error("get err")
}
// test clear all
if err = bm.ClearAll(); err != nil {
t.Error("clear all err")
}
}

612
config.go
View File

@ -1,179 +1,433 @@
package beego
import (
"bufio"
"bytes"
"errors"
"io"
"os"
"strconv"
"strings"
"sync"
"unicode"
)
var (
bComment = []byte{'#'}
bEmpty = []byte{}
bEqual = []byte{'='}
bDQuote = []byte{'"'}
)
// A Config represents the configuration.
type Config struct {
filename string
comment map[int][]string // id: []{comment, key...}; id 1 is for main comment.
data map[string]string // key: value
offset map[string]int64 // key: offset; for editing.
sync.RWMutex
}
// ParseFile creates a new Config and parses the file configuration from the
// named file.
func LoadConfig(name string) (*Config, error) {
file, err := os.Open(name)
if err != nil {
return nil, err
}
cfg := &Config{
file.Name(),
make(map[int][]string),
make(map[string]string),
make(map[string]int64),
sync.RWMutex{},
}
cfg.Lock()
defer cfg.Unlock()
defer file.Close()
var comment bytes.Buffer
buf := bufio.NewReader(file)
for nComment, off := 0, int64(1); ; {
line, _, err := buf.ReadLine()
if err == io.EOF {
break
}
if bytes.Equal(line, bEmpty) {
continue
}
off += int64(len(line))
if bytes.HasPrefix(line, bComment) {
line = bytes.TrimLeft(line, "#")
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
comment.Write(line)
comment.WriteByte('\n')
continue
}
if comment.Len() != 0 {
cfg.comment[nComment] = []string{comment.String()}
comment.Reset()
nComment++
}
val := bytes.SplitN(line, bEqual, 2)
if bytes.HasPrefix([]byte(strings.TrimSpace(string(val[1]))), bDQuote) {
val[1] = bytes.Trim([]byte(strings.TrimSpace(string(val[1]))), `"`)
}
key := strings.TrimSpace(string(val[0]))
cfg.comment[nComment-1] = append(cfg.comment[nComment-1], key)
cfg.data[key] = strings.TrimSpace(string(val[1]))
cfg.offset[key] = off
}
return cfg, nil
}
// Bool returns the boolean value for a given key.
func (c *Config) Bool(key string) (bool, error) {
return strconv.ParseBool(c.data[key])
}
// Int returns the integer value for a given key.
func (c *Config) Int(key string) (int, error) {
return strconv.Atoi(c.data[key])
}
func (c *Config) Int64(key string) (int64, error) {
return strconv.ParseInt(c.data[key], 10, 64)
}
// Float returns the float value for a given key.
func (c *Config) Float(key string) (float64, error) {
return strconv.ParseFloat(c.data[key], 64)
}
// String returns the string value for a given key.
func (c *Config) String(key string) string {
return c.data[key]
}
// WriteValue writes a new value for key.
func (c *Config) SetValue(key, value string) error {
c.Lock()
defer c.Unlock()
if _, found := c.data[key]; !found {
return errors.New("key not found: " + key)
}
c.data[key] = value
return nil
}
func ParseConfig() (err error) {
AppConfig, err = LoadConfig(AppConfigPath)
if err != nil {
return err
} else {
HttpAddr = AppConfig.String("httpaddr")
if v, err := AppConfig.Int("httpport"); err == nil {
HttpPort = v
}
if v, err := AppConfig.Int64("maxmemory"); err == nil {
MaxMemory = v
}
AppName = AppConfig.String("appname")
if runmode := AppConfig.String("runmode"); runmode != "" {
RunMode = runmode
}
if ar, err := AppConfig.Bool("autorender"); err == nil {
AutoRender = ar
}
if ar, err := AppConfig.Bool("autorecover"); err == nil {
RecoverPanic = ar
}
if ar, err := AppConfig.Bool("pprofon"); err == nil {
PprofOn = ar
}
if views := AppConfig.String("viewspath"); views != "" {
ViewsPath = views
}
if ar, err := AppConfig.Bool("sessionon"); err == nil {
SessionOn = ar
}
if ar := AppConfig.String("sessionprovider"); ar != "" {
SessionProvider = ar
}
if ar := AppConfig.String("sessionname"); ar != "" {
SessionName = ar
}
if ar := AppConfig.String("sessionsavepath"); ar != "" {
SessionSavePath = ar
}
if ar, err := AppConfig.Int("sessiongcmaxlifetime"); err == nil && ar != 0 {
int64val, _ := strconv.ParseInt(strconv.Itoa(ar), 10, 64)
SessionGCMaxLifetime = int64val
}
if ar, err := AppConfig.Bool("usefcgi"); err == nil {
UseFcgi = ar
}
if ar, err := AppConfig.Bool("enablegzip"); err == nil {
EnableGzip = ar
}
}
return nil
}
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"errors"
"fmt"
"html/template"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/astaxie/beego/config"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/session"
"github.com/astaxie/beego/utils"
)
var (
BeeApp *App // beego application
AppName string
AppPath string
workPath string
AppConfigPath string
StaticDir map[string]string
TemplateCache map[string]*template.Template // template caching map
StaticExtensionsToGzip []string // files with should be compressed with gzip (.js,.css,etc)
EnableHttpListen bool
HttpAddr string
HttpPort int
EnableHttpTLS bool
HttpsPort int
HttpCertFile string
HttpKeyFile string
RecoverPanic bool // flag of auto recover panic
AutoRender bool // flag of render template automatically
ViewsPath string
RunMode string // run mode, "dev" or "prod"
AppConfig config.ConfigContainer
GlobalSessions *session.Manager // global session mananger
SessionOn bool // flag of starting session auto. default is false.
SessionProvider string // default session provider, memory, mysql , redis ,etc.
SessionName string // the cookie name when saving session id into cookie.
SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session.
SessionSavePath string // if use mysql/redis/file provider, define save path to connection info.
SessionHashFunc string // session hash generation func.
SessionHashKey string // session hash salt string.
SessionCookieLifeTime int // the life time of session id in cookie.
SessionAutoSetCookie bool // auto setcookie
SessionDomain string // the cookie domain default is empty
UseFcgi bool
MaxMemory int64
EnableGzip bool // flag of enable gzip
DirectoryIndex bool // flag of display directory index. default is false.
HttpServerTimeOut int64
ErrorsShow bool // flag of show errors in page. if true, show error and trace info in page rendered with error template.
XSRFKEY string // xsrf hash salt string.
EnableXSRF bool // flag of enable xsrf.
XSRFExpire int // the expiry of xsrf value.
CopyRequestBody bool // flag of copy raw request body in context.
TemplateLeft string
TemplateRight string
BeegoServerName string // beego server name exported in response header.
EnableAdmin bool // flag of enable admin module to log every request info.
AdminHttpAddr string // http server configurations for admin module.
AdminHttpPort int
FlashName string // name of the flash variable found in response header and cookie
FlashSeperator string // used to seperate flash key:value
AppConfigProvider string // config provider
EnableDocs bool // enable generate docs & server docs API Swagger
)
func init() {
// create beego application
BeeApp = NewApp()
workPath, _ = os.Getwd()
workPath, _ = filepath.Abs(workPath)
// initialize default configurations
AppPath, _ = filepath.Abs(filepath.Dir(os.Args[0]))
AppConfigPath = filepath.Join(AppPath, "conf", "app.conf")
if workPath != AppPath {
if utils.FileExists(AppConfigPath) {
os.Chdir(AppPath)
} else {
AppConfigPath = filepath.Join(workPath, "conf", "app.conf")
}
}
AppConfigProvider = "ini"
StaticDir = make(map[string]string)
StaticDir["/static"] = "static"
StaticExtensionsToGzip = []string{".css", ".js"}
TemplateCache = make(map[string]*template.Template)
// set this to 0.0.0.0 to make this app available to externally
EnableHttpListen = true //default enable http Listen
HttpAddr = ""
HttpPort = 8080
HttpsPort = 10443
AppName = "beego"
RunMode = "dev" //default runmod
AutoRender = true
RecoverPanic = true
ViewsPath = "views"
SessionOn = false
SessionProvider = "memory"
SessionName = "beegosessionID"
SessionGCMaxLifetime = 3600
SessionSavePath = ""
SessionHashFunc = "sha1"
SessionHashKey = "beegoserversessionkey"
SessionCookieLifeTime = 0 //set cookie default is the brower life
SessionAutoSetCookie = true
UseFcgi = false
MaxMemory = 1 << 26 //64MB
EnableGzip = false
HttpServerTimeOut = 0
ErrorsShow = true
XSRFKEY = "beegoxsrf"
XSRFExpire = 0
TemplateLeft = "{{"
TemplateRight = "}}"
BeegoServerName = "beegoServer:" + VERSION
EnableAdmin = false
AdminHttpAddr = "127.0.0.1"
AdminHttpPort = 8088
FlashName = "BEEGO_FLASH"
FlashSeperator = "BEEGOFLASH"
runtime.GOMAXPROCS(runtime.NumCPU())
// init BeeLogger
BeeLogger = logs.NewLogger(10000)
err := BeeLogger.SetLogger("console", "")
if err != nil {
fmt.Println("init console log error:", err)
}
err = ParseConfig()
if err != nil && !os.IsNotExist(err) {
// for init if doesn't have app.conf will not panic
Info(err)
}
}
// ParseConfig parsed default config file.
// now only support ini, next will support json.
func ParseConfig() (err error) {
AppConfig, err = config.NewConfig(AppConfigProvider, AppConfigPath)
if err != nil {
AppConfig = config.NewFakeConfig()
return err
} else {
if v, err := GetConfig("string", "HttpAddr"); err == nil {
HttpAddr = v.(string)
}
if v, err := GetConfig("int", "HttpPort"); err == nil {
HttpPort = v.(int)
}
if v, err := GetConfig("bool", "EnableHttpListen"); err == nil {
EnableHttpListen = v.(bool)
}
if maxmemory, err := GetConfig("int64", "MaxMemory"); err == nil {
MaxMemory = maxmemory.(int64)
}
if appname, _ := GetConfig("string", "AppName"); appname != "" {
AppName = appname.(string)
}
if runmode, _ := GetConfig("string", "RunMode"); runmode != "" {
RunMode = runmode.(string)
}
if autorender, err := GetConfig("bool", "AutoRender"); err == nil {
AutoRender = autorender.(bool)
}
if autorecover, err := GetConfig("bool", "RecoverPanic"); err == nil {
RecoverPanic = autorecover.(bool)
}
if views, _ := GetConfig("string", "ViewsPath"); views != "" {
ViewsPath = views.(string)
}
if sessionon, err := GetConfig("bool", "SessionOn"); err == nil {
SessionOn = sessionon.(bool)
}
if sessProvider, _ := GetConfig("string", "SessionProvider"); sessProvider != "" {
SessionProvider = sessProvider.(string)
}
if sessName, _ := GetConfig("string", "SessionName"); sessName != "" {
SessionName = sessName.(string)
}
if sesssavepath, _ := GetConfig("string", "SessionSavePath"); sesssavepath != "" {
SessionSavePath = sesssavepath.(string)
}
if sesshashfunc, _ := GetConfig("string", "SessionHashFunc"); sesshashfunc != "" {
SessionHashFunc = sesshashfunc.(string)
}
if sesshashkey, _ := GetConfig("string", "SessionHashKey"); sesshashkey != "" {
SessionHashKey = sesshashkey.(string)
}
if sessMaxLifeTime, err := GetConfig("int64", "SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
SessionGCMaxLifetime = sessMaxLifeTime.(int64)
}
if sesscookielifetime, err := GetConfig("int", "SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
SessionCookieLifeTime = sesscookielifetime.(int)
}
if usefcgi, err := GetConfig("bool", "UseFcgi"); err == nil {
UseFcgi = usefcgi.(bool)
}
if enablegzip, err := GetConfig("bool", "EnableGzip"); err == nil {
EnableGzip = enablegzip.(bool)
}
if directoryindex, err := GetConfig("bool", "DirectoryIndex"); err == nil {
DirectoryIndex = directoryindex.(bool)
}
if timeout, err := GetConfig("int64", "HttpServerTimeOut"); err == nil {
HttpServerTimeOut = timeout.(int64)
}
if errorsshow, err := GetConfig("bool", "ErrorsShow"); err == nil {
ErrorsShow = errorsshow.(bool)
}
if copyrequestbody, err := GetConfig("bool", "CopyRequestBody"); err == nil {
CopyRequestBody = copyrequestbody.(bool)
}
if xsrfkey, _ := GetConfig("string", "XSRFKEY"); xsrfkey != "" {
XSRFKEY = xsrfkey.(string)
}
if enablexsrf, err := GetConfig("bool", "EnableXSRF"); err == nil {
EnableXSRF = enablexsrf.(bool)
}
if expire, err := GetConfig("int", "XSRFExpire"); err == nil {
XSRFExpire = expire.(int)
}
if tplleft, _ := GetConfig("string", "TemplateLeft"); tplleft != "" {
TemplateLeft = tplleft.(string)
}
if tplright, _ := GetConfig("string", "TemplateRight"); tplright != "" {
TemplateRight = tplright.(string)
}
if httptls, err := GetConfig("bool", "EnableHttpTLS"); err == nil {
EnableHttpTLS = httptls.(bool)
}
if httpsport, err := GetConfig("int", "HttpsPort"); err == nil {
HttpsPort = httpsport.(int)
}
if certfile, _ := GetConfig("string", "HttpCertFile"); certfile != "" {
HttpCertFile = certfile.(string)
}
if keyfile, _ := GetConfig("string", "HttpKeyFile"); keyfile != "" {
HttpKeyFile = keyfile.(string)
}
if serverName, _ := GetConfig("string", "BeegoServerName"); serverName != "" {
BeegoServerName = serverName.(string)
}
if flashname, _ := GetConfig("string", "FlashName"); flashname != "" {
FlashName = flashname.(string)
}
if flashseperator, _ := GetConfig("string", "FlashSeperator"); flashseperator != "" {
FlashSeperator = flashseperator.(string)
}
if sd, _ := GetConfig("string", "StaticDir"); sd != "" {
for k := range StaticDir {
delete(StaticDir, k)
}
sds := strings.Fields(sd.(string))
for _, v := range sds {
if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1]
} else {
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0]
}
}
}
if sgz, _ := GetConfig("string", "StaticExtensionsToGzip"); sgz != "" {
extensions := strings.Split(sgz.(string), ",")
if len(extensions) > 0 {
StaticExtensionsToGzip = []string{}
for _, ext := range extensions {
if len(ext) == 0 {
continue
}
extWithDot := ext
if extWithDot[:1] != "." {
extWithDot = "." + extWithDot
}
StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot)
}
}
}
if enableadmin, err := GetConfig("bool", "EnableAdmin"); err == nil {
EnableAdmin = enableadmin.(bool)
}
if adminhttpaddr, _ := GetConfig("string", "AdminHttpAddr"); adminhttpaddr != "" {
AdminHttpAddr = adminhttpaddr.(string)
}
if adminhttpport, err := GetConfig("int", "AdminHttpPort"); err == nil {
AdminHttpPort = adminhttpport.(int)
}
if enabledocs, err := GetConfig("bool", "EnableDocs"); err == nil {
EnableDocs = enabledocs.(bool)
}
}
return nil
}
// Getconfig throw the Runmode
// [dev]
// name = astaixe
// IsEnable = false
// [prod]
// name = slene
// IsEnable = true
//
// usage:
// GetConfig("string", "name")
// GetConfig("bool", "IsEnable")
func GetConfig(typ, key string) (interface{}, error) {
switch typ {
case "string":
v := AppConfig.String(RunMode + "::" + key)
if v == "" {
v = AppConfig.String(key)
}
return v, nil
case "strings":
v := AppConfig.Strings(RunMode + "::" + key)
if len(v) == 0 {
v = AppConfig.Strings(key)
}
return v, nil
case "int":
v, err := AppConfig.Int(RunMode + "::" + key)
if err != nil || v == 0 {
return AppConfig.Int(key)
}
return v, nil
case "bool":
v, err := AppConfig.Bool(RunMode + "::" + key)
if err != nil {
return AppConfig.Bool(key)
}
return v, nil
case "int64":
v, err := AppConfig.Int64(RunMode + "::" + key)
if err != nil || v == 0 {
return AppConfig.Int64(key)
}
return v, nil
case "float":
v, err := AppConfig.Float(RunMode + "::" + key)
if err != nil || v == 0 {
return AppConfig.Float(key)
}
return v, nil
}
return "", errors.New("not support type")
}

107
config/config.go Normal file
View File

@ -0,0 +1,107 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Usage:
// import(
// "github.com/astaxie/beego/config"
// )
//
// cnf, err := config.NewConfig("ini", "config.conf")
//
// cnf APIS:
//
// cnf.Set(key, val string) error
// cnf.String(key string) string
// cnf.Strings(key string) []string
// cnf.Int(key string) (int, error)
// cnf.Int64(key string) (int64, error)
// cnf.Bool(key string) (bool, error)
// cnf.Float(key string) (float64, error)
// cnf.DefaultString(key string, defaultval string) string
// cnf.DefaultStrings(key string, defaultval []string) []string
// cnf.DefaultInt(key string, defaultval int) int
// cnf.DefaultInt64(key string, defaultval int64) int64
// cnf.DefaultBool(key string, defaultval bool) bool
// cnf.DefaultFloat(key string, defaultval float64) float64
// cnf.DIY(key string) (interface{}, error)
// cnf.GetSection(section string) (map[string]string, error)
// cnf.SaveConfigFile(filename string) error
//
// more docs http://beego.me/docs/module/config.md
package config
import (
"fmt"
)
// ConfigContainer defines how to get and set value from configuration raw data.
type ConfigContainer interface {
Set(key, val string) error // support section::key type in given key when using ini type.
String(key string) string // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
Strings(key string) []string //get string slice
Int(key string) (int, error)
Int64(key string) (int64, error)
Bool(key string) (bool, error)
Float(key string) (float64, error)
DefaultString(key string, defaultval string) string // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
DefaultStrings(key string, defaultval []string) []string //get string slice
DefaultInt(key string, defaultval int) int
DefaultInt64(key string, defaultval int64) int64
DefaultBool(key string, defaultval bool) bool
DefaultFloat(key string, defaultval float64) float64
DIY(key string) (interface{}, error)
GetSection(section string) (map[string]string, error)
SaveConfigFile(filename string) error
}
// Config is the adapter interface for parsing config file to get raw data to ConfigContainer.
type Config interface {
Parse(key string) (ConfigContainer, error)
ParseData(data []byte) (ConfigContainer, error)
}
var adapters = make(map[string]Config)
// Register makes a config adapter available by the adapter name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, adapter Config) {
if adapter == nil {
panic("config: Register adapter is nil")
}
if _, ok := adapters[name]; ok {
panic("config: Register called twice for adapter " + name)
}
adapters[name] = adapter
}
// adapterName is ini/json/xml/yaml.
// filename is the config file path.
func NewConfig(adapterName, fileaname string) (ConfigContainer, error) {
adapter, ok := adapters[adapterName]
if !ok {
return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName)
}
return adapter.Parse(fileaname)
}
// adapterName is ini/json/xml/yaml.
// data is the config data.
func NewConfigData(adapterName string, data []byte) (ConfigContainer, error) {
adapter, ok := adapters[adapterName]
if !ok {
return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName)
}
return adapter.ParseData(data)
}

129
config/fake.go Normal file
View File

@ -0,0 +1,129 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"errors"
"strconv"
"strings"
)
type fakeConfigContainer struct {
data map[string]string
}
func (c *fakeConfigContainer) getData(key string) string {
return c.data[strings.ToLower(key)]
}
func (c *fakeConfigContainer) Set(key, val string) error {
c.data[strings.ToLower(key)] = val
return nil
}
func (c *fakeConfigContainer) String(key string) string {
return c.getData(key)
}
func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string {
if v := c.getData(key); v == "" {
return defaultval
} else {
return v
}
}
func (c *fakeConfigContainer) Strings(key string) []string {
return strings.Split(c.getData(key), ";")
}
func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []string {
if v := c.Strings(key); len(v) == 0 {
return defaultval
} else {
return v
}
}
func (c *fakeConfigContainer) Int(key string) (int, error) {
return strconv.Atoi(c.getData(key))
}
func (c *fakeConfigContainer) DefaultInt(key string, defaultval int) int {
if v, err := c.Int(key); err != nil {
return defaultval
} else {
return v
}
}
func (c *fakeConfigContainer) Int64(key string) (int64, error) {
return strconv.ParseInt(c.getData(key), 10, 64)
}
func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
if v, err := c.Int64(key); err != nil {
return defaultval
} else {
return v
}
}
func (c *fakeConfigContainer) Bool(key string) (bool, error) {
return strconv.ParseBool(c.getData(key))
}
func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool {
if v, err := c.Bool(key); err != nil {
return defaultval
} else {
return v
}
}
func (c *fakeConfigContainer) Float(key string) (float64, error) {
return strconv.ParseFloat(c.getData(key), 64)
}
func (c *fakeConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
if v, err := c.Float(key); err != nil {
return defaultval
} else {
return v
}
}
func (c *fakeConfigContainer) DIY(key string) (interface{}, error) {
if v, ok := c.data[strings.ToLower(key)]; ok {
return v, nil
}
return nil, errors.New("key not find")
}
func (c *fakeConfigContainer) GetSection(section string) (map[string]string, error) {
return nil, errors.New("not implement in the fakeConfigContainer")
}
func (c *fakeConfigContainer) SaveConfigFile(filename string) error {
return errors.New("not implement in the fakeConfigContainer")
}
var _ ConfigContainer = new(fakeConfigContainer)
func NewFakeConfig() ConfigContainer {
return &fakeConfigContainer{
data: make(map[string]string),
}
}

367
config/ini.go Normal file
View File

@ -0,0 +1,367 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"sync"
"time"
"unicode"
)
var (
DEFAULT_SECTION = "default" // default section means if some ini items not in a section, make them in default section,
bNumComment = []byte{'#'} // number signal
bSemComment = []byte{';'} // semicolon signal
bEmpty = []byte{}
bEqual = []byte{'='} // equal signal
bDQuote = []byte{'"'} // quote signal
sectionStart = []byte{'['} // section start signal
sectionEnd = []byte{']'} // section end signal
lineBreak = "\n"
)
// IniConfig implements Config to parse ini file.
type IniConfig struct {
}
// ParseFile creates a new Config and parses the file configuration from the named file.
func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
file, err := os.Open(name)
if err != nil {
return nil, err
}
cfg := &IniConfigContainer{
file.Name(),
make(map[string]map[string]string),
make(map[string]string),
make(map[string]string),
sync.RWMutex{},
}
cfg.Lock()
defer cfg.Unlock()
defer file.Close()
var comment bytes.Buffer
buf := bufio.NewReader(file)
section := DEFAULT_SECTION
for {
line, _, err := buf.ReadLine()
if err == io.EOF {
break
}
if bytes.Equal(line, bEmpty) {
continue
}
line = bytes.TrimSpace(line)
var bComment []byte
switch {
case bytes.HasPrefix(line, bNumComment):
bComment = bNumComment
case bytes.HasPrefix(line, bSemComment):
bComment = bSemComment
}
if bComment != nil {
line = bytes.TrimLeft(line, string(bComment))
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
comment.Write(line)
comment.WriteByte('\n')
continue
}
if bytes.HasPrefix(line, sectionStart) && bytes.HasSuffix(line, sectionEnd) {
section = strings.ToLower(string(line[1 : len(line)-1])) // section name case insensitive
if comment.Len() > 0 {
cfg.sectionComment[section] = comment.String()
comment.Reset()
}
if _, ok := cfg.data[section]; !ok {
cfg.data[section] = make(map[string]string)
}
continue
}
if _, ok := cfg.data[section]; !ok {
cfg.data[section] = make(map[string]string)
}
keyValue := bytes.SplitN(line, bEqual, 2)
val := bytes.TrimSpace(keyValue[1])
if bytes.HasPrefix(val, bDQuote) {
val = bytes.Trim(val, `"`)
}
key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
key = strings.ToLower(key)
cfg.data[section][key] = string(val)
if comment.Len() > 0 {
cfg.keyComment[section+"."+key] = comment.String()
comment.Reset()
}
}
return cfg, nil
}
func (ini *IniConfig) ParseData(data []byte) (ConfigContainer, error) {
// Save memory data to temporary file
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
return nil, err
}
return ini.Parse(tmpName)
}
// A Config represents the ini configuration.
// When set and get value, support key as section:name type.
type IniConfigContainer struct {
filename string
data map[string]map[string]string // section=> key:val
sectionComment map[string]string // section : comment
keyComment map[string]string // id: []{comment, key...}; id 1 is for main comment.
sync.RWMutex
}
// Bool returns the boolean value for a given key.
func (c *IniConfigContainer) Bool(key string) (bool, error) {
return strconv.ParseBool(c.getdata(key))
}
// DefaultBool returns the boolean value for a given key.
// if err != nil return defaltval
func (c *IniConfigContainer) DefaultBool(key string, defaultval bool) bool {
if v, err := c.Bool(key); err != nil {
return defaultval
} else {
return v
}
}
// Int returns the integer value for a given key.
func (c *IniConfigContainer) Int(key string) (int, error) {
return strconv.Atoi(c.getdata(key))
}
// DefaultInt returns the integer value for a given key.
// if err != nil return defaltval
func (c *IniConfigContainer) DefaultInt(key string, defaultval int) int {
if v, err := c.Int(key); err != nil {
return defaultval
} else {
return v
}
}
// Int64 returns the int64 value for a given key.
func (c *IniConfigContainer) Int64(key string) (int64, error) {
return strconv.ParseInt(c.getdata(key), 10, 64)
}
// DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval
func (c *IniConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
if v, err := c.Int64(key); err != nil {
return defaultval
} else {
return v
}
}
// Float returns the float value for a given key.
func (c *IniConfigContainer) Float(key string) (float64, error) {
return strconv.ParseFloat(c.getdata(key), 64)
}
// DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval
func (c *IniConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
if v, err := c.Float(key); err != nil {
return defaultval
} else {
return v
}
}
// String returns the string value for a given key.
func (c *IniConfigContainer) String(key string) string {
return c.getdata(key)
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
func (c *IniConfigContainer) DefaultString(key string, defaultval string) string {
if v := c.String(key); v == "" {
return defaultval
} else {
return v
}
}
// Strings returns the []string value for a given key.
func (c *IniConfigContainer) Strings(key string) []string {
return strings.Split(c.String(key), ";")
}
// DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval
func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []string {
if v := c.Strings(key); len(v) == 0 {
return defaultval
} else {
return v
}
}
// GetSection returns map for the given section
func (c *IniConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok {
return v, nil
} else {
return nil, errors.New("not exist setction")
}
}
// SaveConfigFile save the config into file
func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
// Write configuration file by filename.
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
buf := bytes.NewBuffer(nil)
for section, dt := range c.data {
// Write section comments.
if v, ok := c.sectionComment[section]; ok {
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
return err
}
}
if section != DEFAULT_SECTION {
// Write section name.
if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil {
return err
}
}
for key, val := range dt {
if key != " " {
// Write key comments.
if v, ok := c.keyComment[key]; ok {
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
return err
}
}
// Write key and value.
if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil {
return err
}
}
}
// Put a line between sections.
if _, err = buf.WriteString(lineBreak); err != nil {
return err
}
}
if _, err = buf.WriteTo(f); err != nil {
return err
}
return nil
}
// WriteValue writes a new value for key.
// if write to one section, the key need be "section::key".
// if the section is not existed, it panics.
func (c *IniConfigContainer) Set(key, value string) error {
c.Lock()
defer c.Unlock()
if len(key) == 0 {
return errors.New("key is empty")
}
var (
section, k string
sectionKey []string = strings.Split(key, "::")
)
if len(sectionKey) >= 2 {
section = sectionKey[0]
k = sectionKey[1]
} else {
section = DEFAULT_SECTION
k = sectionKey[0]
}
if _, ok := c.data[section]; !ok {
c.data[section] = make(map[string]string)
}
c.data[section][k] = value
return nil
}
// DIY returns the raw value by a given key.
func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
if v, ok := c.data[strings.ToLower(key)]; ok {
return v, nil
}
return v, errors.New("key not find")
}
// section.key or key
func (c *IniConfigContainer) getdata(key string) string {
if len(key) == 0 {
return ""
}
c.RLock()
defer c.RUnlock()
var (
section, k string
sectionKey []string = strings.Split(strings.ToLower(key), "::")
)
if len(sectionKey) >= 2 {
section = sectionKey[0]
k = sectionKey[1]
} else {
section = DEFAULT_SECTION
k = sectionKey[0]
}
if v, ok := c.data[section]; ok {
if vv, ok := v[k]; ok {
return vv
}
}
return ""
}
func init() {
Register("ini", &IniConfig{})
}

103
config/ini_test.go Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"os"
"testing"
)
var inicontext = `
;comment one
#comment two
appname = beeapi
httpport = 8080
mysqlport = 3600
PI = 3.1415976
runmode = "dev"
autorender = false
copyrequestbody = true
[demo]
key1="asta"
key2 = "xie"
CaseInsensitive = true
peers = one;two;three
`
func TestIni(t *testing.T) {
f, err := os.Create("testini.conf")
if err != nil {
t.Fatal(err)
}
_, err = f.WriteString(inicontext)
if err != nil {
f.Close()
t.Fatal(err)
}
f.Close()
defer os.Remove("testini.conf")
iniconf, err := NewConfig("ini", "testini.conf")
if err != nil {
t.Fatal(err)
}
if iniconf.String("appname") != "beeapi" {
t.Fatal("appname not equal to beeapi")
}
if port, err := iniconf.Int("httpport"); err != nil || port != 8080 {
t.Error(port)
t.Fatal(err)
}
if port, err := iniconf.Int64("mysqlport"); err != nil || port != 3600 {
t.Error(port)
t.Fatal(err)
}
if pi, err := iniconf.Float("PI"); err != nil || pi != 3.1415976 {
t.Error(pi)
t.Fatal(err)
}
if iniconf.String("runmode") != "dev" {
t.Fatal("runmode not equal to dev")
}
if v, err := iniconf.Bool("autorender"); err != nil || v != false {
t.Error(v)
t.Fatal(err)
}
if v, err := iniconf.Bool("copyrequestbody"); err != nil || v != true {
t.Error(v)
t.Fatal(err)
}
if err = iniconf.Set("name", "astaxie"); err != nil {
t.Fatal(err)
}
if iniconf.String("name") != "astaxie" {
t.Fatal("get name error")
}
if iniconf.String("demo::key1") != "asta" {
t.Fatal("get demo.key1 error")
}
if iniconf.String("demo::key2") != "xie" {
t.Fatal("get demo.key2 error")
}
if v, err := iniconf.Bool("demo::caseinsensitive"); err != nil || v != true {
t.Fatal("get demo.caseinsensitive error")
}
if data := iniconf.Strings("demo::peers"); len(data) != 3 {
t.Fatal("get strings error", data)
} else if data[0] != "one" {
t.Fatal("get first params error not equat to one")
}
}

272
config/json.go Normal file
View File

@ -0,0 +1,272 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"sync"
"time"
)
// JsonConfig is a json config parser and implements Config interface.
type JsonConfig struct {
}
// Parse returns a ConfigContainer with parsed json config map.
func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
content, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
x := &JsonConfigContainer{
data: make(map[string]interface{}),
}
err = json.Unmarshal(content, &x.data)
if err != nil {
var wrappingArray []interface{}
err2 := json.Unmarshal(content, &wrappingArray)
if err2 != nil {
return nil, err
}
x.data["rootArray"] = wrappingArray
}
return x, nil
}
func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) {
// Save memory data to temporary file
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
return nil, err
}
return js.Parse(tmpName)
}
// A Config represents the json configuration.
// Only when get value, support key as section:name type.
type JsonConfigContainer struct {
data map[string]interface{}
sync.RWMutex
}
// Bool returns the boolean value for a given key.
func (c *JsonConfigContainer) Bool(key string) (bool, error) {
val := c.getData(key)
if val != nil {
if v, ok := val.(bool); ok {
return v, nil
}
return false, errors.New("not bool value")
}
return false, errors.New("not exist key:" + key)
}
// DefaultBool return the bool value if has no error
// otherwise return the defaultval
func (c *JsonConfigContainer) DefaultBool(key string, defaultval bool) bool {
if v, err := c.Bool(key); err != nil {
return defaultval
} else {
return v
}
}
// Int returns the integer value for a given key.
func (c *JsonConfigContainer) Int(key string) (int, error) {
val := c.getData(key)
if val != nil {
if v, ok := val.(float64); ok {
return int(v), nil
}
return 0, errors.New("not int value")
}
return 0, errors.New("not exist key:" + key)
}
// DefaultInt returns the integer value for a given key.
// if err != nil return defaltval
func (c *JsonConfigContainer) DefaultInt(key string, defaultval int) int {
if v, err := c.Int(key); err != nil {
return defaultval
} else {
return v
}
}
// Int64 returns the int64 value for a given key.
func (c *JsonConfigContainer) Int64(key string) (int64, error) {
val := c.getData(key)
if val != nil {
if v, ok := val.(float64); ok {
return int64(v), nil
}
return 0, errors.New("not int64 value")
}
return 0, errors.New("not exist key:" + key)
}
// DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval
func (c *JsonConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
if v, err := c.Int64(key); err != nil {
return defaultval
} else {
return v
}
}
// Float returns the float value for a given key.
func (c *JsonConfigContainer) Float(key string) (float64, error) {
val := c.getData(key)
if val != nil {
if v, ok := val.(float64); ok {
return v, nil
}
return 0.0, errors.New("not float64 value")
}
return 0.0, errors.New("not exist key:" + key)
}
// DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval
func (c *JsonConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
if v, err := c.Float(key); err != nil {
return defaultval
} else {
return v
}
}
// String returns the string value for a given key.
func (c *JsonConfigContainer) String(key string) string {
val := c.getData(key)
if val != nil {
if v, ok := val.(string); ok {
return v
}
}
return ""
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
func (c *JsonConfigContainer) DefaultString(key string, defaultval string) string {
if v := c.String(key); v == "" {
return defaultval
} else {
return v
}
}
// Strings returns the []string value for a given key.
func (c *JsonConfigContainer) Strings(key string) []string {
return strings.Split(c.String(key), ";")
}
// DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval
func (c *JsonConfigContainer) DefaultStrings(key string, defaultval []string) []string {
if v := c.Strings(key); len(v) == 0 {
return defaultval
} else {
return v
}
}
// GetSection returns map for the given section
func (c *JsonConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok {
return v.(map[string]string), nil
} else {
return nil, errors.New("not exist setction")
}
}
// SaveConfigFile save the config into file
func (c *JsonConfigContainer) SaveConfigFile(filename string) (err error) {
// Write configuration file by filename.
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
b, err := json.MarshalIndent(c.data, "", " ")
if err != nil {
return err
}
_, err = f.Write(b)
return err
}
// WriteValue writes a new value for key.
func (c *JsonConfigContainer) Set(key, val string) error {
c.Lock()
defer c.Unlock()
c.data[key] = val
return nil
}
// DIY returns the raw value by a given key.
func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) {
val := c.getData(key)
if val != nil {
return val, nil
}
return nil, errors.New("not exist key")
}
// section.key or key
func (c *JsonConfigContainer) getData(key string) interface{} {
c.RLock()
defer c.RUnlock()
if len(key) == 0 {
return nil
}
sectionKey := strings.Split(key, "::")
if len(sectionKey) >= 2 {
curValue, ok := c.data[sectionKey[0]]
if !ok {
return nil
}
for _, key := range sectionKey[1:] {
if v, ok := curValue.(map[string]interface{}); ok {
if curValue, ok = v[key]; !ok {
return nil
}
}
}
return curValue
}
if v, ok := c.data[key]; ok {
return v
}
return nil
}
func init() {
Register("json", &JsonConfig{})
}

182
config/json_test.go Normal file
View File

@ -0,0 +1,182 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"os"
"testing"
)
var jsoncontext = `{
"appname": "beeapi",
"httpport": 8080,
"mysqlport": 3600,
"PI": 3.1415976,
"runmode": "dev",
"autorender": false,
"copyrequestbody": true,
"database": {
"host": "host",
"port": "port",
"database": "database",
"username": "username",
"password": "password",
"conns":{
"maxconnection":12,
"autoconnect":true,
"connectioninfo":"info"
}
}
}`
var jsoncontextwitharray = `[
{
"url": "user",
"serviceAPI": "http://www.test.com/user"
},
{
"url": "employee",
"serviceAPI": "http://www.test.com/employee"
}
]`
func TestJsonStartsWithArray(t *testing.T) {
f, err := os.Create("testjsonWithArray.conf")
if err != nil {
t.Fatal(err)
}
_, err = f.WriteString(jsoncontextwitharray)
if err != nil {
f.Close()
t.Fatal(err)
}
f.Close()
defer os.Remove("testjsonWithArray.conf")
jsonconf, err := NewConfig("json", "testjsonWithArray.conf")
if err != nil {
t.Fatal(err)
}
rootArray, err := jsonconf.DIY("rootArray")
if err != nil {
t.Error("array does not exist as element")
}
rootArrayCasted := rootArray.([]interface{})
if rootArrayCasted == nil {
t.Error("array from root is nil")
} else {
elem := rootArrayCasted[0].(map[string]interface{})
if elem["url"] != "user" || elem["serviceAPI"] != "http://www.test.com/user" {
t.Error("array[0] values are not valid")
}
elem2 := rootArrayCasted[1].(map[string]interface{})
if elem2["url"] != "employee" || elem2["serviceAPI"] != "http://www.test.com/employee" {
t.Error("array[1] values are not valid")
}
}
}
func TestJson(t *testing.T) {
f, err := os.Create("testjson.conf")
if err != nil {
t.Fatal(err)
}
_, err = f.WriteString(jsoncontext)
if err != nil {
f.Close()
t.Fatal(err)
}
f.Close()
defer os.Remove("testjson.conf")
jsonconf, err := NewConfig("json", "testjson.conf")
if err != nil {
t.Fatal(err)
}
if jsonconf.String("appname") != "beeapi" {
t.Fatal("appname not equal to beeapi")
}
if port, err := jsonconf.Int("httpport"); err != nil || port != 8080 {
t.Error(port)
t.Fatal(err)
}
if port, err := jsonconf.Int64("mysqlport"); err != nil || port != 3600 {
t.Error(port)
t.Fatal(err)
}
if pi, err := jsonconf.Float("PI"); err != nil || pi != 3.1415976 {
t.Error(pi)
t.Fatal(err)
}
if jsonconf.String("runmode") != "dev" {
t.Fatal("runmode not equal to dev")
}
if v, err := jsonconf.Bool("autorender"); err != nil || v != false {
t.Error(v)
t.Fatal(err)
}
if v, err := jsonconf.Bool("copyrequestbody"); err != nil || v != true {
t.Error(v)
t.Fatal(err)
}
if err = jsonconf.Set("name", "astaxie"); err != nil {
t.Fatal(err)
}
if jsonconf.String("name") != "astaxie" {
t.Fatal("get name error")
}
if jsonconf.String("database::host") != "host" {
t.Fatal("get database::host error")
}
if jsonconf.String("database::conns::connectioninfo") != "info" {
t.Fatal("get database::conns::connectioninfo error")
}
if maxconnection, err := jsonconf.Int("database::conns::maxconnection"); err != nil || maxconnection != 12 {
t.Fatal("get database::conns::maxconnection error")
}
if db, err := jsonconf.DIY("database"); err != nil {
t.Fatal(err)
} else if m, ok := db.(map[string]interface{}); !ok {
t.Log(db)
t.Fatal("db not map[string]interface{}")
} else {
if m["host"].(string) != "host" {
t.Fatal("get host err")
}
}
if _, err := jsonconf.Int("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting an Int")
}
if _, err := jsonconf.Int64("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting an Int64")
}
if _, err := jsonconf.Float("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting a Float")
}
if _, err := jsonconf.DIY("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting an interface{}")
}
if val := jsonconf.String("unknown"); val != "" {
t.Error("unknown keys should return an empty string when expecting a String")
}
if _, err := jsonconf.Bool("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting a Bool")
}
}

228
config/xml/xml.go Normal file
View File

@ -0,0 +1,228 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// package xml for config provider
//
// depend on github.com/beego/x2j
//
// go install github.com/beego/x2j
//
// Usage:
// import(
// _ "github.com/astaxie/beego/config/xml"
// "github.com/astaxie/beego/config"
// )
//
// cnf, err := config.NewConfig("xml", "config.xml")
//
// more docs http://beego.me/docs/module/config.md
package xml
import (
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"sync"
"time"
"github.com/astaxie/beego/config"
"github.com/beego/x2j"
)
// XmlConfig is a xml config parser and implements Config interface.
// xml configurations should be included in <config></config> tag.
// only support key/value pair as <key>value</key> as each item.
type XMLConfig struct{}
// Parse returns a ConfigContainer with parsed xml config map.
func (xc *XMLConfig) Parse(filename string) (config.ConfigContainer, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
x := &XMLConfigContainer{data: make(map[string]interface{})}
content, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
d, err := x2j.DocToMap(string(content))
if err != nil {
return nil, err
}
x.data = d["config"].(map[string]interface{})
return x, nil
}
func (x *XMLConfig) ParseData(data []byte) (config.ConfigContainer, error) {
// Save memory data to temporary file
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
return nil, err
}
return x.Parse(tmpName)
}
// A Config represents the xml configuration.
type XMLConfigContainer struct {
data map[string]interface{}
sync.Mutex
}
// Bool returns the boolean value for a given key.
func (c *XMLConfigContainer) Bool(key string) (bool, error) {
return strconv.ParseBool(c.data[key].(string))
}
// DefaultBool return the bool value if has no error
// otherwise return the defaultval
func (c *XMLConfigContainer) DefaultBool(key string, defaultval bool) bool {
if v, err := c.Bool(key); err != nil {
return defaultval
} else {
return v
}
}
// Int returns the integer value for a given key.
func (c *XMLConfigContainer) Int(key string) (int, error) {
return strconv.Atoi(c.data[key].(string))
}
// DefaultInt returns the integer value for a given key.
// if err != nil return defaltval
func (c *XMLConfigContainer) DefaultInt(key string, defaultval int) int {
if v, err := c.Int(key); err != nil {
return defaultval
} else {
return v
}
}
// Int64 returns the int64 value for a given key.
func (c *XMLConfigContainer) Int64(key string) (int64, error) {
return strconv.ParseInt(c.data[key].(string), 10, 64)
}
// DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval
func (c *XMLConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
if v, err := c.Int64(key); err != nil {
return defaultval
} else {
return v
}
}
// Float returns the float value for a given key.
func (c *XMLConfigContainer) Float(key string) (float64, error) {
return strconv.ParseFloat(c.data[key].(string), 64)
}
// DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval
func (c *XMLConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
if v, err := c.Float(key); err != nil {
return defaultval
} else {
return v
}
}
// String returns the string value for a given key.
func (c *XMLConfigContainer) String(key string) string {
if v, ok := c.data[key].(string); ok {
return v
}
return ""
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
func (c *XMLConfigContainer) DefaultString(key string, defaultval string) string {
if v := c.String(key); v == "" {
return defaultval
} else {
return v
}
}
// Strings returns the []string value for a given key.
func (c *XMLConfigContainer) Strings(key string) []string {
return strings.Split(c.String(key), ";")
}
// DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval
func (c *XMLConfigContainer) DefaultStrings(key string, defaultval []string) []string {
if v := c.Strings(key); len(v) == 0 {
return defaultval
} else {
return v
}
}
// GetSection returns map for the given section
func (c *XMLConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok {
return v.(map[string]string), nil
} else {
return nil, errors.New("not exist setction")
}
}
// SaveConfigFile save the config into file
func (c *XMLConfigContainer) SaveConfigFile(filename string) (err error) {
// Write configuration file by filename.
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
b, err := xml.MarshalIndent(c.data, " ", " ")
if err != nil {
return err
}
_, err = f.Write(b)
return err
}
// WriteValue writes a new value for key.
func (c *XMLConfigContainer) Set(key, val string) error {
c.Lock()
defer c.Unlock()
c.data[key] = val
return nil
}
// DIY returns the raw value by a given key.
func (c *XMLConfigContainer) DIY(key string) (v interface{}, err error) {
if v, ok := c.data[key]; ok {
return v, nil
}
return nil, errors.New("not exist key")
}
func init() {
config.Register("xml", &XMLConfig{})
}

85
config/xml/xml_test.go Normal file
View File

@ -0,0 +1,85 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package xml
import (
"os"
"testing"
"github.com/astaxie/beego/config"
)
//xml parse should incluce in <config></config> tags
var xmlcontext = `<?xml version="1.0" encoding="UTF-8"?>
<config>
<appname>beeapi</appname>
<httpport>8080</httpport>
<mysqlport>3600</mysqlport>
<PI>3.1415976</PI>
<runmode>dev</runmode>
<autorender>false</autorender>
<copyrequestbody>true</copyrequestbody>
</config>
`
func TestXML(t *testing.T) {
f, err := os.Create("testxml.conf")
if err != nil {
t.Fatal(err)
}
_, err = f.WriteString(xmlcontext)
if err != nil {
f.Close()
t.Fatal(err)
}
f.Close()
defer os.Remove("testxml.conf")
xmlconf, err := config.NewConfig("xml", "testxml.conf")
if err != nil {
t.Fatal(err)
}
if xmlconf.String("appname") != "beeapi" {
t.Fatal("appname not equal to beeapi")
}
if port, err := xmlconf.Int("httpport"); err != nil || port != 8080 {
t.Error(port)
t.Fatal(err)
}
if port, err := xmlconf.Int64("mysqlport"); err != nil || port != 3600 {
t.Error(port)
t.Fatal(err)
}
if pi, err := xmlconf.Float("PI"); err != nil || pi != 3.1415976 {
t.Error(pi)
t.Fatal(err)
}
if xmlconf.String("runmode") != "dev" {
t.Fatal("runmode not equal to dev")
}
if v, err := xmlconf.Bool("autorender"); err != nil || v != false {
t.Error(v)
t.Fatal(err)
}
if v, err := xmlconf.Bool("copyrequestbody"); err != nil || v != true {
t.Error(v)
t.Fatal(err)
}
if err = xmlconf.Set("name", "astaxie"); err != nil {
t.Fatal(err)
}
if xmlconf.String("name") != "astaxie" {
t.Fatal("get name error")
}
}

265
config/yaml/yaml.go Normal file
View File

@ -0,0 +1,265 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// package yaml for config provider
//
// depend on github.com/beego/goyaml2
//
// go install github.com/beego/goyaml2
//
// Usage:
// import(
// _ "github.com/astaxie/beego/config/yaml"
// "github.com/astaxie/beego/config"
// )
//
// cnf, err := config.NewConfig("yaml", "config.yaml")
//
// more docs http://beego.me/docs/module/config.md
package yaml
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"strings"
"sync"
"time"
"github.com/astaxie/beego/config"
"github.com/beego/goyaml2"
)
// YAMLConfig is a yaml config parser and implements Config interface.
type YAMLConfig struct{}
// Parse returns a ConfigContainer with parsed yaml config map.
func (yaml *YAMLConfig) Parse(filename string) (y config.ConfigContainer, err error) {
cnf, err := ReadYmlReader(filename)
if err != nil {
return
}
y = &YAMLConfigContainer{
data: cnf,
}
return
}
func (yaml *YAMLConfig) ParseData(data []byte) (config.ConfigContainer, error) {
// Save memory data to temporary file
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
return nil, err
}
return yaml.Parse(tmpName)
}
// Read yaml file to map.
// if json like, use json package, unless goyaml2 package.
func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
f, err := os.Open(path)
if err != nil {
return
}
defer f.Close()
buf, err := ioutil.ReadAll(f)
if err != nil || len(buf) < 3 {
return
}
if string(buf[0:1]) == "{" {
log.Println("Look like a Json, try json umarshal")
err = json.Unmarshal(buf, &cnf)
if err == nil {
log.Println("It is Json Map")
return
}
}
data, err := goyaml2.Read(bytes.NewBuffer(buf))
if err != nil {
log.Println("Goyaml2 ERR>", string(buf), err)
return
}
if data == nil {
log.Println("Goyaml2 output nil? Pls report bug\n" + string(buf))
return
}
cnf, ok := data.(map[string]interface{})
if !ok {
log.Println("Not a Map? >> ", string(buf), data)
cnf = nil
}
return
}
// A Config represents the yaml configuration.
type YAMLConfigContainer struct {
data map[string]interface{}
sync.Mutex
}
// Bool returns the boolean value for a given key.
func (c *YAMLConfigContainer) Bool(key string) (bool, error) {
if v, ok := c.data[key].(bool); ok {
return v, nil
}
return false, errors.New("not bool value")
}
// DefaultBool return the bool value if has no error
// otherwise return the defaultval
func (c *YAMLConfigContainer) DefaultBool(key string, defaultval bool) bool {
if v, err := c.Bool(key); err != nil {
return defaultval
} else {
return v
}
}
// Int returns the integer value for a given key.
func (c *YAMLConfigContainer) Int(key string) (int, error) {
if v, ok := c.data[key].(int64); ok {
return int(v), nil
}
return 0, errors.New("not int value")
}
// DefaultInt returns the integer value for a given key.
// if err != nil return defaltval
func (c *YAMLConfigContainer) DefaultInt(key string, defaultval int) int {
if v, err := c.Int(key); err != nil {
return defaultval
} else {
return v
}
}
// Int64 returns the int64 value for a given key.
func (c *YAMLConfigContainer) Int64(key string) (int64, error) {
if v, ok := c.data[key].(int64); ok {
return v, nil
}
return 0, errors.New("not bool value")
}
// DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval
func (c *YAMLConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
if v, err := c.Int64(key); err != nil {
return defaultval
} else {
return v
}
}
// Float returns the float value for a given key.
func (c *YAMLConfigContainer) Float(key string) (float64, error) {
if v, ok := c.data[key].(float64); ok {
return v, nil
}
return 0.0, errors.New("not float64 value")
}
// DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval
func (c *YAMLConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
if v, err := c.Float(key); err != nil {
return defaultval
} else {
return v
}
}
// String returns the string value for a given key.
func (c *YAMLConfigContainer) String(key string) string {
if v, ok := c.data[key].(string); ok {
return v
}
return ""
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
func (c *YAMLConfigContainer) DefaultString(key string, defaultval string) string {
if v := c.String(key); v == "" {
return defaultval
} else {
return v
}
}
// Strings returns the []string value for a given key.
func (c *YAMLConfigContainer) Strings(key string) []string {
return strings.Split(c.String(key), ";")
}
// DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval
func (c *YAMLConfigContainer) DefaultStrings(key string, defaultval []string) []string {
if v := c.Strings(key); len(v) == 0 {
return defaultval
} else {
return v
}
}
// GetSection returns map for the given section
func (c *YAMLConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok {
return v.(map[string]string), nil
} else {
return nil, errors.New("not exist setction")
}
}
// SaveConfigFile save the config into file
func (c *YAMLConfigContainer) SaveConfigFile(filename string) (err error) {
// Write configuration file by filename.
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
err = goyaml2.Write(f, c.data)
return err
}
// WriteValue writes a new value for key.
func (c *YAMLConfigContainer) Set(key, val string) error {
c.Lock()
defer c.Unlock()
c.data[key] = val
return nil
}
// DIY returns the raw value by a given key.
func (c *YAMLConfigContainer) DIY(key string) (v interface{}, err error) {
if v, ok := c.data[key]; ok {
return v, nil
}
return nil, errors.New("not exist key")
}
func init() {
config.Register("yaml", &YAMLConfig{})
}

82
config/yaml/yaml_test.go Normal file
View File

@ -0,0 +1,82 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
"os"
"testing"
"github.com/astaxie/beego/config"
)
var yamlcontext = `
"appname": beeapi
"httpport": 8080
"mysqlport": 3600
"PI": 3.1415976
"runmode": dev
"autorender": false
"copyrequestbody": true
`
func TestYaml(t *testing.T) {
f, err := os.Create("testyaml.conf")
if err != nil {
t.Fatal(err)
}
_, err = f.WriteString(yamlcontext)
if err != nil {
f.Close()
t.Fatal(err)
}
f.Close()
defer os.Remove("testyaml.conf")
yamlconf, err := config.NewConfig("yaml", "testyaml.conf")
if err != nil {
t.Fatal(err)
}
if yamlconf.String("appname") != "beeapi" {
t.Fatal("appname not equal to beeapi")
}
if port, err := yamlconf.Int("httpport"); err != nil || port != 8080 {
t.Error(port)
t.Fatal(err)
}
if port, err := yamlconf.Int64("mysqlport"); err != nil || port != 3600 {
t.Error(port)
t.Fatal(err)
}
if pi, err := yamlconf.Float("PI"); err != nil || pi != 3.1415976 {
t.Error(pi)
t.Fatal(err)
}
if yamlconf.String("runmode") != "dev" {
t.Fatal("runmode not equal to dev")
}
if v, err := yamlconf.Bool("autorender"); err != nil || v != false {
t.Error(v)
t.Fatal(err)
}
if v, err := yamlconf.Bool("copyrequestbody"); err != nil || v != true {
t.Error(v)
t.Fatal(err)
}
if err = yamlconf.Set("name", "astaxie"); err != nil {
t.Fatal(err)
}
if yamlconf.String("name") != "astaxie" {
t.Fatal("get name error")
}
}

29
config_test.go Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"testing"
)
func TestDefaults(t *testing.T) {
if FlashName != "BEEGO_FLASH" {
t.Errorf("FlashName was not set to default.")
}
if FlashSeperator != "BEEGOFLASH" {
t.Errorf("FlashName was not set to default.")
}
}

View File

@ -1,71 +0,0 @@
package beego
import (
"fmt"
"mime"
"net/http"
"strings"
"time"
)
type Context struct {
ResponseWriter http.ResponseWriter
Request *http.Request
Params map[string]string
}
func (ctx *Context) WriteString(content string) {
ctx.ResponseWriter.Write([]byte(content))
}
func (ctx *Context) Abort(status int, body string) {
ctx.ResponseWriter.WriteHeader(status)
ctx.ResponseWriter.Write([]byte(body))
}
func (ctx *Context) Redirect(status int, url_ string) {
ctx.ResponseWriter.Header().Set("Location", url_)
ctx.ResponseWriter.WriteHeader(status)
}
func (ctx *Context) NotModified() {
ctx.ResponseWriter.WriteHeader(304)
}
func (ctx *Context) NotFound(message string) {
ctx.ResponseWriter.WriteHeader(404)
ctx.ResponseWriter.Write([]byte(message))
}
//Sets the content type by extension, as defined in the mime package.
//For example, ctx.ContentType("json") sets the content-type to "application/json"
func (ctx *Context) ContentType(ext string) {
if !strings.HasPrefix(ext, ".") {
ext = "." + ext
}
ctype := mime.TypeByExtension(ext)
if ctype != "" {
ctx.ResponseWriter.Header().Set("Content-Type", ctype)
}
}
func (ctx *Context) SetHeader(hdr string, val string, unique bool) {
if unique {
ctx.ResponseWriter.Header().Set(hdr, val)
} else {
ctx.ResponseWriter.Header().Add(hdr, val)
}
}
//Sets a cookie -- duration is the amount of time in seconds. 0 = forever
func (ctx *Context) SetCookie(name string, value string, age int64) {
var utctime time.Time
if age == 0 {
// 2^31 - 1 seconds (roughly 2038)
utctime = time.Unix(2147483647, 0)
} else {
utctime = time.Unix(time.Now().Unix()+age, 0)
}
cookie := fmt.Sprintf("%s=%s; Expires=%s; Path=/", name, value, webTime(utctime))
ctx.SetHeader("Set-Cookie", cookie, true)
}

161
context/context.go Normal file
View File

@ -0,0 +1,161 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Usage:
//
// import "github.com/astaxie/beego/context"
//
// ctx := context.Context{Request:req,ResponseWriter:rw}
//
// more docs http://beego.me/docs/module/context.md
package context
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/utils"
)
// Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter.
// BeegoInput and BeegoOutput provides some api to operate request and response more easily.
type Context struct {
Input *BeegoInput
Output *BeegoOutput
Request *http.Request
ResponseWriter http.ResponseWriter
_xsrf_token string
}
// Redirect does redirection to localurl with http header status code.
// It sends http response header directly.
func (ctx *Context) Redirect(status int, localurl string) {
ctx.Output.Header("Location", localurl)
ctx.ResponseWriter.WriteHeader(status)
}
// Abort stops this request.
// if middleware.ErrorMaps exists, panic body.
// if middleware.HTTPExceptionMaps exists, panic HTTPException struct with status and body string.
func (ctx *Context) Abort(status int, body string) {
ctx.ResponseWriter.WriteHeader(status)
// first panic from ErrorMaps, is is user defined error functions.
if _, ok := middleware.ErrorMaps[body]; ok {
panic(body)
}
// second panic from HTTPExceptionMaps, it is system defined functions.
if e, ok := middleware.HTTPExceptionMaps[status]; ok {
if len(body) >= 1 {
e.Description = body
}
panic(e)
}
// last panic user string
panic(body)
}
// Write string to response body.
// it sends response body.
func (ctx *Context) WriteString(content string) {
ctx.ResponseWriter.Write([]byte(content))
}
// Get cookie from request by a given key.
// It's alias of BeegoInput.Cookie.
func (ctx *Context) GetCookie(key string) string {
return ctx.Input.Cookie(key)
}
// Set cookie for response.
// It's alias of BeegoOutput.Cookie.
func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
ctx.Output.Cookie(name, value, others...)
}
// Get secure cookie from request by a given key.
func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
val := ctx.Input.Cookie(key)
if val == "" {
return "", false
}
parts := strings.SplitN(val, "|", 3)
if len(parts) != 3 {
return "", false
}
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
}
res, _ := base64.URLEncoding.DecodeString(vs)
return string(res), true
}
// Set Secure cookie for response.
func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
vs := base64.URLEncoding.EncodeToString([]byte(value))
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}, "|")
ctx.Output.Cookie(name, cookie, others...)
}
// XsrfToken creates a xsrf token string and returns.
func (ctx *Context) XsrfToken(key string, expire int64) string {
if ctx._xsrf_token == "" {
token, ok := ctx.GetSecureCookie(key, "_xsrf")
if !ok {
token = string(utils.RandomCreateBytes(32))
ctx.SetSecureCookie(key, "_xsrf", token, expire)
}
ctx._xsrf_token = token
}
return ctx._xsrf_token
}
// CheckXsrfCookie checks xsrf token in this request is valid or not.
// the token can provided in request header "X-Xsrftoken" and "X-CsrfToken"
// or in form field value named as "_xsrf".
func (ctx *Context) CheckXsrfCookie() bool {
token := ctx.Input.Query("_xsrf")
if token == "" {
token = ctx.Request.Header.Get("X-Xsrftoken")
}
if token == "" {
token = ctx.Request.Header.Get("X-Csrftoken")
}
if token == "" {
ctx.Abort(403, "'_xsrf' argument missing from POST")
} else if ctx._xsrf_token != token {
ctx.Abort(403, "XSRF cookie does not match POST argument")
}
return true
}

535
context/input.go Normal file
View File

@ -0,0 +1,535 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package context
import (
"bytes"
"errors"
"io/ioutil"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"github.com/astaxie/beego/session"
)
// BeegoInput operates the http request header ,data ,cookie and body.
// it also contains router params and current session.
type BeegoInput struct {
CruSession session.SessionStore
Params map[string]string
Data map[interface{}]interface{} // store some values in this context when calling context in filter or controller.
Request *http.Request
RequestBody []byte
RunController reflect.Type
RunMethod string
}
// NewInput return BeegoInput generated by http.Request.
func NewInput(req *http.Request) *BeegoInput {
return &BeegoInput{
Params: make(map[string]string),
Data: make(map[interface{}]interface{}),
Request: req,
}
}
// Protocol returns request protocol name, such as HTTP/1.1 .
func (input *BeegoInput) Protocol() string {
return input.Request.Proto
}
// Uri returns full request url with query string, fragment.
func (input *BeegoInput) Uri() string {
return input.Request.RequestURI
}
// Url returns request url path (without query string, fragment).
func (input *BeegoInput) Url() string {
return input.Request.URL.Path
}
// Site returns base site url as scheme://domain type.
func (input *BeegoInput) Site() string {
return input.Scheme() + "://" + input.Domain()
}
// Scheme returns request scheme as "http" or "https".
func (input *BeegoInput) Scheme() string {
if input.Request.URL.Scheme != "" {
return input.Request.URL.Scheme
} else if input.Request.TLS == nil {
return "http"
} else {
return "https"
}
}
// Domain returns host name.
// Alias of Host method.
func (input *BeegoInput) Domain() string {
return input.Host()
}
// Host returns host name.
// if no host info in request, return localhost.
func (input *BeegoInput) Host() string {
if input.Request.Host != "" {
hostParts := strings.Split(input.Request.Host, ":")
if len(hostParts) > 0 {
return hostParts[0]
}
return input.Request.Host
}
return "localhost"
}
// Method returns http request method.
func (input *BeegoInput) Method() string {
return input.Request.Method
}
// Is returns boolean of this request is on given method, such as Is("POST").
func (input *BeegoInput) Is(method string) bool {
return input.Method() == method
}
// Is this a GET method request?
func (input *BeegoInput) IsGet() bool {
return input.Is("GET")
}
// Is this a POST method request?
func (input *BeegoInput) IsPost() bool {
return input.Is("POST")
}
// Is this a Head method request?
func (input *BeegoInput) IsHead() bool {
return input.Is("HEAD")
}
// Is this a OPTIONS method request?
func (input *BeegoInput) IsOptions() bool {
return input.Is("OPTIONS")
}
// Is this a PUT method request?
func (input *BeegoInput) IsPut() bool {
return input.Is("PUT")
}
// Is this a DELETE method request?
func (input *BeegoInput) IsDelete() bool {
return input.Is("DELETE")
}
// Is this a PATCH method request?
func (input *BeegoInput) IsPatch() bool {
return input.Is("PATCH")
}
// IsAjax returns boolean of this request is generated by ajax.
func (input *BeegoInput) IsAjax() bool {
return input.Header("X-Requested-With") == "XMLHttpRequest"
}
// IsSecure returns boolean of this request is in https.
func (input *BeegoInput) IsSecure() bool {
return input.Scheme() == "https"
}
// IsSecure returns boolean of this request is in webSocket.
func (input *BeegoInput) IsWebsocket() bool {
return input.Header("Upgrade") == "websocket"
}
// IsSecure returns boolean of whether file uploads in this request or not..
func (input *BeegoInput) IsUpload() bool {
return strings.Contains(input.Header("Content-Type"), "multipart/form-data")
}
// IP returns request client ip.
// if in proxy, return first proxy id.
// if error, return 127.0.0.1.
func (input *BeegoInput) IP() string {
ips := input.Proxy()
if len(ips) > 0 && ips[0] != "" {
rip := strings.Split(ips[0], ":")
return rip[0]
}
ip := strings.Split(input.Request.RemoteAddr, ":")
if len(ip) > 0 {
if ip[0] != "[" {
return ip[0]
}
}
return "127.0.0.1"
}
// Proxy returns proxy client ips slice.
func (input *BeegoInput) Proxy() []string {
if ips := input.Header("X-Forwarded-For"); ips != "" {
return strings.Split(ips, ",")
}
return []string{}
}
// Refer returns http referer header.
func (input *BeegoInput) Refer() string {
return input.Header("Referer")
}
// SubDomains returns sub domain string.
// if aa.bb.domain.com, returns aa.bb .
func (input *BeegoInput) SubDomains() string {
parts := strings.Split(input.Host(), ".")
return strings.Join(parts[len(parts)-2:], ".")
}
// Port returns request client port.
// when error or empty, return 80.
func (input *BeegoInput) Port() int {
parts := strings.Split(input.Request.Host, ":")
if len(parts) == 2 {
port, _ := strconv.Atoi(parts[1])
return port
}
return 80
}
// UserAgent returns request client user agent string.
func (input *BeegoInput) UserAgent() string {
return input.Header("User-Agent")
}
// Param returns router param by a given key.
func (input *BeegoInput) Param(key string) string {
if v, ok := input.Params[key]; ok {
return v
}
return ""
}
// Query returns input data item string by a given string.
func (input *BeegoInput) Query(key string) string {
if val := input.Param(key); val != "" {
return val
}
if input.Request.Form == nil {
input.Request.ParseForm()
}
return input.Request.Form.Get(key)
}
// Header returns request header item string by a given string.
func (input *BeegoInput) Header(key string) string {
return input.Request.Header.Get(key)
}
// Cookie returns request cookie item string by a given key.
// if non-existed, return empty string.
func (input *BeegoInput) Cookie(key string) string {
ck, err := input.Request.Cookie(key)
if err != nil {
return ""
}
return ck.Value
}
// Session returns current session item value by a given key.
func (input *BeegoInput) Session(key interface{}) interface{} {
return input.CruSession.Get(key)
}
// Body returns the raw request body data as bytes.
func (input *BeegoInput) CopyBody() []byte {
requestbody, _ := ioutil.ReadAll(input.Request.Body)
input.Request.Body.Close()
bf := bytes.NewBuffer(requestbody)
input.Request.Body = ioutil.NopCloser(bf)
input.RequestBody = requestbody
return requestbody
}
// GetData returns the stored data in this context.
func (input *BeegoInput) GetData(key interface{}) interface{} {
if v, ok := input.Data[key]; ok {
return v
}
return nil
}
// SetData stores data with given key in this context.
// This data are only available in this context.
func (input *BeegoInput) SetData(key, val interface{}) {
input.Data[key] = val
}
// parseForm or parseMultiForm based on Content-type
func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error {
// Parse the body depending on the content type.
if strings.Contains(input.Header("Content-Type"), "multipart/form-data") {
if err := input.Request.ParseMultipartForm(maxMemory); err != nil {
return errors.New("Error parsing request body:" + err.Error())
}
} else if err := input.Request.ParseForm(); err != nil {
return errors.New("Error parsing request body:" + err.Error())
}
return nil
}
// Bind data from request.Form[key] to dest
// like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie
// var id int beegoInput.Bind(&id, "id") id ==123
// var isok bool beegoInput.Bind(&isok, "isok") id ==true
// var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2
// ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2]
// ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array]
// user struct{Name} beegoInput.Bind(&user, "user") user == {Name:"astaxie"}
func (input *BeegoInput) Bind(dest interface{}, key string) error {
value := reflect.ValueOf(dest)
if value.Kind() != reflect.Ptr {
return errors.New("beego: non-pointer passed to Bind: " + key)
}
value = value.Elem()
if !value.CanSet() {
return errors.New("beego: non-settable variable passed to Bind: " + key)
}
rv := input.bind(key, value.Type())
if !rv.IsValid() {
return errors.New("beego: reflect value is empty")
}
value.Set(rv)
return nil
}
func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value {
rv := reflect.Zero(reflect.TypeOf(0))
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val := input.Query(key)
if len(val) == 0 {
return rv
}
rv = input.bindInt(val, typ)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val := input.Query(key)
if len(val) == 0 {
return rv
}
rv = input.bindUint(val, typ)
case reflect.Float32, reflect.Float64:
val := input.Query(key)
if len(val) == 0 {
return rv
}
rv = input.bindFloat(val, typ)
case reflect.String:
val := input.Query(key)
if len(val) == 0 {
return rv
}
rv = input.bindString(val, typ)
case reflect.Bool:
val := input.Query(key)
if len(val) == 0 {
return rv
}
rv = input.bindBool(val, typ)
case reflect.Slice:
rv = input.bindSlice(&input.Request.Form, key, typ)
case reflect.Struct:
rv = input.bindStruct(&input.Request.Form, key, typ)
case reflect.Ptr:
rv = input.bindPoint(key, typ)
case reflect.Map:
rv = input.bindMap(&input.Request.Form, key, typ)
}
return rv
}
func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value {
rv := reflect.Zero(reflect.TypeOf(0))
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
rv = input.bindInt(val, typ)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
rv = input.bindUint(val, typ)
case reflect.Float32, reflect.Float64:
rv = input.bindFloat(val, typ)
case reflect.String:
rv = input.bindString(val, typ)
case reflect.Bool:
rv = input.bindBool(val, typ)
case reflect.Slice:
rv = input.bindSlice(&url.Values{"": {val}}, "", typ)
case reflect.Struct:
rv = input.bindStruct(&url.Values{"": {val}}, "", typ)
case reflect.Ptr:
rv = input.bindPoint(val, typ)
case reflect.Map:
rv = input.bindMap(&url.Values{"": {val}}, "", typ)
}
return rv
}
func (input *BeegoInput) bindInt(val string, typ reflect.Type) reflect.Value {
intValue, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return reflect.Zero(typ)
}
pValue := reflect.New(typ)
pValue.Elem().SetInt(intValue)
return pValue.Elem()
}
func (input *BeegoInput) bindUint(val string, typ reflect.Type) reflect.Value {
uintValue, err := strconv.ParseUint(val, 10, 64)
if err != nil {
return reflect.Zero(typ)
}
pValue := reflect.New(typ)
pValue.Elem().SetUint(uintValue)
return pValue.Elem()
}
func (input *BeegoInput) bindFloat(val string, typ reflect.Type) reflect.Value {
floatValue, err := strconv.ParseFloat(val, 64)
if err != nil {
return reflect.Zero(typ)
}
pValue := reflect.New(typ)
pValue.Elem().SetFloat(floatValue)
return pValue.Elem()
}
func (input *BeegoInput) bindString(val string, typ reflect.Type) reflect.Value {
return reflect.ValueOf(val)
}
func (input *BeegoInput) bindBool(val string, typ reflect.Type) reflect.Value {
val = strings.TrimSpace(strings.ToLower(val))
switch val {
case "true", "on", "1":
return reflect.ValueOf(true)
}
return reflect.ValueOf(false)
}
type sliceValue struct {
index int // Index extracted from brackets. If -1, no index was provided.
value reflect.Value // the bound value for this slice element.
}
func (input *BeegoInput) bindSlice(params *url.Values, key string, typ reflect.Type) reflect.Value {
maxIndex := -1
numNoIndex := 0
sliceValues := []sliceValue{}
for reqKey, vals := range *params {
if !strings.HasPrefix(reqKey, key+"[") {
continue
}
// Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
index := -1
leftBracket, rightBracket := len(key), strings.Index(reqKey[len(key):], "]")+len(key)
if rightBracket > leftBracket+1 {
index, _ = strconv.Atoi(reqKey[leftBracket+1 : rightBracket])
}
subKeyIndex := rightBracket + 1
// Handle the indexed case.
if index > -1 {
if index > maxIndex {
maxIndex = index
}
sliceValues = append(sliceValues, sliceValue{
index: index,
value: input.bind(reqKey[:subKeyIndex], typ.Elem()),
})
continue
}
// It's an un-indexed element. (e.g. element[])
numNoIndex += len(vals)
for _, val := range vals {
// Unindexed values can only be direct-bound.
sliceValues = append(sliceValues, sliceValue{
index: -1,
value: input.bindValue(val, typ.Elem()),
})
}
}
resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
for _, sv := range sliceValues {
if sv.index != -1 {
resultArray.Index(sv.index).Set(sv.value)
} else {
resultArray = reflect.Append(resultArray, sv.value)
}
}
return resultArray
}
func (input *BeegoInput) bindStruct(params *url.Values, key string, typ reflect.Type) reflect.Value {
result := reflect.New(typ).Elem()
fieldValues := make(map[string]reflect.Value)
for reqKey, val := range *params {
if !strings.HasPrefix(reqKey, key+".") {
continue
}
fieldName := reqKey[len(key)+1:]
if _, ok := fieldValues[fieldName]; !ok {
// Time to bind this field. Get it and make sure we can set it.
fieldValue := result.FieldByName(fieldName)
if !fieldValue.IsValid() {
continue
}
if !fieldValue.CanSet() {
continue
}
boundVal := input.bindValue(val[0], fieldValue.Type())
fieldValue.Set(boundVal)
fieldValues[fieldName] = boundVal
}
}
return result
}
func (input *BeegoInput) bindPoint(key string, typ reflect.Type) reflect.Value {
return input.bind(key, typ.Elem()).Addr()
}
func (input *BeegoInput) bindMap(params *url.Values, key string, typ reflect.Type) reflect.Value {
var (
result = reflect.MakeMap(typ)
keyType = typ.Key()
valueType = typ.Elem()
)
for paramName, values := range *params {
if !strings.HasPrefix(paramName, key+"[") || paramName[len(paramName)-1] != ']' {
continue
}
key := paramName[len(key)+1 : len(paramName)-1]
result.SetMapIndex(input.bindValue(key, keyType), input.bindValue(values[0], valueType))
}
return result
}

72
context/input_test.go Normal file
View File

@ -0,0 +1,72 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package context
import (
"fmt"
"net/http"
"testing"
)
func TestParse(t *testing.T) {
r, _ := http.NewRequest("GET", "/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
beegoInput := NewInput(r)
beegoInput.ParseFormOrMulitForm(1 << 20)
var id int
err := beegoInput.Bind(&id, "id")
if id != 123 || err != nil {
t.Fatal("id should has int value")
}
fmt.Println(id)
var isok bool
err = beegoInput.Bind(&isok, "isok")
if !isok || err != nil {
t.Fatal("isok should be true")
}
fmt.Println(isok)
var float float64
err = beegoInput.Bind(&float, "ft")
if float != 1.2 || err != nil {
t.Fatal("float should be equal to 1.2")
}
fmt.Println(float)
ol := make([]int, 0, 2)
err = beegoInput.Bind(&ol, "ol")
if len(ol) != 2 || err != nil || ol[0] != 1 || ol[1] != 2 {
t.Fatal("ol should has two elements")
}
fmt.Println(ol)
ul := make([]string, 0, 2)
err = beegoInput.Bind(&ul, "ul")
if len(ul) != 2 || err != nil || ul[0] != "str" || ul[1] != "array" {
t.Fatal("ul should has two elements")
}
fmt.Println(ul)
type User struct {
Name string
}
user := User{}
err = beegoInput.Bind(&user, "user")
if err != nil || user.Name != "astaxie" {
t.Fatal("user should has name")
}
fmt.Println(user)
}

360
context/output.go Normal file
View File

@ -0,0 +1,360 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package context
import (
"bytes"
"compress/flate"
"compress/gzip"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"html/template"
"io"
"mime"
"net/http"
"path/filepath"
"strconv"
"strings"
)
// BeegoOutput does work for sending response header.
type BeegoOutput struct {
Context *Context
Status int
EnableGzip bool
}
// NewOutput returns new BeegoOutput.
// it contains nothing now.
func NewOutput() *BeegoOutput {
return &BeegoOutput{}
}
// Header sets response header item string via given key.
func (output *BeegoOutput) Header(key, val string) {
output.Context.ResponseWriter.Header().Set(key, val)
}
// Body sets response body content.
// if EnableGzip, compress content string.
// it sends out response body directly.
func (output *BeegoOutput) Body(content []byte) {
output_writer := output.Context.ResponseWriter.(io.Writer)
if output.EnableGzip == true && output.Context.Input.Header("Accept-Encoding") != "" {
splitted := strings.SplitN(output.Context.Input.Header("Accept-Encoding"), ",", -1)
encodings := make([]string, len(splitted))
for i, val := range splitted {
encodings[i] = strings.TrimSpace(val)
}
for _, val := range encodings {
if val == "gzip" {
output.Header("Content-Encoding", "gzip")
output_writer, _ = gzip.NewWriterLevel(output.Context.ResponseWriter, gzip.BestSpeed)
break
} else if val == "deflate" {
output.Header("Content-Encoding", "deflate")
output_writer, _ = flate.NewWriter(output.Context.ResponseWriter, flate.BestSpeed)
break
}
}
} else {
output.Header("Content-Length", strconv.Itoa(len(content)))
}
// Write status code if it has been set manually
// Set it to 0 afterwards to prevent "multiple response.WriteHeader calls"
if output.Status != 0 {
output.Context.ResponseWriter.WriteHeader(output.Status)
output.Status = 0
}
output_writer.Write(content)
switch output_writer.(type) {
case *gzip.Writer:
output_writer.(*gzip.Writer).Close()
case *flate.Writer:
output_writer.(*flate.Writer).Close()
}
}
// Cookie sets cookie value via given key.
// others are ordered as cookie's max age time, path,domain, secure and httponly.
func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) {
var b bytes.Buffer
fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value))
if len(others) > 0 {
switch v := others[0].(type) {
case int:
if v > 0 {
fmt.Fprintf(&b, "; Max-Age=%d", v)
} else if v < 0 {
fmt.Fprintf(&b, "; Max-Age=0")
}
case int64:
if v > 0 {
fmt.Fprintf(&b, "; Max-Age=%d", v)
} else if v < 0 {
fmt.Fprintf(&b, "; Max-Age=0")
}
case int32:
if v > 0 {
fmt.Fprintf(&b, "; Max-Age=%d", v)
} else if v < 0 {
fmt.Fprintf(&b, "; Max-Age=0")
}
}
}
// the settings below
// Path, Domain, Secure, HttpOnly
// can use nil skip set
// default "/"
if len(others) > 1 {
if v, ok := others[1].(string); ok && len(v) > 0 {
fmt.Fprintf(&b, "; Path=%s", sanitizeValue(v))
}
} else {
fmt.Fprintf(&b, "; Path=%s", "/")
}
// default empty
if len(others) > 2 {
if v, ok := others[2].(string); ok && len(v) > 0 {
fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(v))
}
}
// default empty
if len(others) > 3 {
var secure bool
switch v := others[3].(type) {
case bool:
secure = v
default:
if others[3] != nil {
secure = true
}
}
if secure {
fmt.Fprintf(&b, "; Secure")
}
}
// default false. for session cookie default true
httponly := false
if len(others) > 4 {
if v, ok := others[4].(bool); ok && v {
// HttpOnly = true
httponly = true
}
}
if httponly {
fmt.Fprintf(&b, "; HttpOnly")
}
output.Context.ResponseWriter.Header().Add("Set-Cookie", b.String())
}
var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")
func sanitizeName(n string) string {
return cookieNameSanitizer.Replace(n)
}
var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ")
func sanitizeValue(v string) string {
return cookieValueSanitizer.Replace(v)
}
// Json writes json to response body.
// if coding is true, it converts utf-8 to \u0000 type.
func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error {
output.Header("Content-Type", "application/json;charset=UTF-8")
var content []byte
var err error
if hasIndent {
content, err = json.MarshalIndent(data, "", " ")
} else {
content, err = json.Marshal(data)
}
if err != nil {
http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
return err
}
if coding {
content = []byte(stringsToJson(string(content)))
}
output.Body(content)
return nil
}
// Jsonp writes jsonp to response body.
func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/javascript;charset=UTF-8")
var content []byte
var err error
if hasIndent {
content, err = json.MarshalIndent(data, "", " ")
} else {
content, err = json.Marshal(data)
}
if err != nil {
http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
return err
}
callback := output.Context.Input.Query("callback")
if callback == "" {
return errors.New(`"callback" parameter required`)
}
callback_content := bytes.NewBufferString(" " + template.JSEscapeString(callback))
callback_content.WriteString("(")
callback_content.Write(content)
callback_content.WriteString(");\r\n")
output.Body(callback_content.Bytes())
return nil
}
// Xml writes xml string to response body.
func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/xml;charset=UTF-8")
var content []byte
var err error
if hasIndent {
content, err = xml.MarshalIndent(data, "", " ")
} else {
content, err = xml.Marshal(data)
}
if err != nil {
http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
return err
}
output.Body(content)
return nil
}
// Download forces response for download file.
// it prepares the download response header automatically.
func (output *BeegoOutput) Download(file string, filename ...string) {
output.Header("Content-Description", "File Transfer")
output.Header("Content-Type", "application/octet-stream")
if len(filename) > 0 && filename[0] != "" {
output.Header("Content-Disposition", "attachment; filename="+filename[0])
} else {
output.Header("Content-Disposition", "attachment; filename="+filepath.Base(file))
}
output.Header("Content-Transfer-Encoding", "binary")
output.Header("Expires", "0")
output.Header("Cache-Control", "must-revalidate")
output.Header("Pragma", "public")
http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file)
}
// ContentType sets the content type from ext string.
// MIME type is given in mime package.
func (output *BeegoOutput) ContentType(ext string) {
if !strings.HasPrefix(ext, ".") {
ext = "." + ext
}
ctype := mime.TypeByExtension(ext)
if ctype != "" {
output.Header("Content-Type", ctype)
}
}
// SetStatus sets response status code.
// It writes response header directly.
func (output *BeegoOutput) SetStatus(status int) {
output.Status = status
}
// IsCachable returns boolean of this request is cached.
// HTTP 304 means cached.
func (output *BeegoOutput) IsCachable(status int) bool {
return output.Status >= 200 && output.Status < 300 || output.Status == 304
}
// IsEmpty returns boolean of this request is empty.
// HTTP 201204 and 304 means empty.
func (output *BeegoOutput) IsEmpty(status int) bool {
return output.Status == 201 || output.Status == 204 || output.Status == 304
}
// IsOk returns boolean of this request runs well.
// HTTP 200 means ok.
func (output *BeegoOutput) IsOk(status int) bool {
return output.Status == 200
}
// IsSuccessful returns boolean of this request runs successfully.
// HTTP 2xx means ok.
func (output *BeegoOutput) IsSuccessful(status int) bool {
return output.Status >= 200 && output.Status < 300
}
// IsRedirect returns boolean of this request is redirection header.
// HTTP 301,302,307 means redirection.
func (output *BeegoOutput) IsRedirect(status int) bool {
return output.Status == 301 || output.Status == 302 || output.Status == 303 || output.Status == 307
}
// IsForbidden returns boolean of this request is forbidden.
// HTTP 403 means forbidden.
func (output *BeegoOutput) IsForbidden(status int) bool {
return output.Status == 403
}
// IsNotFound returns boolean of this request is not found.
// HTTP 404 means forbidden.
func (output *BeegoOutput) IsNotFound(status int) bool {
return output.Status == 404
}
// IsClient returns boolean of this request client sends error data.
// HTTP 4xx means forbidden.
func (output *BeegoOutput) IsClientError(status int) bool {
return output.Status >= 400 && output.Status < 500
}
// IsServerError returns boolean of this server handler errors.
// HTTP 5xx means server internal error.
func (output *BeegoOutput) IsServerError(status int) bool {
return output.Status >= 500 && output.Status < 600
}
func stringsToJson(str string) string {
rs := []rune(str)
jsons := ""
for _, r := range rs {
rint := int(r)
if rint < 128 {
jsons += string(r)
} else {
jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json
}
}
return jsons
}
// Sessions sets session item value with given key.
func (output *BeegoOutput) Session(name interface{}, value interface{}) {
output.Context.Input.CruSession.Set(name, value)
}

View File

@ -1,13 +1,22 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"bytes"
"compress/gzip"
"compress/zlib"
"encoding/json"
"encoding/xml"
"errors"
"github.com/astaxie/beego/session"
"html/template"
"io"
"io/ioutil"
@ -15,23 +24,59 @@ import (
"net/http"
"net/url"
"os"
"path"
"reflect"
"strconv"
"strings"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/session"
)
type Controller struct {
Ctx *Context
Data map[interface{}]interface{}
ChildName string
TplNames string
Layout string
TplExt string
CruSession session.SessionStore
//commonly used mime-types
const (
applicationJson = "application/json"
applicationXml = "application/xml"
textXml = "text/xml"
)
var (
// custom error when user stop request handler manually.
USERSTOPRUN = errors.New("User stop run")
GlobalControllerRouter map[string][]ControllerComments = make(map[string][]ControllerComments) //pkgpath+controller:comments
)
// store the comment for the controller method
type ControllerComments struct {
Method string
Router string
AllowHTTPMethods []string
Params []map[string]string
}
// Controller defines some basic http request handler operations, such as
// http context, template and view, session and xsrf.
type Controller struct {
Ctx *context.Context
Data map[interface{}]interface{}
controllerName string
actionName string
TplNames string
Layout string
LayoutSections map[string]string // the key is the section name and the value is the template name
TplExt string
_xsrf_token string
gotofunc string
CruSession session.SessionStore
XSRFExpire int
AppController interface{}
EnableRender bool
EnableXSRF bool
methodMapping map[string]func() //method:routertree
}
// ControllerInterface is an interface to uniform all controller handler.
type ControllerInterface interface {
Init(ct *Context, cn string)
Init(ct *context.Context, controllerName, actionName string, app interface{})
Prepare()
Get()
Post()
@ -42,217 +87,324 @@ type ControllerInterface interface {
Options()
Finish()
Render() error
XsrfToken() string
CheckXsrfCookie() bool
HandlerFunc(fn string) bool
URLMapping()
}
func (c *Controller) Init(ctx *Context, cn string) {
c.Data = make(map[interface{}]interface{})
// Init generates default values of controller operations.
func (c *Controller) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
c.Layout = ""
c.TplNames = ""
c.ChildName = cn
c.controllerName = controllerName
c.actionName = actionName
c.Ctx = ctx
c.TplExt = "tpl"
c.AppController = app
c.EnableRender = true
c.EnableXSRF = true
c.Data = ctx.Input.Data
c.methodMapping = make(map[string]func())
}
// Prepare runs after Init before request function execution.
func (c *Controller) Prepare() {
}
// Finish runs after request function execution.
func (c *Controller) Finish() {
}
func (c *Controller) Destructor() {
if c.CruSession != nil {
c.CruSession.SessionRelease()
}
}
// Get adds a request function to handle GET request.
func (c *Controller) Get() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}
// Post adds a request function to handle POST request.
func (c *Controller) Post() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}
// Delete adds a request function to handle DELETE request.
func (c *Controller) Delete() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}
// Put adds a request function to handle PUT request.
func (c *Controller) Put() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}
// Head adds a request function to handle HEAD request.
func (c *Controller) Head() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}
// Patch adds a request function to handle PATCH request.
func (c *Controller) Patch() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}
// Options adds a request function to handle OPTIONS request.
func (c *Controller) Options() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
}
// call function fn
func (c *Controller) HandlerFunc(fnname string) bool {
if v, ok := c.methodMapping[fnname]; ok {
v()
return true
} else {
return false
}
}
// URLMapping register the internal Controller router.
func (c *Controller) URLMapping() {
}
func (c *Controller) Mapping(method string, fn func()) {
c.methodMapping[method] = fn
}
// Render sends the response with rendered template bytes as text/html type.
func (c *Controller) Render() error {
if !c.EnableRender {
return nil
}
rb, err := c.RenderBytes()
if err != nil {
return err
} else {
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
output_writer := c.Ctx.ResponseWriter.(io.Writer)
if EnableGzip == true && c.Ctx.Request.Header.Get("Accept-Encoding") != "" {
splitted := strings.SplitN(c.Ctx.Request.Header.Get("Accept-Encoding"), ",", -1)
encodings := make([]string, len(splitted))
for i, val := range splitted {
encodings[i] = strings.TrimSpace(val)
}
for _, val := range encodings {
if val == "gzip" {
c.Ctx.ResponseWriter.Header().Set("Content-Encoding", "gzip")
output_writer, _ = gzip.NewWriterLevel(c.Ctx.ResponseWriter, gzip.BestSpeed)
break
} else if val == "deflate" {
c.Ctx.ResponseWriter.Header().Set("Content-Encoding", "deflate")
output_writer, _ = zlib.NewWriterLevel(c.Ctx.ResponseWriter, zlib.BestSpeed)
break
}
}
} else {
c.Ctx.SetHeader("Content-Length", strconv.Itoa(len(rb)), true)
}
output_writer.Write(rb)
switch output_writer.(type) {
case *gzip.Writer:
output_writer.(*gzip.Writer).Close()
case *zlib.Writer:
output_writer.(*zlib.Writer).Close()
case io.WriteCloser:
output_writer.(io.WriteCloser).Close()
}
return nil
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
c.Ctx.Output.Body(rb)
}
return nil
}
// RenderString returns the rendered template string. Do not send out response.
func (c *Controller) RenderString() (string, error) {
b, e := c.RenderBytes()
return string(b), e
}
// RenderBytes returns the bytes of rendered template string. Do not send out response.
func (c *Controller) RenderBytes() ([]byte, error) {
//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 == "" {
c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt
c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
}
if RunMode == "dev" {
BuildTemplate(ViewsPath)
}
subdir := path.Dir(c.TplNames)
_, file := path.Split(c.TplNames)
newbytes := bytes.NewBufferString("")
if _, ok := BeeTemplates[subdir]; !ok {
if _, ok := BeeTemplates[c.TplNames]; !ok {
panic("can't find templatefile in the path:" + c.TplNames)
return []byte{}, errors.New("can't find templatefile in the path:" + c.TplNames)
}
BeeTemplates[subdir].ExecuteTemplate(newbytes, file, c.Data)
tplcontent, _ := ioutil.ReadAll(newbytes)
c.Data["LayoutContent"] = template.HTML(string(tplcontent))
subdir = path.Dir(c.Layout)
_, file = path.Split(c.Layout)
ibytes := bytes.NewBufferString("")
err := BeeTemplates[subdir].ExecuteTemplate(ibytes, file, c.Data)
err := BeeTemplates[c.TplNames].ExecuteTemplate(newbytes, c.TplNames, c.Data)
if err != nil {
Trace("template Execute err:", err)
return nil, err
}
tplcontent, _ := ioutil.ReadAll(newbytes)
c.Data["LayoutContent"] = template.HTML(string(tplcontent))
if c.LayoutSections != nil {
for sectionName, sectionTpl := range c.LayoutSections {
if sectionTpl == "" {
c.Data[sectionName] = ""
continue
}
sectionBytes := bytes.NewBufferString("")
err = BeeTemplates[sectionTpl].ExecuteTemplate(sectionBytes, sectionTpl, c.Data)
if err != nil {
Trace("template Execute err:", err)
return nil, err
}
sectionContent, _ := ioutil.ReadAll(sectionBytes)
c.Data[sectionName] = template.HTML(string(sectionContent))
}
}
ibytes := bytes.NewBufferString("")
err = BeeTemplates[c.Layout].ExecuteTemplate(ibytes, c.Layout, c.Data)
if err != nil {
Trace("template Execute err:", err)
return nil, err
}
icontent, _ := ioutil.ReadAll(ibytes)
return icontent, nil
} else {
if c.TplNames == "" {
c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt
c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
}
if RunMode == "dev" {
BuildTemplate(ViewsPath)
}
subdir := path.Dir(c.TplNames)
_, file := path.Split(c.TplNames)
ibytes := bytes.NewBufferString("")
if _, ok := BeeTemplates[subdir]; !ok {
if _, ok := BeeTemplates[c.TplNames]; !ok {
panic("can't find templatefile in the path:" + c.TplNames)
return []byte{}, errors.New("can't find templatefile in the path:" + c.TplNames)
}
err := BeeTemplates[subdir].ExecuteTemplate(ibytes, file, c.Data)
err := BeeTemplates[c.TplNames].ExecuteTemplate(ibytes, c.TplNames, c.Data)
if err != nil {
Trace("template Execute err:", err)
return nil, err
}
icontent, _ := ioutil.ReadAll(ibytes)
return icontent, nil
}
return []byte{}, nil
}
// Redirect sends the redirection response to url with status code.
func (c *Controller) Redirect(url string, code int) {
c.Ctx.Redirect(code, url)
}
// Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string.
func (c *Controller) Abort(code string) {
panic(code)
}
func (c *Controller) ServeJson() {
content, err := json.MarshalIndent(c.Data["json"], "", " ")
if err != nil {
http.Error(c.Ctx.ResponseWriter, err.Error(), http.StatusInternalServerError)
return
}
c.Ctx.SetHeader("Content-Length", strconv.Itoa(len(content)), true)
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json")
c.Ctx.ResponseWriter.Write(content)
}
func (c *Controller) ServeXml() {
content, err := xml.Marshal(c.Data["xml"])
if err != nil {
http.Error(c.Ctx.ResponseWriter, err.Error(), http.StatusInternalServerError)
return
}
c.Ctx.SetHeader("Content-Length", strconv.Itoa(len(content)), true)
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/xml")
c.Ctx.ResponseWriter.Write(content)
}
func (c *Controller) Input() url.Values {
ct := c.Ctx.Request.Header.Get("Content-Type")
if strings.Contains(ct, "multipart/form-data") {
c.Ctx.Request.ParseMultipartForm(MaxMemory) //64MB
status, err := strconv.Atoi(code)
if err == nil {
c.Ctx.Abort(status, code)
} else {
c.Ctx.Abort(200, code)
}
}
// CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body.
func (c *Controller) CustomAbort(status int, body string) {
c.Ctx.Abort(status, body)
}
// StopRun makes panic of USERSTOPRUN error and go to recover function if defined.
func (c *Controller) StopRun() {
panic(USERSTOPRUN)
}
// UrlFor does another controller handler in this request function.
// it goes to this controller method if endpoint is not clear.
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...)
}
}
// ServeJson sends a json response with encoding charset.
func (c *Controller) ServeJson(encoding ...bool) {
var hasIndent bool
var hasencoding bool
if RunMode == "prod" {
hasIndent = false
} else {
hasIndent = true
}
if len(encoding) > 0 && encoding[0] == true {
hasencoding = true
}
c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding)
}
// ServeJsonp sends a jsonp response.
func (c *Controller) ServeJsonp() {
var hasIndent bool
if RunMode == "prod" {
hasIndent = false
} else {
hasIndent = true
}
c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent)
}
// ServeXml sends xml response.
func (c *Controller) ServeXml() {
var hasIndent bool
if RunMode == "prod" {
hasIndent = false
} else {
hasIndent = true
}
c.Ctx.Output.Xml(c.Data["xml"], hasIndent)
}
// ServeFormatted serve Xml OR Json, depending on the value of the Accept header
func (c *Controller) ServeFormatted() {
accept := c.Ctx.Input.Header("Accept")
switch accept {
case applicationJson:
c.ServeJson()
case applicationXml, textXml:
c.ServeXml()
default:
c.ServeJson()
}
}
// Input returns the input data map from POST or PUT request body and query string.
func (c *Controller) Input() url.Values {
if c.Ctx.Request.Form == nil {
c.Ctx.Request.ParseForm()
}
return c.Ctx.Request.Form
}
// ParseForm maps input data map to obj struct.
func (c *Controller) ParseForm(obj interface{}) error {
return ParseForm(c.Input(), obj)
}
// GetString returns the input value by key string.
func (c *Controller) GetString(key string) string {
return c.Input().Get(key)
return c.Ctx.Input.Query(key)
}
// GetStrings returns the input string slice by key string.
// it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection.
func (c *Controller) GetStrings(key string) []string {
f := c.Input()
if f == nil {
return []string{}
}
vs := f[key]
if len(vs) > 0 {
return vs
}
return []string{}
}
// GetInt returns input value as int64.
func (c *Controller) GetInt(key string) (int64, error) {
return strconv.ParseInt(c.Input().Get(key), 10, 64)
return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64)
}
// GetBool returns input value as bool.
func (c *Controller) GetBool(key string) (bool, error) {
return strconv.ParseBool(c.Input().Get(key))
return strconv.ParseBool(c.Ctx.Input.Query(key))
}
// GetFloat returns input value as float64.
func (c *Controller) GetFloat(key string) (float64, error) {
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64)
}
// GetFile returns the file data in file upload field named as key.
// it returns the first one of multi-uploaded files.
func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) {
return c.Ctx.Request.FormFile(key)
}
// SaveToFile saves uploaded file to new path.
// it only operates the first one of mutil-upload form file field.
func (c *Controller) SaveToFile(fromfile, tofile string) error {
file, _, err := c.Ctx.Request.FormFile(fromfile)
if err != nil {
@ -268,13 +420,15 @@ func (c *Controller) SaveToFile(fromfile, tofile string) error {
return nil
}
// StartSession starts session and load old session data info this controller.
func (c *Controller) StartSession() session.SessionStore {
if c.CruSession == nil {
c.CruSession = GlobalSessions.SessionStart(c.Ctx.ResponseWriter, c.Ctx.Request)
c.CruSession = c.Ctx.Input.CruSession
}
return c.CruSession
}
// SetSession puts value into session.
func (c *Controller) SetSession(name interface{}, value interface{}) {
if c.CruSession == nil {
c.StartSession()
@ -282,6 +436,7 @@ func (c *Controller) SetSession(name interface{}, value interface{}) {
c.CruSession.Set(name, value)
}
// GetSession gets value from session.
func (c *Controller) GetSession(name interface{}) interface{} {
if c.CruSession == nil {
c.StartSession()
@ -289,9 +444,76 @@ func (c *Controller) GetSession(name interface{}) interface{} {
return c.CruSession.Get(name)
}
// SetSession removes value from session.
func (c *Controller) DelSession(name interface{}) {
if c.CruSession == nil {
c.StartSession()
}
c.CruSession.Delete(name)
}
// SessionRegenerateID regenerates session id for this session.
// the session data have no changes.
func (c *Controller) SessionRegenerateID() {
if c.CruSession != nil {
c.CruSession.SessionRelease(c.Ctx.ResponseWriter)
}
c.CruSession = GlobalSessions.SessionRegenerateId(c.Ctx.ResponseWriter, c.Ctx.Request)
c.Ctx.Input.CruSession = c.CruSession
}
// DestroySession cleans session data and session cookie.
func (c *Controller) DestroySession() {
c.Ctx.Input.CruSession.Flush()
GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request)
}
// IsAjax returns this request is ajax or not.
func (c *Controller) IsAjax() bool {
return c.Ctx.Input.IsAjax()
}
// GetSecureCookie returns decoded cookie value from encoded browser cookie values.
func (c *Controller) GetSecureCookie(Secret, key string) (string, bool) {
return c.Ctx.GetSecureCookie(Secret, key)
}
// SetSecureCookie puts value into cookie after encoded the value.
func (c *Controller) SetSecureCookie(Secret, name, value string, others ...interface{}) {
c.Ctx.SetSecureCookie(Secret, name, value, others...)
}
// XsrfToken creates a xsrf token string and returns.
func (c *Controller) XsrfToken() string {
if c._xsrf_token == "" {
var expire int64
if c.XSRFExpire > 0 {
expire = int64(c.XSRFExpire)
} else {
expire = int64(XSRFExpire)
}
c._xsrf_token = c.Ctx.XsrfToken(XSRFKEY, expire)
}
return c._xsrf_token
}
// CheckXsrfCookie checks xsrf token in this request is valid or not.
// the token can provided in request header "X-Xsrftoken" and "X-CsrfToken"
// or in form field value named as "_xsrf".
func (c *Controller) CheckXsrfCookie() bool {
if !c.EnableXSRF {
return true
}
return c.Ctx.CheckXsrfCookie()
}
// XsrfFormHtml writes an input field contains xsrf token value.
func (c *Controller) XsrfFormHtml() string {
return "<input type=\"hidden\" name=\"_xsrf\" value=\"" +
c._xsrf_token + "\"/>"
}
// GetControllerAndAction gets the executing controller name and action name.
func (c *Controller) GetControllerAndAction() (controllerName, actionName string) {
return c.controllerName, c.actionName
}

52
docs.go Normal file
View File

@ -0,0 +1,52 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"encoding/json"
"github.com/astaxie/beego/context"
)
var GlobalDocApi map[string]interface{}
func init() {
if EnableDocs {
GlobalDocApi = make(map[string]interface{})
}
}
func serverDocs(ctx *context.Context) {
var obj interface{}
if splat := ctx.Input.Param(":splat"); splat == "" {
obj = GlobalDocApi["Root"]
} else {
if v, ok := GlobalDocApi[splat]; ok {
obj = v
}
}
if obj != nil {
bt, err := json.Marshal(obj)
if err != nil {
ctx.Output.SetStatus(504)
return
}
ctx.Output.Header("Content-Type", "application/json;charset=UTF-8")
ctx.Output.Header("Access-Control-Allow-Origin", "*")
ctx.Output.Body(bt)
return
}
ctx.Output.SetStatus(404)
}

View File

@ -1,25 +0,0 @@
#Installation
Beego is a simple web framework, but it uses many third-party packages, so you have to install all dependency packages also.
- Before anything you do, you have to check that you installed Go in your computer, see more detail about Go installation in my book: [Chapter 1](https://github.com/Unknwon/build-web-application-with-golang_EN/blob/master/eBook/01.1.md)
- Use `go get ` to install Beego:
go get github.com/astaxie/beego
- Install bee tools for fast-develop Beego applications:
go get github.com/astaxie/bee
Good job, you're ready to Beego with powerful bee tools!
![](images/bee.png)
Beego has following dependency packages:
- Session module: [github.com/astaxie/beego/session](github.com/astaxie/beego/session)
- To support redis engine: [github.com/garyburd/redigo/redis](github.com/garyburd/redigo/redis)
- To support mysql engine: [github.com/go-sql-driver/mysql](github.com/go-sql-driver/mysql)
- To support markdown as template function: [github.com/russross/blackfriday](github.com/russross/blackfriday)
- [Introduction](README.md)
- [Quick start](Quickstart.md)

View File

@ -1,979 +0,0 @@
# Quick start
Hey, you say you've never heard about Beego and don't know how to use it? Don't worry, after you read this section, you will know a lot about Beego. Before you start reading, make sure you installed Beego in your computer, if not, check this tutorial: [Installation](Install.md)
**Navigation**
- [Hello world](#hello-world)
- [New project](#new-project)
- [Development mode](#development-mode)
- [Router](#router)
- [Static files](#static-files)
- [Filter and middleware](#filter-and-middleware)
- [Controller](#controller)
- [Template](#template)
- [Handle request](#handle-request)
- [Redirect and error](#redirect-and-error)
- [Handle response](#handle-response)
- [Sessions](#sessions)
- [Cache](#cache)
- [Safe map](#safe-map)
- [Log](#log)
- [Configuration](#configuration)
- [Beego arguments](#beego-arguments)
- [Integrated third-party applications](#integrated-third-party-applications)
- [Deployment](#deployment)
## Hello world
This is an example of "Hello world" in Beego:
package main
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Ctx.WriteString("hello world")
}
func main() {
beego.Router("/", &MainController{})
beego.Run()
}
Save file as "hello.go", build and run it:
$ go build main.go
$ ./hello
Open address [http://127.0.0.1:8080](http://127.0.0.1:8080) in your browser and you will see "hello world".
What happened in behind above example?
1. We import package `github.com/astaxie/beego`. As we know that Go initialize packages and runs init() function in every package(more detail [here](https://github.com/Unknwon/build-web-application-with-golang_EN/blob/master/eBook/02.3.md#main-function-and-init-function)), so Beego initializes the BeeApp application at this time.
2. Define controller. We define a struct called `MainController` with a anonymous field `beego.Controller`, so the `MainController` has all methods that `beego.Controller` has.
3. Define RESTful methods. Once we use anonymous combination, `MainController` has already had `Get`, `Post`, `Delete`, `Put` and other methods, these methods will be called when user sends corresponding request, like `Post` method for requests that are using POST method. Therefore, after we overloaded `Get` method in `MainController`, all GET requests will use `Get` method in `MainController` instead of in `beego.Controller`.
4. Define main function. All applications in Go use main function as entry point as C does.
5. Register routers, it tells Beego which controller is responsibility for specific requests. Here we register `/` for `MainController`, so all requests in `/` will be handed to `MainController`. Be aware that the first argument is the path and the second one is pointer of controller that you want to register.
6. Run application in port 8080 as default, press `Ctrl+c` to exit.
## New project
Get into your $GOPATH, then use following command to setup Beego project:
bee create hello
It generates folders and files for your project, directory structure as follows:
.
├── conf
│ └── app.conf
├── controllers
│ └── default.go
├── main.go
├── models
├── static
│ ├── css
│ ├── img
│ └── js
└── views
└── index.tpl
## Development mode
Beego uses development mode as default, you can use following code to change mode in your application:
beego.RunMode = "pro"
Or use configuration file in `conf/app.conf`, and input following content:
runmode = pro
No differences between two ways.
In development mode, you have following effects:
- If you don't have directory `views`, it prints following error prompt:
2013/04/13 19:36:17 [W] [stat views: no such file or directory]
- It doesn't cache template and reload every time.
- If panic occurs in your server, it prints information like following screen shot:
![](images/dev.png)
## Router
The main function of router is to connect request URL and handler. Beego wrapped `Controller`, so it connects request URL and `ControllerInterface`. The `ControllerInterface` has following methods:
type ControllerInterface interface {
Init(ct *Context, cn string)
Prepare()
Get()
Post()
Delete()
Put()
Head()
Patch()
Options()
Finish()
Render() error
}
`beego.Controller` implemented all of them, so you just use this struct as anonymous field in your controller struct. Of course you have to overload corresponding methods for more specific usages.
Users can use following ways to register route rules:
beego.Router("/", &controllers.MainController{})
beego.Router("/admin", &admin.UserController{})
beego.Router("/admin/index", &admin.ArticleController{})
beego.Router("/admin/addpkg", &admin.AddController{})
For more convenient configure route rules, Beego references the idea from sinatra, so it supports more kinds of route rules as follows:
- beego.Router("/api/:id([0-9]+)", &controllers.RController{})
Customized regular expression match // match /api/123 :id= 123
- beego.Router("/news/:all", &controllers.RController{})
Match rest of all // match /news/path/to/123.html :all= path/to/123.html
- beego.Router("/user/:username([\w]+)", &controllers.RController{})
Regular expression // match /user/astaxie :username = astaxie
- beego.Router("/download/`*`.`*`", &controllers.RController{})
Wildcard character // match /download/file/api.xml :path= file/api :ext=xml
- beego.Router("/download/ceshi/`*`", &controllers.RController{})
wildcard character match rest of all // match /download/ceshi/file/api.json :splat=file/api.json
- beego.Router("/:id:int", &controllers.RController{})
Match type int // match :id is int type, Beego uses regular expression ([0-9]+) automatically
- beego.Router("/:hi:string", &controllers.RController{})
Match type string // match :hi is string type, Beego uses regular expression ([\w]+) automatically
##Static files
Go provides `http.ServeFile` for static files, Beego wrapped this function and use following way to register static file folder:
beego.SetStaticPath("/static","public")
- The first argument is the path of your URL.
- The second argument is the directory in your application path.
Beego supports multiple static file directories as follows:
beego.SetStaticPath("/images","images")
beego.SetStaticPath("/css","css")
beego.SetStaticPath("/js","js")
After you setting static directory, when users visit `/images/login/login.png`Beego accesses `images/login/login.png` in related to your application directory. One more example, if users visit `/static/img/logo.png`, Beego accesses file `public/img/logo.png`.
##Filter and middleware
Beego supports customized filter and middleware, such as security verification, force redirect, etc.
Here is an example of verify user name of all requests, check if it's admin.
var FilterUser = func(w http.ResponseWriter, r *http.Request) {
if r.URL.User == nil || r.URL.User.Username() != "admin" {
http.Error(w, "", http.StatusUnauthorized)
}
}
beego.Filter(FilterUser)
You can also filter by arguments:
beego.Router("/:id([0-9]+)", &admin.EditController{})
beego.FilterParam("id", func(rw http.ResponseWriter, r *http.Request) {
dosomething()
})
Filter by prefix is also available:
beego.FilterPrefixPath("/admin", func(rw http.ResponseWriter, r *http.Request) {
dosomething()
})
##Controller
Use `beego.controller` as anonymous in your controller struct to implement the interface in Beego:
type xxxController struct {
beego.Controller
}
`beego.Controller` implemented`beego.ControllerInterface`, `beego.ControllerInterface` defined following methods:
- Init(ct `*`Context, cn string)
Initialize context, controller's name, template's name, and container of template arguments
- Prepare()
This is for expend usages, it executes before all the following methods. Users can overload this method for verification for example.
- Get()
This method executes when client sends request as GET method, 403 as default status code. Users overload this method for customized handle process of GET method.
- Post()
This method executes when client sends request as POST method, 403 as default status code. Users overload this method for customized handle process of POST method.
- Delete()
This method executes when client sends request as DELETE method, 403 as default status code. Users overload this method for customized handle process of DELETE method.
- Put()
This method executes when client sends request as PUT method, 403 as default status code. Users overload this method for customized handle process of PUT method.
- Head()
This method executes when client sends request as HEAD method, 403 as default status code. Users overload this method for customized handle process of HEAD method.
- Patch()
This method executes when client sends request as PATCH method, 403 as default status code. Users overload this method for customized handle process of PATCH method.
- Options()
This method executes when client sends request as OPTIONS method, 403 as default status code. Users overload this method for customized handle process of OPTIONS method.
- Finish()
This method executes after corresponding method finished, empty as default. User overload this method for more usages like close database, clean data, etc.
- Render() error
This method is for rendering template, it executes automatically when you set beego.AutoRender to true.
Overload all methods for all customized logic processes, let's see an example:
type AddController struct {
beego.Controller
}
func (this *AddController) Prepare() {
}
func (this *AddController) Get() {
this.Data["content"] ="value"
this.Layout = "admin/layout.html"
this.TplNames = "admin/add.tpl"
}
func (this *AddController) Post() {
pkgname := this.GetString("pkgname")
content := this.GetString("content")
pk := models.GetCruPkg(pkgname)
if pk.Id == 0 {
var pp models.PkgEntity
pp.Pid = 0
pp.Pathname = pkgname
pp.Intro = pkgname
models.InsertPkg(pp)
pk = models.GetCruPkg(pkgname)
}
var at models.Article
at.Pkgid = pk.Id
at.Content = content
models.InsertArticle(at)
this.Ctx.Redirect(302, "/admin/index")
}
##Template
###Template directory
Beego uses `views` as the default directory for template files, parses and caches them as needed(cache is not enable in develop mode), but you can **change**(because only one directory can be used for template files) its directory using following code:
beego.ViewsPath = "/myviewpath"
###Auto-render
You don't need to call render function manually, Beego calls it automatically after corresponding methods executed. If your application is somehow doesn't need templates, you can disable this feature either in code of `main.go` or configuration file.
To disable auto-render in configuration file:
autorender = false
To disable auto-render in `main.go`(before you call `beego.Run()` to run the application):
beego.AutoRender = false
###Template data
You can use `this.Data` in controller methods to access the data in templates. Suppose you want to get content of `{{.Content}}`, you can use following code to do this:
this.Data["Context"] = "value"
###Template name
Beego uses built-in template engine of Go, so there is no different in syntax. As for how to write template file, please visit [Template tutorial](https://github.com/Unknwon/build-web-application-with-golang_EN/blob/master/eBook/07.4.md)。
Beego parses template files in `viewpath` and render it after you set the name of the template file in controller methods. For example, Beego finds the file `add.tpl` in directory `admin` in following code:
this.TplNames = "admin/add.tpl"
Beego supports two kinds of extensions for template files, which are `tpl` and `html`, if you want to use other extensions, you have to use following code to let Beego know:
beego.AddTemplateExt("<your template file extension>")
If you enabled auto-render and you don't tell Beego which template file you are going to use in controller methods, Beego uses following format to find the template file if it exists:
c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt
Which is `<corresponding controller name>/<request method name>.<template extension>`. For example, your controller name is `AddController` and the request method is POST, and the default file extension is `tpl`, so Beego will try to find file `/<viewpath>/AddController/POST.tpl`.
###Layout design
Beego supports layout design, which means if you are working on an administration application, and some part of its user interface is exactly same all the time, then you can make this part as a layout.
this.Layout = "admin/layout.html"
this.TplNames = "admin/add.tpl"
You have to set following variable in order to make Beego possible to insert your dynamic content:
{{.LayoutContent}}
Beego parses template file and assign content to `LayoutContent`, and render them together.
Right now, Beego caches all template files, so you can use following way to implement another kind of layout:
{{template "header.html"}}
Handle logic
{{template "footer.html"}}
###Template function
Beego supports customized template functions that are registered before you call `beego.Run()`.
func hello(in string)(out string){
out = in + "world"
return
}
beego.AddFuncMap("hi",hello)
Then you can use this function in your template files:
{{.Content | hi}}
There are some built-in template functions:
* markdown
This function converts markdown content to HTML format, use {{markdown .Content}} in template files.
* dateformat
This function converts time to formatted string, use {{dateformat .Time "2006-01-02T15:04:05Z07:00"}} in template files.
* date
This function implements date function like in PHP, use formatted string to get corresponding time, use {{date .T "Y-m-d H:i:s"}} in template files.
* compare
This functions compares two objects, returns true if they are same, false otherwise, use {{compare .A .B}} in template files.
* substr
This function cuts out string from another string by index, it supports UTF-8 characters, use {{substr .Str 0 30}} in template files.
* html2str
This function escapes HTML to raw string, use {{html2str .Htmlinfo}} in template files.
* str2html
This function outputs string in HTML format without escaping, use {{str2html .Strhtml}} in template files.
* htmlquote
This functions implements basic HTML escape, use {{htmlquote .quote}} in template files.
* htmlunquote
This functions implements basic invert-escape of HTML, use {{htmlunquote .unquote}} in template files.
##Handle request
We always need to get data from users, including methods like GET, POST, etc. Beego parses these data automatically, and you can access them by following code:
- GetString(key string) string
- GetInt(key string) (int64, error)
- GetBool(key string) (bool, error)
Usage example:
func (this *MainController) Post() {
jsoninfo := this.GetString("jsoninfo")
if jsoninfo == "" {
this.Ctx.WriteString("jsoninfo is empty")
return
}
}
If you need other types that are not included above, like you need int64 instead of int, then you need to do following way:
func (this *MainController) Post() {
id := this.Input().Get("id")
intid, err := strconv.Atoi(id)
}
To use `this.Ctx.Request` for more information about request, and object properties and method please read [Request](http://golang.org/pkg/net/http/#Request)
###File upload
It's very easy to upload file through Beego, but don't forget to add `enctype="multipart/form-data"` in your form, otherwise the browser will not upload anything.
Files will be saved in memory, if the size is greater than cache memory, the rest part will be saved as temporary file. The default cache memory is 64 MB, and you can using following ways to change this size.
In code:
beego.MaxMemory = 1<<22
In configuration file:
maxmemory = 1<<22
Beego provides two convenient functions to upload files:
- GetFile(key string) (multipart.File, `*`multipart.FileHeader, error)
This function is mainly used to read file name element `the_file` in form and returns corresponding information. You can use this information either filter or save files.
- SaveToFile(fromfile, tofile string) error
This function a wrapper of GetFile and gives ability to save file.
This is an example to save file that is uploaded:
func (this *MainController) Post() {
this.SaveToFile("the_file","/var/www/uploads/uploaded_file.txt"")
}
###Output Json and XML
Beego considered API function design at the beginning, and we often use Json or XML format data as output. Therefore, it's no reason that Beego doesn't support it:
Set `content-type` to `application/json` for output raw Json format data:
func (this *AddController) Get() {
mystruct := { ... }
this.Data["json"] = &mystruct
this.ServeJson()
}
Set `content-type` to `application/xml` for output raw XML format data:
func (this *AddController) Get() {
mystruct := { ... }
this.Data["xml"]=&mystruct
this.ServeXml()
}
##Redirect and error
You can use following to redirect:
func (this *AddController) Get() {
this.Redirect("/", 302)
}
@todo Error processing need to be improved.
##Handle response
There are some situations that you may have in response:
1. Output template
I've already talked about template above, Beego outputs template after corresponding method executed.
2. Redirect
You can use this.Redirect("/", 302) to redirect page.
3. Output string
Sometimes we just need to print string on the screen:
this.Ctx.WriteString("ok")
## Sessions
Beego has a built-in session module and supports four engines, including memory, file, MySQL and redis. You can implement your own engine based on the interface.
It's easy to use session in Beego, use following code in your main() function:
beego.SessionOn = true
Or use configuration file:
sessionon = true
The following example shows you how to use session in Beego:
func (this *MainController) Get() {
v := this.GetSession("asta")
if v == nil {
this.SetSession("asta", int(1))
this.Data["num"] = 0
} else {
this.SetSession("asta", v.(int)+1)
this.Data["num"] = v.(int)
}
this.TplNames = "index.tpl"
}
We can see that there are few convenient methods:
- SetSession(name string, value interface{})
- GetSession(name string) interface{}
- DelSession(name string)
There are three kinds of operation for session: set, get, and delete.
Of course you can use following code to customized session logic:
sess:=this.StartSession()
defer sess.SessionRelease()
The sess object has following methods:
* sess.Set()
* sess.Get()
* sess.Delete()
* sess.SessionID()
However, I recommend you to use SetSessionGetSessionDelSession these three operations in order to prevent resource leak.
There are some arguments you can use in session module:
- SessionOn
Whether enable session or not, default is false, corresponding arguments in configuration file: sessionon.
- SessionProvider
Setting session engine, default is memory, other options are file, MySQL and redis, corresponding arguments in configuration file: sessionprovider.
- SessionName
Setting name of cookies, it saves in users' browser with name beegosessionID, corresponding arguments in configuration file: sessionname.
- SessionGCMaxLifetime
Setting session expired time, default is 3600 seconds, corresponding arguments in configuration: sessiongcmaxlifetime
- SessionSavePath
Setting save path or link address of corresponding file, MySQL and redis engines, default is empty, corresponding arguments in configuration file: sessionsavepath
When the SessionProvider is file, SessionSavePath saves file path:
beego.SessionProvider = "file"
beego.SessionSavePath = "./tmp"
When the SessionProvider is mysql, SessionSavePath is link address, it uses driver [go-sql-driver](https://github.com/go-sql-driver/mysql):
beego.SessionProvider = "mysql"
beego.SessionSavePath = "username:password@protocol(address)/dbname?param=value"
When the SessionProvider is redis, SessionSavePath is link address of redis, it uses driver [redigo](https://github.com/garyburd/redigo):
beego.SessionProvider = "redis"
beego.SessionSavePath = "127.0.0.1:6379"
## Cache
Beego has a built-in cache module, it's like memcache, which caches data in memory. Here is an example of using cache module in Beego:
var (
urllist *beego.BeeCache
)
func init() {
urllist = beego.NewBeeCache()
urllist.Every = 0 // Not expired
urllist.Start()
}
func (this *ShortController) Post() {
var result ShortResult
longurl := this.Input().Get("longurl")
beego.Info(longurl)
result.UrlLong = longurl
urlmd5 := models.GetMD5(longurl)
beego.Info(urlmd5)
if urllist.IsExist(urlmd5) {
result.UrlShort = urllist.Get(urlmd5).(string)
} else {
result.UrlShort = models.Generate()
err := urllist.Put(urlmd5, result.UrlShort, 0)
if err != nil {
beego.Info(err)
}
err = urllist.Put(result.UrlShort, longurl, 0)
if err != nil {
beego.Info(err)
}
}
this.Data["json"] = result
this.ServeJson()
}
To use cache, you need to initialize a `beego.NewBeeCache` object and set expired time, and enable expired check. Then you can use following methods to achieve other operations:
- Get(name string) interface{}
- Put(name string, value interface{}, expired int) error
- Delete(name string) (ok bool, err error)
- IsExist(name string) bool
##Safe map
We know that map is not thread safe in Go, if you don't know it, this article may be helpful for you: [atomic_maps](http://golang.org/doc/faq#atomic_maps). However, we need a kind of thread safe map in practice, especially when we are using goroutines. Therefore, Beego provides a simple built-in thread safe map implementation.
bm := NewBeeMap()
if !bm.Set("astaxie", 1) {
t.Error("set Error")
}
if !bm.Check("astaxie") {
t.Error("check err")
}
if v := bm.Get("astaxie"); v.(int) != 1 {
t.Error("get err")
}
bm.Delete("astaxie")
if bm.Check("astaxie") {
t.Error("delete err")
}
This map has following interfaces:
- Get(k interface{}) interface{}
- Set(k interface{}, v interface{}) bool
- Check(k interface{}) bool
- Delete(k interface{})
##Log
Beego has a default BeeLogger object that outputs log into stdout, and you can use your own logger as well:
beego.SetLogger(*log.Logger)
You can output everything that implemented `*log.Logger`, for example, write to file:
fd,err := os.OpenFile("/var/log/beeapp/beeapp.log", os.O_RDWR|os.O_APPEND, 0644)
if err != nil {
beego.Critical("openfile beeapp.log:", err)
return
}
lg := log.New(fd, "", log.Ldate|log.Ltime)
beego.SetLogger(lg)
###Different levels of log
* Trace(v ...interface{})
* Debug(v ...interface{})
* Info(v ...interface{})
* Warn(v ...interface{})
* Error(v ...interface{})
* Critical(v ...interface{})
You can use following code to set log level:
beego.SetLevel(beego.LevelError)
Your project may have a lot of log outputs, but you don't want to output everything after your application is running on the internet, for example, you want to ignore Trace, Debug and Info level log outputs, you can use following setting:
beego.SetLevel(beego.LevelWarning)
Then Beego will not output log that has lower level of LevelWarning. Here is the list of all log levels, order from lower to higher:
LevelTraceLevelDebugLevelInfoLevelWarning LevelErrorLevelCritical
You can use different log level to output different error messages, it's based on how critical the error you think it is:
### Examples of log messages
- Trace
* "Entered parse function validation block"
* "Validation: entered second 'if'"
* "Dictionary 'Dict' is empty. Using default value"
- Debug
* "Web page requested: http://somesite.com Params='...'"
* "Response generated. Response size: 10000. Sending."
* "New file received. Type:PNG Size:20000"
- Info
* "Web server restarted"
* "Hourly statistics: Requested pages: 12345 Errors: 123 ..."
* "Service paused. Waiting for 'resume' call"
- Warn
* "Cache corrupted for file='test.file'. Reading from back-end"
* "Database 192.168.0.7/DB not responding. Using backup 192.168.0.8/DB"
* "No response from statistics server. Statistics not sent"
- Error
* "Internal error. Cannot process request #12345 Error:...."
* "Cannot perform login: credentials DB not responding"
- Critical
* "Critical panic received: .... Shutting down"
* "Fatal error: ... App is shutting down to prevent data corruption or loss"
### Example
func internalCalculationFunc(x, y int) (result int, err error) {
beego.Debug("calculating z. x:",x," y:",y)
z := y
switch {
case x == 3 :
beego.Trace("x == 3")
panic("Failure.")
case y == 1 :
beego.Trace("y == 1")
return 0, errors.New("Error!")
case y == 2 :
beego.Trace("y == 2")
z = x
default :
beego.Trace("default")
z += x
}
retVal := z-3
beego.Debug("Returning ", retVal)
return retVal, nil
}
func processInput(input inputData) {
defer func() {
if r := recover(); r != nil {
beego.Error("Unexpected error occurred: ", r)
outputs <- outputData{result : 0, error : true}
}
}()
beego.Info("Received input signal. x:",input.x," y:", input.y)
res, err := internalCalculationFunc(input.x, input.y)
if err != nil {
beego.Warn("Error in calculation:", err.Error())
}
beego.Info("Returning result: ",res," error: ",err)
outputs <- outputData{result : res, error : err != nil}
}
func main() {
inputs = make(chan inputData)
outputs = make(chan outputData)
criticalChan = make(chan int)
beego.Info("App started.")
go consumeResults(outputs)
beego.Info("Started receiving results.")
go generateInputs(inputs)
beego.Info("Started sending signals.")
for {
select {
case input := <- inputs:
processInput(input)
case <- criticalChan:
beego.Critical("Caught value from criticalChan: Go shut down.")
panic("Shut down due to critical fault.")
}
}
}
##Configuration
Beego supports to parse .ini file in path `conf/app.conf`, and you have following options:
appname = beepkg
httpaddr = "127.0.0.1"
httpport = 9090
runmode ="dev"
autorender = false
autorecover = false
viewspath = "myview"
If you set value in configuration file, Beego uses it to replace default value.
You can also have other values for your application, for example, database connection information:
mysqluser = "root"
mysqlpass = "rootpass"
mysqlurls = "127.0.0.1"
mysqldb = "beego"
Then use following code to load your settings:
beego.AppConfig.String("mysqluser")
beego.AppConfig.String("mysqlpass")
beego.AppConfig.String("mysqlurls")
beego.AppConfig.String("mysqldb")
AppConfig supports following methods:
- Bool(key string) (bool, error)
- Int(key string) (int, error)
- Int64(key string) (int64, error)
- Float(key string) (float64, error)
- String(key string) string
##Beego arguments
Beego has many configurable arguments, let me introduce to you all of them, so you can use them for more usage in your application:
* BeeApp
Entry point of Beego, it initialized in init() function when you import Beego package.
* AppConfig
It stores values from file `conf/app.conf` and initialized in init() function.
* HttpAddr
Application listening address, default is empty for listening all IP.
* HttpPort
Application listening port, default is 8080.
* AppName
Application name, default is "beego".
* RunMode
Application mode, default is "dev" develop mode and gives friendly error messages.
* AutoRender
This value indicates whether auto-render or not, default is true, you should set to false for API usage applications.
* RecoverPanic
This value indicates whether recover from panic or not, default is true, and program will not exit when error occurs.
* PprofOn
This value indicates whether enable pprof or not, default is false, and you can use following address to see goroutine execution status once you enable this feature.
/debug/pprof
/debug/pprof/cmdline
/debug/pprof/profile
/debug/pprof/symbol
For more information about pprof, please read [pprof](http://golang.org/pkg/net/http/pprof/)
* ViewsPath
Template path, default is "views".
* SessionOn
This value indicate whether enable session or not, default is false.
* SessionProvider
Session engine, default is memory.
* SessionName
Name for cookie that save in client browser, default is "beegosessionID".
* SessionGCMaxLifetime
Session expired time, default is 3600 seconds.
* SessionSavePath
Save path of session, default is empty.
* UseFcgi
This value indicates whether enable fastcgi or not, default is false.
* MaxMemory
Maximum memory size for file upload, default is `1 << 26`(64M).
##Integrated third-party applications
Beego supports to integrate third-party application, you can customized `http.Handler` as follows:
beego.RouterHandler("/chat/:info(.*)", sockjshandler)
sockjshandler implemented interface `http.Handler`.
Beego has an example for supporting chat of sockjs, here is the code:
package main
import (
"fmt"
"github.com/astaxie/beego"
"github.com/fzzy/sockjs-go/sockjs"
"strings"
)
var users *sockjs.SessionPool = sockjs.NewSessionPool()
func chatHandler(s sockjs.Session) {
users.Add(s)
defer users.Remove(s)
for {
m := s.Receive()
if m == nil {
break
}
fullAddr := s.Info().RemoteAddr
addr := fullAddr[:strings.LastIndex(fullAddr, ":")]
m = []byte(fmt.Sprintf("%s: %s", addr, m))
users.Broadcast(m)
}
}
type MainController struct {
beego.Controller
}
func (m *MainController) Get() {
m.TplNames = "index.html"
}
func main() {
conf := sockjs.NewConfig()
sockjshandler := sockjs.NewHandler("/chat", chatHandler, conf)
beego.Router("/", &MainController{})
beego.RouterHandler("/chat/:info(.*)", sockjshandler)
beego.Run()
}
The above example implemented a simple chat room for sockjs, and you can use `http.Handler` for more extensions.
##Deployment
Go compiles program to binary file, you only need to copy this binary to your server and run it. Because Beego uses MVC model, so you may have folders for static files, configuration files and template files, so you have to copy those files as well. Here is a real example for deployment.
$ mkdir /opt/app/beepkg
$ cp beepkg /opt/app/beepkg
$ cp -fr views /opt/app/beepkg
$ cp -fr static /opt/app/beepkg
$ cp -fr conf /opt/app/beepkg
Here is the directory structure pf `/opt/app/beepkg`.
.
├── conf
├── app.conf
├── static
├── css
├── img
└── js
└── views
└── index.tpl
├── beepkg
Now you can run your application in server, here are two good ways to manage your applications, and I recommend the first one.
- Supervisord
More information: [Supervisord](Supervisord.md)
- nohup
nohup ./beepkg &
- [Introduction](README.md)
- [Step by step](Tutorial.md)

View File

@ -1,52 +0,0 @@
#Beego
Beego is a lightweight, open source, non-blocking and scalable web framework for the Go programming language. It's like tornado in Python. This web framework has already been using for building web server and tools in SNDA's CDN system. Documentation and downloads available at [http://astaxie.github.com/beego](http://astaxie.github.com/beego)
It has following main features:
- Supports MVC model, you only need to focus on logic and implementation methods.
- Supports websocket, use customized handlers to integrate sockjs.
- Supports customized router rules, including regex and semanteme.
- Session integration, supports memory, file, redis, mysql, etc.
- Automated parsing user form, you can get data very easy.
- Log level system, easy to record debugging and deployment logs.
- Use configuration file (.ini) to customized your system.
- Use built-in templates in Go, and it provides much more useful functions which are commonly used in web development.
The working principles of Beego as follows:
![](images/beego.png)
Beego is licensed under the Apache Licence, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0.html).
#Simple example
The following example prints string "Hello world" to your browser, it shows how easy to build a web application with Beego.
package main
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Ctx.WriteString("hello world")
}
func main() {
beego.Router("/", &MainController{})
beego.Run()
}
#Handbook
- [Purposes](Why.md)
- [Installation](Install.md)
- [Quick start](Quickstart.md)
- [Step by step](Tutorial.md)
- [Real world usage](Application.md)
#Documentation
[godoc](http://godoc.org/github.com/astaxie/beego)

View File

@ -1,34 +0,0 @@
##supervisord
1. Installation
wget http://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg
sh setuptools-0.6c11-py2.7.egg
easy_install supervisor
echo_supervisord_conf >/etc/supervisord.conf
mkdir /etc/supervisord.conf.d
2. Configure /etc/supervisord.conf
[include]
files = /etc/supervisord.conf.d/*.conf
3. Add new application
cd /etc/supervisord.conf.d
vim beepkg.conf
Configuration file:
[program:beepkg]
directory = /opt/app/beepkg
command = /opt/app/beepkg/beepkg
autostart = true
startsecs = 5
user = root
redirect_stderr = true
stdout_logfile = /var/log/supervisord/beepkg.log

View File

@ -1,13 +0,0 @@
# 一步一步跟我写博客
## 创建项目
## 数据库结构设计
## 控制器设计
## 模板设计
## 用户登陆退出
## 数据库操作

View File

@ -1,19 +0,0 @@
# Design purposes and ideas
People may ask me why I want to build a new web framework rather than use other good ones. I know there are many excellent web frameworks on the internet and almost all of them are open source, and I have my reasons to do this.
Remember when I was writing the book about how to build web applications with Go, I just wanted to tell people what were my valuable experiences with Go in web development, especially I have been working with PHP and Python for almost ten years. At first, I didn't realize that a small web framework can give great help to web developers when they are learning to build web applications in a new programming language, and it also helps people more by studying its source code. Finally, I decided to write a open source web framework called Beego as supporting materiel for my book.
I used to use CI in PHP and tornado in Python, there are both lightweight, so they has following advantages:
1. Save time for handling general problems, I only need to care about logic part.
2. Learn more about languages by studying their source code, it's not hard to read and understand them because they are both lightweight frameworks.
3. It's quite easy to make secondary development of these frameworks for specific purposes.
Those reasons are my original intention of implementing Beego, and used two chapters in my book to introduce and design this lightweight web framework in GO.
Then I started to design logic execution of Beego. Because Go and Python have somewhat similar, I referenced some ideas from tornado to design Beego. As you can see, there is no different between Beego and tornado in RESTful processing; they both use GET, POST or some other methods to implement RESTful. I took some ideas from [https://github.com/drone/routes](https://github.com/drone/routes) at the beginning of designing routes. It uses regular expression in route rules processing, which is an excellent idea that to make up for the default Mux router function in Go. However, I have to design my own interface in order to implement RESTful and use inherited ideas in Python.
The controller is the most important part of whole MVC model, and Beego uses the interface and ideas I said above for the controller. Although I haven't decided to have to design the model part, everyone is welcome to implement data management by referencing Beedb, my another open source project. I simply adopt Go built-in template engine for the view part, but add more commonly used functions as template functions. This is how a simple web framework looks like, but I'll keep working on form processing, session handling, log recording, configuration, automated operation, etc, to build a simple but complete web framework.
- [Introduction](README.md)
- [Installation](Install.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@ -1,23 +0,0 @@
# beego案例
- 短域名服务
使用beego开发了一个类似bitly的短域名服务提供盛大内部项目使用目前一台机器32G内存、8核、centos64
redis数据库数据量在1500w多点从2012年8月份运行至今没有出现过问题
- 政府合作项目
使用beego提供系统级别服务监控继承电路板信号智能分析nginx配置提供大数据量的下载调度
- 内部监控系统
目前这个项目还在开发中主要是利用beego做两个服务一个是服务器端收集信息一个是客户端收集信息并上报给服务器端如果和服务端断开那么本地可以暂存数据防止数据丢失同时还支持类似pupput的功能支持程序自动更新(重启功能,不支持热更新)
- 日志分析系统
以前采用hadoop来分析日志的来源和省份信息发现hadoop分析这么大的数据性能不是很好延迟比较大目前采用自己的一套架构squid日志每小时分割上报每小时对日志进行分割然后进行UV、PV、省份、运营商、浏览器、数据量等的分析同时把日志进行按域名分割提供用户原始日志下载
- 下载分发系统
架构暂时不好公布我们提供文件下载的智能分发但是从文件上传到下发到每一台服务器以前采用BT的方式性能不是很好目前采用新的架构性能提升十几倍
- 基于微信的提醒助手
开源在github上
- 视频直播调度器

View File

@ -1,30 +0,0 @@
# 安装入门
beego虽然是一个简单的框架但是其中用到了很多第三方的包所以在你安装beego的过程中Go会自动安装其他关联的包。
- 当然第一步你需要安装Go如何安装Go请参考我的书[第一章](https://github.com/astaxie/build-web-application-with-golang/blob/master/ebook/01.1.md)
- 安装beego
go get github.com/astaxie/beego
- 安装bee工具,这个工具可以用来快速的建立beego的应用
go get github.com/astaxie/bee
这样就完成了beego的安装你就可以开始开发了,可以通过bee工具来创建beego项目
![](images/bee.png)
>beego依赖的第三方包有如下
> - session模块github.com/astaxie/beego/session
> - session模块中支持redis引擎github.com/garyburd/redigo/redis
> - session模块中支持mysql引擎github.com/go-sql-driver/mysql
> - 模板函数中支持markdown转化github.com/russross/blackfriday
- [beego介绍](README.md)
- [快速入门](Quickstart.md)

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +0,0 @@
# beego介绍
beego是一个类似tornado的Go应用框架采用了RESTFul的方式来实现应用框架是一个超轻量级的框架主要有如下的特点
- 支持MVC的方式用户只需要关注逻辑实现对应method的方法即可
- 支持websocket通过自定义Handler实现集成sockjs等方式实现
- 支持自定义路由支持各种方式的路由正则、语意均支持类似sinatra
- session集成支持memory、file、redis、mysql等存储
- 表单处理自动化解析,用户可以很方便的获取数据
- 日志分级系统,用户可以很方便的调试和应用日志记录
- 自定义配置文件支持ini格式的文本配置可以方便的在系统中调参数
- 采用了Go内置的模板集成实现了很多Web开发中常用的函数
执行过程如下所示:
![](images/beego.png)
# beego简单例子
package main
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Ctx.WriteString("hello world")
}
func main() {
beego.Router("/", &MainController{})
beego.Run()
}
# beego 指南
* [为什么设计beego](Why.md)
* [安装入门](Install.md)
* [快速入门](Quickstart.md)
* [一步一步开发应用](Tutorial.md)
* [beego案例](Application.md)
# API接口
API对于我们平时开发应用非常有用用于查询一些开发的函数godoc做的非常好了
[godoc](http://godoc.org/github.com/astaxie/beego)

View File

@ -1,34 +0,0 @@
## supervisord安装
1. setuptools安装
wget http://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg
sh setuptools-0.6c11-py2.7.egg
easy_install supervisor
echo_supervisord_conf >/etc/supervisord.conf
mkdir /etc/supervisord.conf.d
2. 修改配置/etc/supervisord.conf
[include]
files = /etc/supervisord.conf.d/*.conf
3. 新建管理的应用
cd /etc/supervisord.conf.d
vim beepkg.conf
配置文件:
[program:beepkg]
directory = /opt/app/beepkg
command = /opt/app/beepkg/beepkg
autostart = true
startsecs = 5
user = root
redirect_stderr = true
stdout_logfile = /var/log/supervisord/beepkg.log

View File

@ -1,13 +0,0 @@
# 一步一步跟我写博客
## 创建项目
## 数据库结构设计
## 控制器设计
## 模板设计
## 用户登陆退出
## 数据库操作

View File

@ -1,20 +0,0 @@
# 为什么设计beego和设计的思路
很多人会问为什么有那么多框架了还要去实现一个框架呢是不是大家都有自己实现框架的情节我可以肯定的说不是我说一下为什么设计beego的初衷
还记得当初写书的时候我纯粹只是想把自己在学习Go语言中的一些体会写出来由于我以前主要从事PHP和python的Web开发所以想写一本Go如何来做Web实战的经验刚开始的时候书的目录里面根本就没有框架实现这些章节是写到后来发现其实对于Web开发者来说一个微型的框架是非常有利于大家学习一个语言和快速进行应用开发的。
我以前经常用PHP的CI框架和python的tornado框架这些框架都是非常轻量级的轻量级就有利于我们
- 第一节约我开发中一些常见问题的处理,用户只需要关注逻辑层面的东西
- 第二轻量级以至于他们的代码也是非常清晰的,我们可以通过阅读他们的源码来学习和体会这门语言的一些细节
- 第三对于项目开发者来说可以基于这些框架进行改造以适应自己的项目,从而实现二次框架的创造
所以基于上面这些的考虑我就想实现一个类似这些语言的轻量级框架所以我就在书的最后设计了两个章节来介绍和实现beego框架这就是当初写beego框架的初衷。
有了这个初衷之后我就开始设计beego的执行逻辑由于Go语言和python的思路比较接近所以我就参考了tornado的思路来设计beego你可以看到beego的RESTful处理完全和tornado的处理是一模一样的通过controller层的Get、Post等方法来实现RESTFul。刚开始的时候路由参考的是[https://github.com/drone/routes](https://github.com/drone/routes)这个的正则处理我觉得非常好弥补了Go语言默认Mux中的路由功能但是由于要采用RESTFul方式所以我自己设计了一个接口实现python中的继承思想。
整个的MVC逻辑中C是最重要的部分这一块采用了我上面说的接口方式M模块目前我还没想好怎么做但是大家可以参考我的另一个开源项目beedb来实现数据的管理V这一块目前采用了Go语言自带的模板引擎但是实现了很多方便的模板函数。这样一个简易的框架就完成了然后我就不断的完善周边的功能包括表单处理、session处理、日志处理、配置处理、自动化运行等功能。
- [beego介绍](README.md)
- [安装入门](Install.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@ -0,0 +1,5 @@
appname = beeapi
httpport = 8080
runmode = dev
autorender = false
copyrequestbody = true

View File

@ -0,0 +1,63 @@
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors astaxie
package controllers
import (
"encoding/json"
"github.com/astaxie/beego"
"github.com/astaxie/beego/example/beeapi/models"
)
type ObjectController struct {
beego.Controller
}
func (this *ObjectController) Post() {
var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
objectid := models.AddOne(ob)
this.Data["json"] = map[string]string{"ObjectId": objectid}
this.ServeJson()
}
func (this *ObjectController) Get() {
objectId := this.Ctx.Input.Params[":objectId"]
if objectId != "" {
ob, err := models.GetOne(objectId)
if err != nil {
this.Data["json"] = err
} else {
this.Data["json"] = ob
}
} else {
obs := models.GetAll()
this.Data["json"] = obs
}
this.ServeJson()
}
func (this *ObjectController) Put() {
objectId := this.Ctx.Input.Params[":objectId"]
var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
err := models.Update(objectId, ob.Score)
if err != nil {
this.Data["json"] = err
} else {
this.Data["json"] = "update success!"
}
this.ServeJson()
}
func (this *ObjectController) Delete() {
objectId := this.Ctx.Input.Params[":objectId"]
models.Delete(objectId)
this.Data["json"] = "delete success!"
this.ServeJson()
}

30
example/beeapi/main.go Normal file
View File

@ -0,0 +1,30 @@
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors astaxie
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/example/beeapi/controllers"
)
// Objects
// URL HTTP Verb Functionality
// /object POST Creating Objects
// /object/<objectId> GET Retrieving Objects
// /object/<objectId> PUT Updating Objects
// /object GET Queries
// /object/<objectId> DELETE Deleting Objects
func main() {
beego.RESTRouter("/object", &controllers.ObjectController{})
beego.Run()
}

View File

@ -0,0 +1,58 @@
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors astaxie
package models
import (
"errors"
"strconv"
"time"
)
var (
Objects map[string]*Object
)
type Object struct {
ObjectId string
Score int64
PlayerName string
}
func init() {
Objects = make(map[string]*Object)
Objects["hjkhsbnmn123"] = &Object{"hjkhsbnmn123", 100, "astaxie"}
Objects["mjjkxsxsaa23"] = &Object{"mjjkxsxsaa23", 101, "someone"}
}
func AddOne(object Object) (ObjectId string) {
object.ObjectId = "astaxie" + strconv.FormatInt(time.Now().UnixNano(), 10)
Objects[object.ObjectId] = &object
return object.ObjectId
}
func GetOne(ObjectId string) (object *Object, err error) {
if v, ok := Objects[ObjectId]; ok {
return v, nil
}
return nil, errors.New("ObjectId Not Exist")
}
func GetAll() map[string]*Object {
return Objects
}
func Update(ObjectId string, Score int64) (err error) {
if v, ok := Objects[ObjectId]; ok {
v.Score = Score
return nil
}
return errors.New("ObjectId Not Exist")
}
func Delete(ObjectId string) {
delete(Objects, ObjectId)
}

View File

@ -1,42 +0,0 @@
package main
import (
"fmt"
"github.com/astaxie/beego"
"github.com/fzzy/sockjs-go/sockjs"
"strings"
)
var users *sockjs.SessionPool = sockjs.NewSessionPool()
func chatHandler(s sockjs.Session) {
users.Add(s)
defer users.Remove(s)
for {
m := s.Receive()
if m == nil {
break
}
fullAddr := s.Info().RemoteAddr
addr := fullAddr[:strings.LastIndex(fullAddr, ":")]
m = []byte(fmt.Sprintf("%s: %s", addr, m))
users.Broadcast(m)
}
}
type MainController struct {
beego.Controller
}
func (m *MainController) Get() {
m.TplNames = "index.html"
}
func main() {
conf := sockjs.NewConfig()
sockjshandler := sockjs.NewHandler("/chat", chatHandler, conf)
beego.Router("/", &MainController{})
beego.RouterHandler("/chat/:info(.*)", sockjshandler)
beego.Run()
}

View File

@ -0,0 +1,3 @@
appname = chat
httpport = 8080
runmode = dev

View File

@ -0,0 +1,20 @@
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors Unknwon
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Data["host"] = this.Ctx.Request.Host
this.TplNames = "index.tpl"
}

View File

@ -0,0 +1,181 @@
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors Unknwon
package controllers
import (
"io/ioutil"
"math/rand"
"net/http"
"time"
"github.com/astaxie/beego"
"github.com/gorilla/websocket"
)
const (
// Time allowed to write a message to the client.
writeWait = 10 * time.Second
// Time allowed to read the next message from the client.
readWait = 60 * time.Second
// Send pings to client with this period. Must be less than readWait.
pingPeriod = (readWait * 9) / 10
// Maximum message size allowed from client.
maxMessageSize = 512
)
func init() {
rand.Seed(time.Now().UTC().UnixNano())
go h.run()
}
// connection is an middleman between the websocket connection and the hub.
type connection struct {
username string
// The websocket connection.
ws *websocket.Conn
// Buffered channel of outbound messages.
send chan []byte
}
// readPump pumps messages from the websocket connection to the hub.
func (c *connection) readPump() {
defer func() {
h.unregister <- c
c.ws.Close()
}()
c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(readWait))
for {
op, r, err := c.ws.NextReader()
if err != nil {
break
}
switch op {
case websocket.PongMessage:
c.ws.SetReadDeadline(time.Now().Add(readWait))
case websocket.TextMessage:
message, err := ioutil.ReadAll(r)
if err != nil {
break
}
h.broadcast <- []byte(c.username + "_" + time.Now().Format("15:04:05") + ":" + string(message))
}
}
}
// write writes a message with the given opCode and payload.
func (c *connection) write(opCode int, payload []byte) error {
c.ws.SetWriteDeadline(time.Now().Add(writeWait))
return c.ws.WriteMessage(opCode, payload)
}
// writePump pumps messages from the hub to the websocket connection.
func (c *connection) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.ws.Close()
}()
for {
select {
case message, ok := <-c.send:
if !ok {
c.write(websocket.CloseMessage, []byte{})
return
}
if err := c.write(websocket.TextMessage, message); err != nil {
return
}
case <-ticker.C:
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}
}
type hub struct {
// Registered connections.
connections map[*connection]bool
// Inbound messages from the connections.
broadcast chan []byte
// Register requests from the connections.
register chan *connection
// Unregister requests from connections.
unregister chan *connection
}
var h = &hub{
broadcast: make(chan []byte, maxMessageSize),
register: make(chan *connection, 1),
unregister: make(chan *connection, 1),
connections: make(map[*connection]bool),
}
func (h *hub) run() {
for {
select {
case c := <-h.register:
h.connections[c] = true
case c := <-h.unregister:
delete(h.connections, c)
close(c.send)
case m := <-h.broadcast:
for c := range h.connections {
select {
case c.send <- m:
default:
close(c.send)
delete(h.connections, c)
}
}
}
}
}
type WSController struct {
beego.Controller
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func (this *WSController) Get() {
ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil)
if _, ok := err.(websocket.HandshakeError); ok {
http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400)
return
} else if err != nil {
return
}
c := &connection{send: make(chan []byte, 256), ws: ws, username: randomString(10)}
h.register <- c
go c.writePump()
c.readPump()
}
func randomString(l int) string {
bytes := make([]byte, l)
for i := 0; i < l; i++ {
bytes[i] = byte(randInt(65, 90))
}
return string(bytes)
}
func randInt(min int, max int) int {
return min + rand.Intn(max-min)
}

17
example/chat/main.go Normal file
View File

@ -0,0 +1,17 @@
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors Unknwon
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/example/chat/controllers"
)
func main() {
beego.Router("/", &controllers.MainController{})
beego.Router("/ws", &controllers.WSController{})
beego.Run()
}

View File

@ -1,81 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="http://cdn.sockjs.org/sockjs-0.3.4.min.js"></script>
<script>
$(function() {
var conn = null;
function log(msg) {
var control = $('#log');
control.html(control.html() + msg + '<br/>');
control.scrollTop(control.scrollTop() + 1000);
}
function disconnect() {
if (conn != null) {
log('Disconnecting...');
conn.close();
conn = null;
updateUi();
}
}
function updateUi() {
if (conn == null || conn.readyState != SockJS.OPEN) {
$('#status').text('disconnected');
$('#connect').text('Connect');
} else {
$('#status').text('connected (' + conn.protocol + ')');
$('#connect').text('Disconnect');
}
}
$('form').submit(function() {
var text = $('#message').val();
conn.send(text);
$('#message').val('').focus();
return false;
});
conn = new SockJS('http://' + window.location.host + '/chat');
log('Connecting...');
conn.onopen = function() {
log('Connected.');
updateUi();
};
conn.onmessage = function(e) {
log(e.data);
};
conn.onclose = function() {
log('Disconnected.');
conn = null;
updateUi();
};
$('#message').val('').focus();
});
</script>
<title>Sockjs-go chat</title>
</head>
<body>
<h1>Sockjs-go chat</h1>
<div>
Status: <span id="status">disconnected</span>
</div>
<div id="log" style="width: 60em; height: 20em; overflow:auto; border: 1px solid black">
</div>
<form id="chatform">
<label for="message">Message:</label>
<input id="message" type="text" />
<input type="submit" value="Send" />
</form>
</body>
</html>

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Chat Example</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
var conn;
var msg = $("#msg");
var log = $("#log");
function appendLog(msg) {
var d = log[0]
var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight;
msg.appendTo(log)
if (doScroll) {
d.scrollTop = d.scrollHeight - d.clientHeight;
}
}
$("#form").submit(function() {
if (!conn) {
return false;
}
if (!msg.val()) {
return false;
}
conn.send(msg.val());
msg.val("");
return false
});
if (window["WebSocket"]) {
conn = new WebSocket("ws://{{.host}}/ws");
conn.onclose = function(evt) {
appendLog($("<div><b>Connection closed.</b></div>"))
}
conn.onmessage = function(evt) {
appendLog($("<div/>").text(evt.data))
}
} else {
appendLog($("<div><b>Your browser does not support WebSockets.</b></div>"))
}
});
</script>
<style type="text/css">
html {
overflow: hidden;
}
body {
overflow: hidden;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
background: gray;
}
#log {
background: white;
margin: 0;
padding: 0.5em 0.5em 0.5em 0.5em;
position: absolute;
top: 0.5em;
left: 0.5em;
right: 0.5em;
bottom: 3em;
overflow: auto;
}
#form {
padding: 0 0.5em 0 0.5em;
margin: 0;
position: absolute;
bottom: 1em;
left: 0px;
width: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="log"></div>
<form id="form">
<input type="submit" value="Send" />
<input type="text" id="msg" size="64"/>
</form>
</body>
</html>

37
filter.go Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
// FilterRouter defines filter operation before controller handler execution.
// it can match patterned url and do filter function when action arrives.
type FilterRouter struct {
filterFunc FilterFunc
tree *Tree
pattern string
}
// ValidRouter check current request is valid for this filter.
// if matched, returns parsed params in this request by defined filter router pattern.
func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) {
isok, params := f.tree.Match(router)
if isok == nil {
return false, nil
}
if isok, ok := isok.(bool); ok {
return isok, params
} else {
return false, nil
}
}

68
filter_test.go Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/astaxie/beego/context"
)
var FilterUser = func(ctx *context.Context) {
ctx.Output.Body([]byte("i am " + ctx.Input.Params[":last"] + ctx.Input.Params[":first"]))
}
func TestFilter(t *testing.T) {
r, _ := http.NewRequest("GET", "/person/asta/Xie", nil)
w := httptest.NewRecorder()
handler := NewControllerRegister()
handler.InsertFilter("/person/:last/:first", BeforeRouter, FilterUser)
handler.Add("/person/:last/:first", &TestController{})
handler.ServeHTTP(w, r)
if w.Body.String() != "i am astaXie" {
t.Errorf("user define func can't run")
}
}
var FilterAdminUser = func(ctx *context.Context) {
ctx.Output.Body([]byte("i am admin"))
}
// Filter pattern /admin/:all
// all url like /admin/ /admin/xie will all get filter
func TestPatternTwo(t *testing.T) {
r, _ := http.NewRequest("GET", "/admin/", nil)
w := httptest.NewRecorder()
handler := NewControllerRegister()
handler.InsertFilter("/admin/?:all", BeforeRouter, FilterAdminUser)
handler.ServeHTTP(w, r)
if w.Body.String() != "i am admin" {
t.Errorf("filter /admin/ can't run")
}
}
func TestPatternThree(t *testing.T) {
r, _ := http.NewRequest("GET", "/admin/astaxie", nil)
w := httptest.NewRecorder()
handler := NewControllerRegister()
handler.InsertFilter("/admin/:all", BeforeRouter, FilterAdminUser)
handler.ServeHTTP(w, r)
if w.Body.String() != "i am admin" {
t.Errorf("filter /admin/astaxie can't run")
}
}

92
flash.go Normal file
View File

@ -0,0 +1,92 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"fmt"
"net/url"
"strings"
)
// FlashData is a tools to maintain data when using across request.
type FlashData struct {
Data map[string]string
}
// NewFlash return a new empty FlashData struct.
func NewFlash() *FlashData {
return &FlashData{
Data: make(map[string]string),
}
}
// Notice writes notice message to flash.
func (fd *FlashData) Notice(msg string, args ...interface{}) {
if len(args) == 0 {
fd.Data["notice"] = msg
} else {
fd.Data["notice"] = fmt.Sprintf(msg, args...)
}
}
// Warning writes warning message to flash.
func (fd *FlashData) Warning(msg string, args ...interface{}) {
if len(args) == 0 {
fd.Data["warning"] = msg
} else {
fd.Data["warning"] = fmt.Sprintf(msg, args...)
}
}
// Error writes error message to flash.
func (fd *FlashData) Error(msg string, args ...interface{}) {
if len(args) == 0 {
fd.Data["error"] = msg
} else {
fd.Data["error"] = fmt.Sprintf(msg, args...)
}
}
// Store does the saving operation of flash data.
// the data are encoded and saved in cookie.
func (fd *FlashData) Store(c *Controller) {
c.Data["flash"] = fd.Data
var flashValue string
for key, value := range fd.Data {
flashValue += "\x00" + key + "\x23" + FlashSeperator + "\x23" + value + "\x00"
}
c.Ctx.SetCookie(FlashName, url.QueryEscape(flashValue), 0, "/")
}
// ReadFromRequest parsed flash data from encoded values in cookie.
func ReadFromRequest(c *Controller) *FlashData {
flash := NewFlash()
if cookie, err := c.Ctx.Request.Cookie(FlashName); err == nil {
v, _ := url.QueryUnescape(cookie.Value)
vals := strings.Split(v, "\x00")
for _, v := range vals {
if len(v) > 0 {
kv := strings.Split(v, "\x23"+FlashSeperator+"\x23")
if len(kv) == 2 {
flash.Data[kv[0]] = kv[1]
}
}
}
//read one time then delete it
c.Ctx.SetCookie(FlashName, "", -1, "/")
}
c.Data["flash"] = flash.Data
return flash
}

54
flash_test.go Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
type TestFlashController struct {
Controller
}
func (this *TestFlashController) TestWriteFlash() {
flash := NewFlash()
flash.Notice("TestFlashString")
flash.Store(&this.Controller)
// we choose to serve json because we don't want to load a template html file
this.ServeJson(true)
}
func TestFlashHeader(t *testing.T) {
// create fake GET request
r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
// setup the handler
handler := NewControllerRegister()
handler.Add("/", &TestFlashController{}, "get:TestWriteFlash")
handler.ServeHTTP(w, r)
// get the Set-Cookie value
sc := w.Header().Get("Set-Cookie")
// match for the expected header
res := strings.Contains(sc, "BEEGO_FLASH=%00notice%23BEEGOFLASH%23TestFlashString%00")
// validate the assertion
if res != true {
t.Errorf("TestFlashHeader() unable to validate flash message")
}
}

97
httplib/README.md Normal file
View File

@ -0,0 +1,97 @@
# httplib
httplib is an libs help you to curl remote url.
# How to use?
## GET
you can use Get to crawl data.
import "github.com/astaxie/beego/httplib"
str, err := httplib.Get("http://beego.me/").String()
if err != nil {
// error
}
fmt.Println(str)
## POST
POST data to remote url
req := httplib.Post("http://beego.me/")
req.Param("username","astaxie")
req.Param("password","123456")
str, err := req.String()
if err != nil {
// error
}
fmt.Println(str)
## Set timeout
The default timeout is `60` seconds, function prototype:
SetTimeout(connectTimeout, readWriteTimeout time.Duration)
Exmaple:
// GET
httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
// POST
httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
## Debug
If you want to debug the request info, set the debug on
httplib.Get("http://beego.me/").Debug(true)
## Set HTTP Basic Auth
str, err := Get("http://beego.me/").SetBasicAuth("user", "passwd").String()
if err != nil {
// error
}
fmt.Println(str)
## Set HTTPS
If request url is https, You can set the client support TSL:
httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config
## Set HTTP Version
some servers need to specify the protocol version of HTTP
httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1")
## Set Cookie
some http request need setcookie. So set it like this:
cookie := &http.Cookie{}
cookie.Name = "username"
cookie.Value = "astaxie"
httplib.Get("http://beego.me/").SetCookie(cookie)
## Upload file
httplib support mutil file upload, use `req.PostFile()`
req := httplib.Post("http://beego.me/")
req.Param("username","astaxie")
req.PostFile("uploadfile1", "httplib.pdf")
str, err := req.String()
if err != nil {
// error
}
fmt.Println(str)
See godoc for further documentation and examples.
* [godoc.org/github.com/astaxie/beego/httplib](https://godoc.org/github.com/astaxie/beego/httplib)

469
httplib/httplib.go Normal file
View File

@ -0,0 +1,469 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Usage:
//
// import "github.com/astaxie/beego/httplib"
//
// b := httplib.Post("http://beego.me/")
// b.Param("username","astaxie")
// b.Param("password","123456")
// b.PostFile("uploadfile1", "httplib.pdf")
// b.PostFile("uploadfile2", "httplib.txt")
// str, err := b.String()
// if err != nil {
// t.Fatal(err)
// }
// fmt.Println(str)
//
// more docs http://beego.me/docs/module/httplib.md
package httplib
import (
"bytes"
"crypto/tls"
"encoding/json"
"encoding/xml"
"io"
"io/ioutil"
"mime/multipart"
"net"
"net/http"
"net/http/cookiejar"
"net/http/httputil"
"net/url"
"os"
"strings"
"sync"
"time"
)
var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false}
var defaultCookieJar http.CookieJar
var settingMutex sync.Mutex
// createDefaultCookie creates a global cookiejar to store cookies.
func createDefaultCookie() {
settingMutex.Lock()
defer settingMutex.Unlock()
defaultCookieJar, _ = cookiejar.New(nil)
}
// Overwrite default settings
func SetDefaultSetting(setting BeegoHttpSettings) {
settingMutex.Lock()
defer settingMutex.Unlock()
defaultSetting = setting
if defaultSetting.ConnectTimeout == 0 {
defaultSetting.ConnectTimeout = 60 * time.Second
}
if defaultSetting.ReadWriteTimeout == 0 {
defaultSetting.ReadWriteTimeout = 60 * time.Second
}
}
// return *BeegoHttpRequest with specific method
func newBeegoRequest(url, method string) *BeegoHttpRequest {
var resp http.Response
req := http.Request{
Method: method,
Header: make(http.Header),
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil}
}
// Get returns *BeegoHttpRequest with GET method.
func Get(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "GET")
}
// Post returns *BeegoHttpRequest with POST method.
func Post(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "POST")
}
// Put returns *BeegoHttpRequest with PUT method.
func Put(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "PUT")
}
// Delete returns *BeegoHttpRequest DELETE method.
func Delete(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "DELETE")
}
// Head returns *BeegoHttpRequest with HEAD method.
func Head(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "HEAD")
}
// BeegoHttpSettings
type BeegoHttpSettings struct {
ShowDebug bool
UserAgent string
ConnectTimeout time.Duration
ReadWriteTimeout time.Duration
TlsClientConfig *tls.Config
Proxy func(*http.Request) (*url.URL, error)
Transport http.RoundTripper
EnableCookie bool
}
// BeegoHttpRequest provides more useful methods for requesting one url than http.Request.
type BeegoHttpRequest struct {
url string
req *http.Request
params map[string]string
files map[string]string
setting BeegoHttpSettings
resp *http.Response
body []byte
}
// Change request settings
func (b *BeegoHttpRequest) Setting(setting BeegoHttpSettings) *BeegoHttpRequest {
b.setting = setting
return b
}
// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
func (b *BeegoHttpRequest) SetBasicAuth(username, password string) *BeegoHttpRequest {
b.req.SetBasicAuth(username, password)
return b
}
// SetEnableCookie sets enable/disable cookiejar
func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest {
b.setting.EnableCookie = enable
return b
}
// SetUserAgent sets User-Agent header field
func (b *BeegoHttpRequest) SetUserAgent(useragent string) *BeegoHttpRequest {
b.setting.UserAgent = useragent
return b
}
// Debug sets show debug or not when executing request.
func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest {
b.setting.ShowDebug = isdebug
return b
}
// SetTimeout sets connect time out and read-write time out for BeegoRequest.
func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest {
b.setting.ConnectTimeout = connectTimeout
b.setting.ReadWriteTimeout = readWriteTimeout
return b
}
// SetTLSClientConfig sets tls connection configurations if visiting https url.
func (b *BeegoHttpRequest) SetTLSClientConfig(config *tls.Config) *BeegoHttpRequest {
b.setting.TlsClientConfig = config
return b
}
// Header add header item string in request.
func (b *BeegoHttpRequest) Header(key, value string) *BeegoHttpRequest {
b.req.Header.Set(key, value)
return b
}
// Set the protocol version for incoming requests.
// Client requests always use HTTP/1.1.
func (b *BeegoHttpRequest) SetProtocolVersion(vers string) *BeegoHttpRequest {
if len(vers) == 0 {
vers = "HTTP/1.1"
}
major, minor, ok := http.ParseHTTPVersion(vers)
if ok {
b.req.Proto = vers
b.req.ProtoMajor = major
b.req.ProtoMinor = minor
}
return b
}
// SetCookie add cookie into request.
func (b *BeegoHttpRequest) SetCookie(cookie *http.Cookie) *BeegoHttpRequest {
b.req.Header.Add("Cookie", cookie.String())
return b
}
// Set transport to
func (b *BeegoHttpRequest) SetTransport(transport http.RoundTripper) *BeegoHttpRequest {
b.setting.Transport = transport
return b
}
// Set http proxy
// example:
//
// func(req *http.Request) (*url.URL, error) {
// u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
// return u, nil
// }
func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHttpRequest {
b.setting.Proxy = proxy
return b
}
// Param adds query param in to request.
// params build query string as ?key1=value1&key2=value2...
func (b *BeegoHttpRequest) Param(key, value string) *BeegoHttpRequest {
b.params[key] = value
return b
}
func (b *BeegoHttpRequest) PostFile(formname, filename string) *BeegoHttpRequest {
b.files[formname] = filename
return b
}
// Body adds request raw body.
// it supports string and []byte.
func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest {
switch t := data.(type) {
case string:
bf := bytes.NewBufferString(t)
b.req.Body = ioutil.NopCloser(bf)
b.req.ContentLength = int64(len(t))
case []byte:
bf := bytes.NewBuffer(t)
b.req.Body = ioutil.NopCloser(bf)
b.req.ContentLength = int64(len(t))
}
return b
}
func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
if b.resp.StatusCode != 0 {
return b.resp, nil
}
var paramBody string
if len(b.params) > 0 {
var buf bytes.Buffer
for k, v := range b.params {
buf.WriteString(url.QueryEscape(k))
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(v))
buf.WriteByte('&')
}
paramBody = buf.String()
paramBody = paramBody[0 : len(paramBody)-1]
}
if b.req.Method == "GET" && len(paramBody) > 0 {
if strings.Index(b.url, "?") != -1 {
b.url += "&" + paramBody
} else {
b.url = b.url + "?" + paramBody
}
} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
if len(b.files) > 0 {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
return nil, err
}
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
//iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
return nil, err
}
}
for k, v := range b.params {
bodyWriter.WriteField(k, v)
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
b.Header("Content-Type", contentType)
b.req.Body = ioutil.NopCloser(bodyBuf)
b.req.ContentLength = int64(bodyBuf.Len())
} else {
b.Header("Content-Type", "application/x-www-form-urlencoded")
b.Body(paramBody)
}
}
url, err := url.Parse(b.url)
if err != nil {
return nil, err
}
b.req.URL = url
trans := b.setting.Transport
if trans == nil {
// create default transport
trans = &http.Transport{
TLSClientConfig: b.setting.TlsClientConfig,
Proxy: b.setting.Proxy,
Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
}
} else {
// if b.transport is *http.Transport then set the settings.
if t, ok := trans.(*http.Transport); ok {
if t.TLSClientConfig == nil {
t.TLSClientConfig = b.setting.TlsClientConfig
}
if t.Proxy == nil {
t.Proxy = b.setting.Proxy
}
if t.Dial == nil {
t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
}
}
}
var jar http.CookieJar
if b.setting.EnableCookie {
if defaultCookieJar == nil {
createDefaultCookie()
}
jar = defaultCookieJar
} else {
jar = nil
}
client := &http.Client{
Transport: trans,
Jar: jar,
}
if b.setting.UserAgent != "" {
b.req.Header.Set("User-Agent", b.setting.UserAgent)
}
if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, true)
if err != nil {
println(err.Error())
}
println(string(dump))
}
resp, err := client.Do(b.req)
if err != nil {
return nil, err
}
b.resp = resp
return resp, nil
}
// String returns the body string in response.
// it calls Response inner.
func (b *BeegoHttpRequest) String() (string, error) {
data, err := b.Bytes()
if err != nil {
return "", err
}
return string(data), nil
}
// Bytes returns the body []byte in response.
// it calls Response inner.
func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
if b.body != nil {
return b.body, nil
}
resp, err := b.getResponse()
if err != nil {
return nil, err
}
if resp.Body == nil {
return nil, nil
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
b.body = data
return data, nil
}
// ToFile saves the body data in response to one file.
// it calls Response inner.
func (b *BeegoHttpRequest) ToFile(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
resp, err := b.getResponse()
if err != nil {
return err
}
if resp.Body == nil {
return nil
}
defer resp.Body.Close()
_, err = io.Copy(f, resp.Body)
return err
}
// ToJson returns the map that marshals from the body bytes as json in response .
// it calls Response inner.
func (b *BeegoHttpRequest) ToJson(v interface{}) error {
data, err := b.Bytes()
if err != nil {
return err
}
err = json.Unmarshal(data, v)
return err
}
// ToXml returns the map that marshals from the body bytes as xml in response .
// it calls Response inner.
func (b *BeegoHttpRequest) ToXml(v interface{}) error {
data, err := b.Bytes()
if err != nil {
return err
}
err = xml.Unmarshal(data, v)
return err
}
// Response executes request client gets response mannually.
func (b *BeegoHttpRequest) Response() (*http.Response, error) {
return b.getResponse()
}
// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
return func(netw, addr string) (net.Conn, error) {
conn, err := net.DialTimeout(netw, addr, cTimeout)
if err != nil {
return nil, err
}
conn.SetDeadline(time.Now().Add(rwTimeout))
return conn, nil
}
}

205
httplib/httplib_test.go Normal file
View File

@ -0,0 +1,205 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package httplib
import (
"io/ioutil"
"os"
"strings"
"testing"
)
func TestResponse(t *testing.T) {
req := Get("http://httpbin.org/get")
resp, err := req.Response()
if err != nil {
t.Fatal(err)
}
t.Log(resp)
}
func TestGet(t *testing.T) {
req := Get("http://httpbin.org/get")
b, err := req.Bytes()
if err != nil {
t.Fatal(err)
}
t.Log(b)
s, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(s)
if string(b) != s {
t.Fatal("request data not match")
}
}
func TestSimplePost(t *testing.T) {
v := "smallfish"
req := Post("http://httpbin.org/post")
req.Param("username", v)
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in post")
}
}
func TestPostFile(t *testing.T) {
v := "smallfish"
req := Post("http://httpbin.org/post")
req.Param("username", v)
req.PostFile("uploadfile", "httplib_test.go")
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in post")
}
}
func TestSimplePut(t *testing.T) {
str, err := Put("http://httpbin.org/put").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}
func TestSimpleDelete(t *testing.T) {
str, err := Delete("http://httpbin.org/delete").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}
func TestWithCookie(t *testing.T) {
v := "smallfish"
str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in cookie")
}
}
func TestWithBasicAuth(t *testing.T) {
str, err := Get("http://httpbin.org/basic-auth/user/passwd").SetBasicAuth("user", "passwd").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, "authenticated")
if n == -1 {
t.Fatal("authenticated not found in response")
}
}
func TestWithUserAgent(t *testing.T) {
v := "beego"
str, err := Get("http://httpbin.org/headers").SetUserAgent(v).String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in user-agent")
}
}
func TestWithSetting(t *testing.T) {
v := "beego"
var setting BeegoHttpSettings
setting.EnableCookie = true
setting.UserAgent = v
setting.Transport = nil
SetDefaultSetting(setting)
str, err := Get("http://httpbin.org/get").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in user-agent")
}
}
func TestToJson(t *testing.T) {
req := Get("http://httpbin.org/ip")
resp, err := req.Response()
if err != nil {
t.Fatal(err)
}
t.Log(resp)
// httpbin will return http remote addr
type Ip struct {
Origin string `json:"origin"`
}
var ip Ip
err = req.ToJson(&ip)
if err != nil {
t.Fatal(err)
}
t.Log(ip.Origin)
if n := strings.Count(ip.Origin, "."); n != 3 {
t.Fatal("response is not valid ip")
}
}
func TestToFile(t *testing.T) {
f := "beego_testfile"
req := Get("http://httpbin.org/ip")
err := req.ToFile(f)
if err != nil {
t.Fatal(err)
}
defer os.Remove(f)
b, err := ioutil.ReadFile(f)
if n := strings.Index(string(b), "origin"); n == -1 {
t.Fatal(err)
}
}

198
log.go
View File

@ -1,85 +1,113 @@
package beego
import (
"log"
"os"
)
//--------------------
// LOG LEVEL
//--------------------
// Log levels to control the logging output.
const (
LevelTrace = iota
LevelDebug
LevelInfo
LevelWarning
LevelError
LevelCritical
)
// logLevel controls the global log level used by the logger.
var level = LevelTrace
// LogLevel returns the global log level and can be used in
// own implementations of the logger interface.
func Level() int {
return level
}
// SetLogLevel sets the global log level used by the simple
// logger.
func SetLevel(l int) {
level = l
}
// logger references the used application logger.
var BeeLogger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
// SetLogger sets a new logger.
func SetLogger(l *log.Logger) {
BeeLogger = l
}
// Trace logs a message at trace level.
func Trace(v ...interface{}) {
if level <= LevelTrace {
BeeLogger.Printf("[T] %v\n", v)
}
}
// Debug logs a message at debug level.
func Debug(v ...interface{}) {
if level <= LevelDebug {
BeeLogger.Printf("[D] %v\n", v)
}
}
// Info logs a message at info level.
func Info(v ...interface{}) {
if level <= LevelInfo {
BeeLogger.Printf("[I] %v\n", v)
}
}
// Warning logs a message at warning level.
func Warn(v ...interface{}) {
if level <= LevelWarning {
BeeLogger.Printf("[W] %v\n", v)
}
}
// Error logs a message at error level.
func Error(v ...interface{}) {
if level <= LevelError {
BeeLogger.Printf("[E] %v\n", v)
}
}
// Critical logs a message at critical level.
func Critical(v ...interface{}) {
if level <= LevelCritical {
BeeLogger.Printf("[C] %v\n", v)
}
}
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"strings"
"github.com/astaxie/beego/logs"
)
// Log levels to control the logging output.
const (
LevelEmergency = iota
LevelAlert
LevelCritical
LevelError
LevelWarning
LevelNotice
LevelInformational
LevelDebug
)
// SetLogLevel sets the global log level used by the simple
// logger.
func SetLevel(l int) {
BeeLogger.SetLevel(l)
}
func SetLogFuncCall(b bool) {
BeeLogger.EnableFuncCallDepth(b)
BeeLogger.SetLogFuncCallDepth(3)
}
// logger references the used application logger.
var BeeLogger *logs.BeeLogger
// SetLogger sets a new logger.
func SetLogger(adaptername string, config string) error {
err := BeeLogger.SetLogger(adaptername, config)
if err != nil {
return err
}
return nil
}
func Emergency(v ...interface{}) {
BeeLogger.Emergency(generateFmtStr(len(v)), v...)
}
func Alert(v ...interface{}) {
BeeLogger.Alert(generateFmtStr(len(v)), v...)
}
// Critical logs a message at critical level.
func Critical(v ...interface{}) {
BeeLogger.Critical(generateFmtStr(len(v)), v...)
}
// Error logs a message at error level.
func Error(v ...interface{}) {
BeeLogger.Error(generateFmtStr(len(v)), v...)
}
// Warning logs a message at warning level.
func Warning(v ...interface{}) {
BeeLogger.Warning(generateFmtStr(len(v)), v...)
}
// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0.
func Warn(v ...interface{}) {
Warning(v...)
}
func Notice(v ...interface{}) {
BeeLogger.Notice(generateFmtStr(len(v)), v...)
}
// Info logs a message at info level.
func Informational(v ...interface{}) {
BeeLogger.Informational(generateFmtStr(len(v)), v...)
}
// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0.
func Info(v ...interface{}) {
Informational(v...)
}
// Debug logs a message at debug level.
func Debug(v ...interface{}) {
BeeLogger.Debug(generateFmtStr(len(v)), v...)
}
// Trace logs a message at trace level.
// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0.
func Trace(v ...interface{}) {
BeeLogger.Trace(generateFmtStr(len(v)), v...)
}
func generateFmtStr(n int) string {
return strings.Repeat("%v ", n)
}

63
logs/README.md Normal file
View File

@ -0,0 +1,63 @@
## logs
logs is a Go logs manager. It can use many logs adapters. The repo is inspired by `database/sql` .
## How to install?
go get github.com/astaxie/beego/logs
## What adapters are supported?
As of now this logs support console, file,smtp and conn.
## How to use it?
First you must import it
import (
"github.com/astaxie/beego/logs"
)
Then init a Log (example with console adapter)
log := NewLogger(10000)
log.SetLogger("console", "")
> the first params stand for how many channel
Use it like this:
log.Trace("trace")
log.Info("info")
log.Warn("warning")
log.Debug("debug")
log.Critical("critical")
## File adapter
Configure file adapter like this:
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test.log"}`)
## Conn adapter
Configure like this:
log := NewLogger(1000)
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
log.Info("info")
## Smtp adapter
Configure like this:
log := NewLogger(10000)
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
log.Critical("sendmail critical")
time.Sleep(time.Second * 30)

121
logs/conn.go Normal file
View File

@ -0,0 +1,121 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logs
import (
"encoding/json"
"io"
"log"
"net"
)
// ConnWriter implements LoggerInterface.
// it writes messages in keep-live tcp connection.
type ConnWriter struct {
lg *log.Logger
innerWriter io.WriteCloser
ReconnectOnMsg bool `json:"reconnectOnMsg"`
Reconnect bool `json:"reconnect"`
Net string `json:"net"`
Addr string `json:"addr"`
Level int `json:"level"`
}
// create new ConnWrite returning as LoggerInterface.
func NewConn() LoggerInterface {
conn := new(ConnWriter)
conn.Level = LevelTrace
return conn
}
// init connection writer with json config.
// json config only need key "level".
func (c *ConnWriter) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), c)
if err != nil {
return err
}
return nil
}
// write message in connection.
// if connection is down, try to re-connect.
func (c *ConnWriter) WriteMsg(msg string, level int) error {
if level > c.Level {
return nil
}
if c.neddedConnectOnMsg() {
err := c.connect()
if err != nil {
return err
}
}
if c.ReconnectOnMsg {
defer c.innerWriter.Close()
}
c.lg.Println(msg)
return nil
}
// implementing method. empty.
func (c *ConnWriter) Flush() {
}
// destroy connection writer and close tcp listener.
func (c *ConnWriter) Destroy() {
if c.innerWriter == nil {
return
}
c.innerWriter.Close()
}
func (c *ConnWriter) connect() error {
if c.innerWriter != nil {
c.innerWriter.Close()
c.innerWriter = nil
}
conn, err := net.Dial(c.Net, c.Addr)
if err != nil {
return err
}
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetKeepAlive(true)
}
c.innerWriter = conn
c.lg = log.New(conn, "", log.Ldate|log.Ltime)
return nil
}
func (c *ConnWriter) neddedConnectOnMsg() bool {
if c.Reconnect {
c.Reconnect = false
return true
}
if c.innerWriter == nil {
return true
}
return c.ReconnectOnMsg
}
func init() {
Register("conn", NewConn)
}

25
logs/conn_test.go Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logs
import (
"testing"
)
func TestConn(t *testing.T) {
log := NewLogger(1000)
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
log.Informational("informational")
}

97
logs/console.go Normal file
View File

@ -0,0 +1,97 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logs
import (
"encoding/json"
"log"
"os"
"runtime"
)
type Brush func(string) string
func NewBrush(color string) Brush {
pre := "\033["
reset := "\033[0m"
return func(text string) string {
return pre + color + "m" + text + reset
}
}
var colors = []Brush{
NewBrush("1;37"), // Emergency white
NewBrush("1;36"), // Alert cyan
NewBrush("1;35"), // Critical magenta
NewBrush("1;31"), // Error red
NewBrush("1;33"), // Warning yellow
NewBrush("1;32"), // Notice green
NewBrush("1;34"), // Informational blue
NewBrush("1;34"), // Debug blue
}
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
type ConsoleWriter struct {
lg *log.Logger
Level int `json:"level"`
}
// create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface {
cw := new(ConsoleWriter)
cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime)
cw.Level = LevelDebug
return cw
}
// init console logger.
// jsonconfig like '{"level":LevelTrace}'.
func (c *ConsoleWriter) Init(jsonconfig string) error {
if len(jsonconfig) == 0 {
return nil
}
err := json.Unmarshal([]byte(jsonconfig), c)
if err != nil {
return err
}
return nil
}
// write message in console.
func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
if level > c.Level {
return nil
}
if goos := runtime.GOOS; goos == "windows" {
c.lg.Println(msg)
} else {
c.lg.Println(colors[level](msg))
}
return nil
}
// implementing method. empty.
func (c *ConsoleWriter) Destroy() {
}
// implementing method. empty.
func (c *ConsoleWriter) Flush() {
}
func init() {
Register("console", NewConsole)
}

53
logs/console_test.go Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logs
import (
"testing"
)
// Try each log level in decreasing order of priority.
func testConsoleCalls(bl *BeeLogger) {
bl.Emergency("emergency")
bl.Alert("alert")
bl.Critical("critical")
bl.Error("error")
bl.Warning("warning")
bl.Notice("notice")
bl.Informational("informational")
bl.Debug("debug")
}
// Test console logging by visually comparing the lines being output with and
// without a log level specification.
func TestConsole(t *testing.T) {
log1 := NewLogger(10000)
log1.EnableFuncCallDepth(true)
log1.SetLogger("console", "")
testConsoleCalls(log1)
log2 := NewLogger(100)
log2.SetLogger("console", `{"level":3}`)
testConsoleCalls(log2)
}
func BenchmarkConsole(b *testing.B) {
log := NewLogger(10000)
log.EnableFuncCallDepth(true)
log.SetLogger("console", "")
for i := 0; i < b.N; i++ {
log.Debug("debug")
}
}

260
logs/file.go Normal file
View File

@ -0,0 +1,260 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logs
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
// FileLogWriter implements LoggerInterface.
// It writes messages by lines limit, file size limit, or time frequency.
type FileLogWriter struct {
*log.Logger
mw *MuxWriter
// The opened file
Filename string `json:"filename"`
Maxlines int `json:"maxlines"`
maxlines_curlines int
// Rotate at size
Maxsize int `json:"maxsize"`
maxsize_cursize int
// Rotate daily
Daily bool `json:"daily"`
Maxdays int64 `json:"maxdays"`
daily_opendate int
Rotate bool `json:"rotate"`
startLock sync.Mutex // Only one log can write to the file
Level int `json:"level"`
}
// an *os.File writer with locker.
type MuxWriter struct {
sync.Mutex
fd *os.File
}
// write to os.File.
func (l *MuxWriter) Write(b []byte) (int, error) {
l.Lock()
defer l.Unlock()
return l.fd.Write(b)
}
// set os.File in writer.
func (l *MuxWriter) SetFd(fd *os.File) {
if l.fd != nil {
l.fd.Close()
}
l.fd = fd
}
// create a FileLogWriter returning as LoggerInterface.
func NewFileWriter() LoggerInterface {
w := &FileLogWriter{
Filename: "",
Maxlines: 1000000,
Maxsize: 1 << 28, //256 MB
Daily: true,
Maxdays: 7,
Rotate: true,
Level: LevelTrace,
}
// use MuxWriter instead direct use os.File for lock write when rotate
w.mw = new(MuxWriter)
// set MuxWriter as Logger's io.Writer
w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime)
return w
}
// Init file logger with json config.
// jsonconfig like:
// {
// "filename":"logs/beego.log",
// "maxlines":10000,
// "maxsize":1<<30,
// "daily":true,
// "maxdays":15,
// "rotate":true
// }
func (w *FileLogWriter) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), w)
if err != nil {
return err
}
if len(w.Filename) == 0 {
return errors.New("jsonconfig must have filename")
}
err = w.startLogger()
return err
}
// start file logger. create log file and set to locker-inside file writer.
func (w *FileLogWriter) startLogger() error {
fd, err := w.createLogFile()
if err != nil {
return err
}
w.mw.SetFd(fd)
err = w.initFd()
if err != nil {
return err
}
return nil
}
func (w *FileLogWriter) docheck(size int) {
w.startLock.Lock()
defer w.startLock.Unlock()
if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
(w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
(w.Daily && time.Now().Day() != w.daily_opendate)) {
if err := w.DoRotate(); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
return
}
}
w.maxlines_curlines++
w.maxsize_cursize += size
}
// write logger message into file.
func (w *FileLogWriter) WriteMsg(msg string, level int) error {
if level > w.Level {
return nil
}
n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
w.docheck(n)
w.Logger.Println(msg)
return nil
}
func (w *FileLogWriter) createLogFile() (*os.File, error) {
// Open the log file
fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
return fd, err
}
func (w *FileLogWriter) initFd() error {
fd := w.mw.fd
finfo, err := fd.Stat()
if err != nil {
return fmt.Errorf("get stat err: %s\n", err)
}
w.maxsize_cursize = int(finfo.Size())
w.daily_opendate = time.Now().Day()
if finfo.Size() > 0 {
content, err := ioutil.ReadFile(w.Filename)
if err != nil {
return err
}
w.maxlines_curlines = len(strings.Split(string(content), "\n"))
} else {
w.maxlines_curlines = 0
}
return nil
}
// DoRotate means it need to write file in new file.
// new file name like xx.log.2013-01-01.2
func (w *FileLogWriter) DoRotate() error {
_, err := os.Lstat(w.Filename)
if err == nil { // file exists
// Find the next available number
num := 1
fname := ""
for ; err == nil && num <= 999; num++ {
fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
_, err = os.Lstat(fname)
}
// return error if the last file checked still existed
if err == nil {
return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename)
}
// block Logger's io.Writer
w.mw.Lock()
defer w.mw.Unlock()
fd := w.mw.fd
fd.Close()
// close fd before rename
// Rename the file to its newfound home
err = os.Rename(w.Filename, fname)
if err != nil {
return fmt.Errorf("Rotate: %s\n", err)
}
// re-start logger
err = w.startLogger()
if err != nil {
return fmt.Errorf("Rotate StartLogger: %s\n", err)
}
go w.deleteOldLog()
}
return nil
}
func (w *FileLogWriter) deleteOldLog() {
dir := filepath.Dir(w.Filename)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
defer func() {
if r := recover(); r != nil {
returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
fmt.Println(returnErr)
}
}()
if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
os.Remove(path)
}
}
return
})
}
// destroy file logger, close file writer.
func (w *FileLogWriter) Destroy() {
w.mw.fd.Close()
}
// flush file logger.
// there are no buffering messages in file logger in memory.
// flush file means sync file from disk.
func (w *FileLogWriter) Flush() {
w.mw.fd.Sync()
}
func init() {
Register("file", NewFileWriter)
}

133
logs/file_test.go Normal file
View File

@ -0,0 +1,133 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logs
import (
"bufio"
"fmt"
"os"
"strconv"
"testing"
"time"
)
func TestFile(t *testing.T) {
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test.log"}`)
log.Debug("debug")
log.Informational("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error")
log.Alert("alert")
log.Critical("critical")
log.Emergency("emergency")
time.Sleep(time.Second * 4)
f, err := os.Open("test.log")
if err != nil {
t.Fatal(err)
}
b := bufio.NewReader(f)
linenum := 0
for {
line, _, err := b.ReadLine()
if err != nil {
break
}
if len(line) > 0 {
linenum++
}
}
var expected = LevelDebug + 1
if linenum != expected {
t.Fatal(linenum, "not "+strconv.Itoa(expected)+" lines")
}
os.Remove("test.log")
}
func TestFile2(t *testing.T) {
log := NewLogger(10000)
log.SetLogger("file", fmt.Sprintf(`{"filename":"test2.log","level":%d}`, LevelError))
log.Debug("debug")
log.Info("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error")
log.Alert("alert")
log.Critical("critical")
log.Emergency("emergency")
time.Sleep(time.Second * 4)
f, err := os.Open("test2.log")
if err != nil {
t.Fatal(err)
}
b := bufio.NewReader(f)
linenum := 0
for {
line, _, err := b.ReadLine()
if err != nil {
break
}
if len(line) > 0 {
linenum++
}
}
var expected = LevelError + 1
if linenum != expected {
t.Fatal(linenum, "not "+strconv.Itoa(expected)+" lines")
}
os.Remove("test2.log")
}
func TestFileRotate(t *testing.T) {
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`)
log.Debug("debug")
log.Info("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error")
log.Alert("alert")
log.Critical("critical")
log.Emergency("emergency")
time.Sleep(time.Second * 4)
rotatename := "test3.log" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1)
b, err := exists(rotatename)
if !b || err != nil {
t.Fatal("rotate not generated")
}
os.Remove(rotatename)
os.Remove("test3.log")
}
func exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
func BenchmarkFile(b *testing.B) {
log := NewLogger(100000)
log.SetLogger("file", `{"filename":"test4.log"}`)
for i := 0; i < b.N; i++ {
log.Debug("debug")
}
os.Remove("test4.log")
}

300
logs/log.go Normal file
View File

@ -0,0 +1,300 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Usage:
//
// import "github.com/astaxie/beego/logs"
//
// log := NewLogger(10000)
// log.SetLogger("console", "")
//
// > the first params stand for how many channel
//
// Use it like this:
//
// log.Trace("trace")
// log.Info("info")
// log.Warn("warning")
// log.Debug("debug")
// log.Critical("critical")
//
// more docs http://beego.me/docs/module/logs.md
package logs
import (
"fmt"
"path"
"runtime"
"sync"
)
// RFC5424 log message levels.
const (
LevelEmergency = iota
LevelAlert
LevelCritical
LevelError
LevelWarning
LevelNotice
LevelInformational
LevelDebug
)
// Legacy loglevel constants to ensure backwards compatibility.
//
// Deprecated: will be removed in 1.5.0.
const (
LevelInfo = LevelInformational
LevelTrace = LevelDebug
LevelWarn = LevelWarning
)
type loggerType func() LoggerInterface
// LoggerInterface defines the behavior of a log provider.
type LoggerInterface interface {
Init(config string) error
WriteMsg(msg string, level int) error
Destroy()
Flush()
}
var adapters = make(map[string]loggerType)
// Register makes a log provide available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, log loggerType) {
if log == nil {
panic("logs: Register provide is nil")
}
if _, dup := adapters[name]; dup {
panic("logs: Register called twice for provider " + name)
}
adapters[name] = log
}
// BeeLogger is default logger in beego application.
// it can contain several providers and log message into all providers.
type BeeLogger struct {
lock sync.Mutex
level int
enableFuncCallDepth bool
loggerFuncCallDepth int
msg chan *logMsg
outputs map[string]LoggerInterface
}
type logMsg struct {
level int
msg string
}
// NewLogger returns a new BeeLogger.
// channellen means the number of messages in chan.
// if the buffering chan is full, logger adapters write to file or other way.
func NewLogger(channellen int64) *BeeLogger {
bl := new(BeeLogger)
bl.level = LevelDebug
bl.loggerFuncCallDepth = 2
bl.msg = make(chan *logMsg, channellen)
bl.outputs = make(map[string]LoggerInterface)
//bl.SetLogger("console", "") // default output to console
go bl.startLogger()
return bl
}
// SetLogger provides a given logger adapter into BeeLogger with config string.
// config need to be correct JSON as string: {"interval":360}.
func (bl *BeeLogger) SetLogger(adaptername string, config string) error {
bl.lock.Lock()
defer bl.lock.Unlock()
if log, ok := adapters[adaptername]; ok {
lg := log()
err := lg.Init(config)
bl.outputs[adaptername] = lg
if err != nil {
fmt.Println("logs.BeeLogger.SetLogger: " + err.Error())
return err
}
} else {
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adaptername)
}
return nil
}
// remove a logger adapter in BeeLogger.
func (bl *BeeLogger) DelLogger(adaptername string) error {
bl.lock.Lock()
defer bl.lock.Unlock()
if lg, ok := bl.outputs[adaptername]; ok {
lg.Destroy()
delete(bl.outputs, adaptername)
return nil
} else {
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adaptername)
}
}
func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
if loglevel > bl.level {
return nil
}
lm := new(logMsg)
lm.level = loglevel
if bl.enableFuncCallDepth {
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
if ok {
_, filename := path.Split(file)
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
} else {
lm.msg = msg
}
} else {
lm.msg = msg
}
bl.msg <- lm
return nil
}
// Set log message level.
//
// If message level (such as LevelDebug) is higher than logger level (such as LevelWarning),
// log providers will not even be sent the message.
func (bl *BeeLogger) SetLevel(l int) {
bl.level = l
}
// set log funcCallDepth
func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
bl.loggerFuncCallDepth = d
}
// enable log funcCallDepth
func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
bl.enableFuncCallDepth = b
}
// start logger chan reading.
// when chan is not empty, write logs.
func (bl *BeeLogger) startLogger() {
for {
select {
case bm := <-bl.msg:
for _, l := range bl.outputs {
err := l.WriteMsg(bm.msg, bm.level)
if err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
}
}
}
}
// Log EMERGENCY level message.
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
msg := fmt.Sprintf("[M] "+format, v...)
bl.writerMsg(LevelEmergency, msg)
}
// Log ALERT level message.
func (bl *BeeLogger) Alert(format string, v ...interface{}) {
msg := fmt.Sprintf("[A] "+format, v...)
bl.writerMsg(LevelAlert, msg)
}
// Log CRITICAL level message.
func (bl *BeeLogger) Critical(format string, v ...interface{}) {
msg := fmt.Sprintf("[C] "+format, v...)
bl.writerMsg(LevelCritical, msg)
}
// Log ERROR level message.
func (bl *BeeLogger) Error(format string, v ...interface{}) {
msg := fmt.Sprintf("[E] "+format, v...)
bl.writerMsg(LevelError, msg)
}
// Log WARNING level message.
func (bl *BeeLogger) Warning(format string, v ...interface{}) {
msg := fmt.Sprintf("[W] "+format, v...)
bl.writerMsg(LevelWarning, msg)
}
// Log NOTICE level message.
func (bl *BeeLogger) Notice(format string, v ...interface{}) {
msg := fmt.Sprintf("[N] "+format, v...)
bl.writerMsg(LevelNotice, msg)
}
// Log INFORMATIONAL level message.
func (bl *BeeLogger) Informational(format string, v ...interface{}) {
msg := fmt.Sprintf("[I] "+format, v...)
bl.writerMsg(LevelInformational, msg)
}
// Log DEBUG level message.
func (bl *BeeLogger) Debug(format string, v ...interface{}) {
msg := fmt.Sprintf("[D] "+format, v...)
bl.writerMsg(LevelDebug, msg)
}
// Log WARN level message.
//
// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0.
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
bl.Warning(format, v...)
}
// Log INFO level message.
//
// Deprecated: compatibility alias for Informational(), Will be removed in 1.5.0.
func (bl *BeeLogger) Info(format string, v ...interface{}) {
bl.Informational(format, v...)
}
// Log TRACE level message.
//
// Deprecated: compatibility alias for Debug(), Will be removed in 1.5.0.
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
bl.Debug(format, v...)
}
// flush all chan data.
func (bl *BeeLogger) Flush() {
for _, l := range bl.outputs {
l.Flush()
}
}
// close logger, flush all chan data and destroy all adapters in BeeLogger.
func (bl *BeeLogger) Close() {
for {
if len(bl.msg) > 0 {
bm := <-bl.msg
for _, l := range bl.outputs {
err := l.WriteMsg(bm.msg, bm.level)
if err != nil {
fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
}
}
} else {
break
}
}
for _, l := range bl.outputs {
l.Flush()
l.Destroy()
}
}

166
logs/smtp.go Normal file
View File

@ -0,0 +1,166 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logs
import (
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/smtp"
"strings"
"time"
)
const (
subjectPhrase = "Diagnostic message from server"
)
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
type SmtpWriter struct {
Username string `json:"Username"`
Password string `json:"password"`
Host string `json:"Host"`
Subject string `json:"subject"`
FromAddress string `json:"fromAddress"`
RecipientAddresses []string `json:"sendTos"`
Level int `json:"level"`
}
// create smtp writer.
func NewSmtpWriter() LoggerInterface {
return &SmtpWriter{Level: LevelTrace}
}
// init smtp writer with json config.
// config like:
// {
// "Username":"example@gmail.com",
// "password:"password",
// "host":"smtp.gmail.com:465",
// "subject":"email title",
// "fromAddress":"from@example.com",
// "sendTos":["email1","email2"],
// "level":LevelError
// }
func (s *SmtpWriter) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), s)
if err != nil {
return err
}
return nil
}
func (s *SmtpWriter) GetSmtpAuth(host string) smtp.Auth {
if len(strings.Trim(s.Username, " ")) == 0 && len(strings.Trim(s.Password, " ")) == 0 {
return nil
}
return smtp.PlainAuth(
"",
s.Username,
s.Password,
host,
)
}
func (s *SmtpWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error {
client, err := smtp.Dial(hostAddressWithPort)
if err != nil {
return err
}
host, _, _ := net.SplitHostPort(hostAddressWithPort)
tlsConn := &tls.Config{
InsecureSkipVerify: true,
ServerName: host,
}
if err = client.StartTLS(tlsConn); err != nil {
return err
}
if auth != nil {
if err = client.Auth(auth); err != nil {
return err
}
}
if err = client.Mail(fromAddress); err != nil {
return err
}
for _, rec := range recipients {
if err = client.Rcpt(rec); err != nil {
return err
}
}
w, err := client.Data()
if err != nil {
return err
}
_, err = w.Write([]byte(msgContent))
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
err = client.Quit()
if err != nil {
return err
}
return nil
}
// write message in smtp writer.
// it will send an email with subject and only this message.
func (s *SmtpWriter) WriteMsg(msg string, level int) error {
if level > s.Level {
return nil
}
hp := strings.Split(s.Host, ":")
// Set up authentication information.
auth := s.GetSmtpAuth(hp[0])
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
content_type := "Content-Type: text/plain" + "; charset=UTF-8"
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
err := s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
return err
}
// implementing method. empty.
func (s *SmtpWriter) Flush() {
return
}
// implementing method. empty.
func (s *SmtpWriter) Destroy() {
return
}
func init() {
Register("smtp", NewSmtpWriter)
}

27
logs/smtp_test.go Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logs
import (
"testing"
"time"
)
func TestSmtp(t *testing.T) {
log := NewLogger(10000)
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
log.Critical("sendmail critical")
time.Sleep(time.Second * 30)
}

212
memzipfile.go Normal file
View File

@ -0,0 +1,212 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"bytes"
"compress/flate"
"compress/gzip"
"errors"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"sync"
"time"
)
var gmfim map[string]*memFileInfo = make(map[string]*memFileInfo)
var lock sync.RWMutex
// OpenMemZipFile returns MemFile object with a compressed static file.
// it's used for serve static file if gzip enable.
func openMemZipFile(path string, zip string) (*memFile, error) {
osfile, e := os.Open(path)
if e != nil {
return nil, e
}
defer osfile.Close()
osfileinfo, e := osfile.Stat()
if e != nil {
return nil, e
}
modtime := osfileinfo.ModTime()
fileSize := osfileinfo.Size()
lock.RLock()
cfi, ok := gmfim[zip+":"+path]
lock.RUnlock()
if !(ok && cfi.ModTime() == modtime && cfi.fileSize == fileSize) {
var content []byte
if zip == "gzip" {
var zipbuf bytes.Buffer
gzipwriter, e := gzip.NewWriterLevel(&zipbuf, gzip.BestCompression)
if e != nil {
return nil, e
}
_, e = io.Copy(gzipwriter, osfile)
gzipwriter.Close()
if e != nil {
return nil, e
}
content, e = ioutil.ReadAll(&zipbuf)
if e != nil {
return nil, e
}
} else if zip == "deflate" {
var zipbuf bytes.Buffer
deflatewriter, e := flate.NewWriter(&zipbuf, flate.BestCompression)
if e != nil {
return nil, e
}
_, e = io.Copy(deflatewriter, osfile)
deflatewriter.Close()
if e != nil {
return nil, e
}
content, e = ioutil.ReadAll(&zipbuf)
if e != nil {
return nil, e
}
} else {
content, e = ioutil.ReadAll(osfile)
if e != nil {
return nil, e
}
}
cfi = &memFileInfo{osfileinfo, modtime, content, int64(len(content)), fileSize}
lock.Lock()
defer lock.Unlock()
gmfim[zip+":"+path] = cfi
}
return &memFile{fi: cfi, offset: 0}, nil
}
// MemFileInfo contains a compressed file bytes and file information.
// it implements os.FileInfo interface.
type memFileInfo struct {
os.FileInfo
modTime time.Time
content []byte
contentSize int64
fileSize int64
}
// Name returns the compressed filename.
func (fi *memFileInfo) Name() string {
return fi.Name()
}
// Size returns the raw file content size, not compressed size.
func (fi *memFileInfo) Size() int64 {
return fi.contentSize
}
// Mode returns file mode.
func (fi *memFileInfo) Mode() os.FileMode {
return fi.Mode()
}
// ModTime returns the last modified time of raw file.
func (fi *memFileInfo) ModTime() time.Time {
return fi.modTime
}
// IsDir returns the compressing file is a directory or not.
func (fi *memFileInfo) IsDir() bool {
return fi.IsDir()
}
// return nil. implement the os.FileInfo interface method.
func (fi *memFileInfo) Sys() interface{} {
return nil
}
// MemFile contains MemFileInfo and bytes offset when reading.
// it implements io.Reader,io.ReadCloser and io.Seeker.
type memFile struct {
fi *memFileInfo
offset int64
}
// Close memfile.
func (f *memFile) Close() error {
return nil
}
// Get os.FileInfo of memfile.
func (f *memFile) Stat() (os.FileInfo, error) {
return f.fi, nil
}
// read os.FileInfo of files in directory of memfile.
// it returns empty slice.
func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
infos := []os.FileInfo{}
return infos, nil
}
// Read bytes from the compressed file bytes.
func (f *memFile) Read(p []byte) (n int, err error) {
if len(f.fi.content)-int(f.offset) >= len(p) {
n = len(p)
} else {
n = len(f.fi.content) - int(f.offset)
err = io.EOF
}
copy(p, f.fi.content[f.offset:f.offset+int64(n)])
f.offset += int64(n)
return
}
var errWhence = errors.New("Seek: invalid whence")
var errOffset = errors.New("Seek: invalid offset")
// Read bytes from the compressed file bytes by seeker.
func (f *memFile) Seek(offset int64, whence int) (ret int64, err error) {
switch whence {
default:
return 0, errWhence
case os.SEEK_SET:
case os.SEEK_CUR:
offset += f.offset
case os.SEEK_END:
offset += int64(len(f.fi.content))
}
if offset < 0 || int(offset) > len(f.fi.content) {
return 0, errOffset
}
f.offset = offset
return f.offset, nil
}
// GetAcceptEncodingZip returns accept encoding format in http header.
// zip is first, then deflate if both accepted.
// If no accepted, return empty string.
func getAcceptEncodingZip(r *http.Request) string {
ss := r.Header.Get("Accept-Encoding")
ss = strings.ToLower(ss)
if strings.Contains(ss, "gzip") {
return "gzip"
} else if strings.Contains(ss, "deflate") {
return "deflate"
} else {
return ""
}
}

View File

@ -1,15 +1,34 @@
package beego
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package middleware
import (
"fmt"
"html/template"
"net/http"
"runtime"
"strconv"
)
var (
AppName string
VERSION string
)
var tpl = `
<!DOCTYPE html>
<html>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>beego application error</title>
@ -23,10 +42,10 @@ var tpl = `
#content .stack pre{padding-left: 10px;}
table {}
td.t {text-align: right; padding-right: 5px; color: #888;}
</style>
</style>
<script type="text/javascript">
</script>
</head>
</head>
<body>
<div id="header">
<h2>{{.AppError}}</h2>
@ -53,9 +72,10 @@ var tpl = `
<p>golang version: {{.GoVersion}}</p>
</div>
</body>
</html>
</html>
`
// render default application error page with error and stack string.
func ShowErr(err interface{}, rw http.ResponseWriter, r *http.Request, Stack string) {
t, _ := template.New("beegoerrortemp").Parse(tpl)
data := make(map[string]string)
@ -66,6 +86,7 @@ func ShowErr(err interface{}, rw http.ResponseWriter, r *http.Request, Stack str
data["Stack"] = Stack
data["BeegoVersion"] = VERSION
data["GoVersion"] = runtime.Version()
rw.WriteHeader(500)
t.Execute(rw, data)
}
@ -74,7 +95,7 @@ var errtpl = `
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Page Not Found</title>
<title>{{.Title}}</title>
<style type="text/css">
* {
margin:0;
@ -161,7 +182,7 @@ var errtpl = `
{{.Content}}
<a href="/" title="Home" class="button">Go Home</a><br />
<br>power by beego {{.BeegoVersion}}
<br>Powered by beego {{.BeegoVersion}}
</div>
</div>
</div>
@ -169,18 +190,19 @@ var errtpl = `
</html>
`
// map of http handlers for each error string.
var ErrorMaps map[string]http.HandlerFunc
func init() {
ErrorMaps = make(map[string]http.HandlerFunc)
}
//404
// show 404 notfound error.
func NotFound(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Page Not Found"
data["Content"] = template.HTML("<br>The Page You have requested flown the coop." +
data["Content"] = template.HTML("<br>The page you have requested has flown the coop." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The page has moved" +
@ -189,30 +211,32 @@ func NotFound(rw http.ResponseWriter, r *http.Request) {
"<br>You like 404 pages" +
"</ul>")
data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusNotFound)
t.Execute(rw, data)
}
//401
// show 401 unauthorized error.
func Unauthorized(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Unauthorized"
data["Content"] = template.HTML("<br>The Page You have requested can't authorized." +
data["Content"] = template.HTML("<br>The page you have requested can't be authorized." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Check the credentials that you supplied" +
"<br>Check the address for errors" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>")
data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusUnauthorized)
t.Execute(rw, data)
}
//403
// show 403 forbidden error.
func Forbidden(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Forbidden"
data["Content"] = template.HTML("<br>The Page You have requested forbidden." +
data["Content"] = template.HTML("<br>The page you have requested is forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Your address may be blocked" +
@ -220,39 +244,52 @@ func Forbidden(rw http.ResponseWriter, r *http.Request) {
"<br>You need to log in" +
"</ul>")
data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusForbidden)
t.Execute(rw, data)
}
//503
// show 503 service unavailable error.
func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Service Unavailable"
data["Content"] = template.HTML("<br>The Page You have requested unavailable." +
data["Content"] = template.HTML("<br>The page you have requested is unavailable." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br><br>The page is overloaded" +
"<br>Please try again later." +
"</ul>")
data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusServiceUnavailable)
t.Execute(rw, data)
}
//500
// show 500 internal server error.
func InternalServerError(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Internal Server Error"
data["Content"] = template.HTML("<br>The Page You have requested has down now." +
data["Content"] = template.HTML("<br>The page you have requested is down right now." +
"<br><br><ul>" +
"<br>simply try again later" +
"<br>you should report the fault to the website administrator" +
"</ul>")
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusInternalServerError)
t.Execute(rw, data)
}
func registerErrorHander() {
// show 500 internal error with simple text string.
func SimpleServerError(rw http.ResponseWriter, r *http.Request) {
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
// add http handler for given error string.
func Errorhandler(err string, h http.HandlerFunc) {
ErrorMaps[err] = h
}
// register default error http handlers, 404,401,403,500 and 503.
func RegisterErrorHandler() {
if _, ok := ErrorMaps["404"]; !ok {
ErrorMaps["404"] = NotFound
}
@ -273,3 +310,30 @@ func registerErrorHander() {
ErrorMaps["500"] = InternalServerError
}
}
// show error string as simple text message.
// if error string is empty, show 500 error as default.
func Exception(errcode string, w http.ResponseWriter, r *http.Request, msg string) {
if h, ok := ErrorMaps[errcode]; ok {
isint, err := strconv.Atoi(errcode)
if err != nil {
isint = 500
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(isint)
h(w, r)
return
} else {
isint, err := strconv.Atoi(errcode)
if err != nil {
isint = 500
}
if isint == 400 {
msg = "404 page not found"
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(isint)
fmt.Fprintln(w, msg)
return
}
}

49
middleware/exceptions.go Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package middleware
import "fmt"
// http exceptions
type HTTPException struct {
StatusCode int // http status code 4xx, 5xx
Description string
}
// return http exception error string, e.g. "400 Bad Request".
func (e *HTTPException) Error() string {
return fmt.Sprintf("%d %s", e.StatusCode, e.Description)
}
// map of http exceptions for each http status code int.
// defined 400,401,403,404,405,500,502,503 and 504 default.
var HTTPExceptionMaps map[int]HTTPException
func init() {
HTTPExceptionMaps = make(map[int]HTTPException)
// Normal 4XX HTTP Status
HTTPExceptionMaps[400] = HTTPException{400, "Bad Request"}
HTTPExceptionMaps[401] = HTTPException{401, "Unauthorized"}
HTTPExceptionMaps[403] = HTTPException{403, "Forbidden"}
HTTPExceptionMaps[404] = HTTPException{404, "Not Found"}
HTTPExceptionMaps[405] = HTTPException{405, "Method Not Allowed"}
// Normal 5XX HTTP Status
HTTPExceptionMaps[500] = HTTPException{500, "Internal Server Error"}
HTTPExceptionMaps[502] = HTTPException{502, "Bad Gateway"}
HTTPExceptionMaps[503] = HTTPException{503, "Service Unavailable"}
HTTPExceptionMaps[504] = HTTPException{504, "Gateway Timeout"}
}

71
middleware/i18n.go Normal file
View File

@ -0,0 +1,71 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Usage:
//
// import "github.com/astaxie/beego/middleware"
//
// I18N = middleware.NewLocale("conf/i18n.conf", beego.AppConfig.String("language"))
//
// more docs: http://beego.me/docs/module/i18n.md
package middleware
import (
"encoding/json"
"io/ioutil"
"os"
)
type Translation struct {
filepath string
CurrentLocal string
Locales map[string]map[string]string
}
func NewLocale(filepath string, defaultlocal string) *Translation {
i18n := make(map[string]map[string]string)
file, err := os.Open(filepath)
if err != nil {
panic("open " + filepath + " err :" + err.Error())
}
data, err := ioutil.ReadAll(file)
if err != nil {
panic("read " + filepath + " err :" + err.Error())
}
err = json.Unmarshal(data, &i18n)
if err != nil {
panic("json.Unmarshal " + filepath + " err :" + err.Error())
}
return &Translation{
filepath: filepath,
CurrentLocal: defaultlocal,
Locales: i18n,
}
}
func (t *Translation) SetLocale(local string) {
t.CurrentLocal = local
}
func (t *Translation) Translate(key string, local string) string {
if local == "" {
local = t.CurrentLocal
}
if ct, ok := t.Locales[key]; ok {
if v, o := ct[local]; o {
return v
}
}
return key
}

280
migration/migration.go Normal file
View File

@ -0,0 +1,280 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// migration package for migration
//
// The table structure is as follow:
//
// CREATE TABLE `migrations` (
// `id_migration` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
// `name` varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
// `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'date migrated or rolled back',
// `statements` longtext COMMENT 'SQL statements for this migration',
// `rollback_statements` longtext,
// `status` enum('update','rollback') DEFAULT NULL COMMENT 'update indicates it is a normal migration while rollback means this migration is rolled back',
// PRIMARY KEY (`id_migration`)
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
package migration
import (
"errors"
"sort"
"strings"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
)
// const the data format for the bee generate migration datatype
const (
M_DATE_FORMAT = "20060102_150405"
M_DB_DATE_FORMAT = "2006-01-02 15:04:05"
)
// Migrationer is an interface for all Migration struct
type Migrationer interface {
Up()
Down()
Reset()
Exec(name, status string) error
GetCreated() int64
}
var (
migrationMap map[string]Migrationer
)
func init() {
migrationMap = make(map[string]Migrationer)
}
// the basic type which will implement the basic type
type Migration struct {
sqls []string
Created string
}
// implement in the Inheritance struct for upgrade
func (m *Migration) Up() {
}
// implement in the Inheritance struct for down
func (m *Migration) Down() {
}
// add sql want to execute
func (m *Migration) Sql(sql string) {
m.sqls = append(m.sqls, sql)
}
// Reset the sqls
func (m *Migration) Reset() {
m.sqls = make([]string, 0)
}
// execute the sql already add in the sql
func (m *Migration) Exec(name, status string) error {
o := orm.NewOrm()
for _, s := range m.sqls {
beego.Info("exec sql:", s)
r := o.Raw(s)
_, err := r.Exec()
if err != nil {
return err
}
}
return m.addOrUpdateRecord(name, status)
}
func (m *Migration) addOrUpdateRecord(name, status string) error {
o := orm.NewOrm()
if status == "down" {
status = "rollback"
p, err := o.Raw("update migrations set `status` = ?, `rollback_statements` = ?, `created_at` = ? where name = ?").Prepare()
if err != nil {
return nil
}
_, err = p.Exec(status, strings.Join(m.sqls, "; "), time.Now().Format(M_DB_DATE_FORMAT), name)
return err
} else {
status = "update"
p, err := o.Raw("insert into migrations(`name`, `created_at`, `statements`, `status`) values(?,?,?,?)").Prepare()
if err != nil {
return err
}
_, err = p.Exec(name, time.Now().Format(M_DB_DATE_FORMAT), strings.Join(m.sqls, "; "), status)
return err
}
}
// get the unixtime from the Created
func (m *Migration) GetCreated() int64 {
t, err := time.Parse(M_DATE_FORMAT, m.Created)
if err != nil {
return 0
}
return t.Unix()
}
// register the Migration in the map
func Register(name string, m Migrationer) error {
if _, ok := migrationMap[name]; ok {
return errors.New("already exist name:" + name)
}
migrationMap[name] = m
return nil
}
// upgrate the migration from lasttime
func Upgrade(lasttime int64) error {
sm := sortMap(migrationMap)
i := 0
for _, v := range sm {
if v.created > lasttime {
beego.Info("start upgrade", v.name)
v.m.Reset()
v.m.Up()
err := v.m.Exec(v.name, "up")
if err != nil {
beego.Error("execute error:", err)
time.Sleep(2 * time.Second)
return err
}
beego.Info("end upgrade:", v.name)
i++
}
}
beego.Info("total success upgrade:", i, " migration")
time.Sleep(2 * time.Second)
return nil
}
//rollback the migration by the name
func Rollback(name string) error {
if v, ok := migrationMap[name]; ok {
beego.Info("start rollback")
v.Reset()
v.Down()
err := v.Exec(name, "down")
if err != nil {
beego.Error("execute error:", err)
time.Sleep(2 * time.Second)
return err
}
beego.Info("end rollback")
time.Sleep(2 * time.Second)
return nil
} else {
beego.Error("not exist the migrationMap name:" + name)
time.Sleep(2 * time.Second)
return errors.New("not exist the migrationMap name:" + name)
}
}
// reset all migration
// run all migration's down function
func Reset() error {
sm := sortMap(migrationMap)
i := 0
for j := len(sm) - 1; j >= 0; j-- {
v := sm[j]
if isRollBack(v.name) {
beego.Info("skip the", v.name)
time.Sleep(1 * time.Second)
continue
}
beego.Info("start reset:", v.name)
v.m.Reset()
v.m.Down()
err := v.m.Exec(v.name, "down")
if err != nil {
beego.Error("execute error:", err)
time.Sleep(2 * time.Second)
return err
}
i++
beego.Info("end reset:", v.name)
}
beego.Info("total success reset:", i, " migration")
time.Sleep(2 * time.Second)
return nil
}
// first Reset, then Upgrade
func Refresh() error {
err := Reset()
if err != nil {
beego.Error("execute error:", err)
time.Sleep(2 * time.Second)
return err
}
err = Upgrade(0)
return err
}
type dataSlice []data
type data struct {
created int64
name string
m Migrationer
}
// Len is part of sort.Interface.
func (d dataSlice) Len() int {
return len(d)
}
// Swap is part of sort.Interface.
func (d dataSlice) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
// Less is part of sort.Interface. We use count as the value to sort by
func (d dataSlice) Less(i, j int) bool {
return d[i].created < d[j].created
}
func sortMap(m map[string]Migrationer) dataSlice {
s := make(dataSlice, 0, len(m))
for k, v := range m {
d := data{}
d.created = v.GetCreated()
d.name = k
d.m = v
s = append(s, d)
}
sort.Sort(s)
return s
}
func isRollBack(name string) bool {
o := orm.NewOrm()
var maps []orm.Params
num, err := o.Raw("select * from migrations where `name` = ? order by id_migration desc", name).Values(&maps)
if err != nil {
beego.Info("get name has error", err)
return false
}
if num <= 0 {
return false
}
if maps[0]["status"] == "rollback" {
return true
}
return false
}

566
mime.go Normal file
View File

@ -0,0 +1,566 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"mime"
)
var mimemaps map[string]string = map[string]string{
".3dm": "x-world/x-3dmf",
".3dmf": "x-world/x-3dmf",
".7z": "application/x-7z-compressed",
".a": "application/octet-stream",
".aab": "application/x-authorware-bin",
".aam": "application/x-authorware-map",
".aas": "application/x-authorware-seg",
".abc": "text/vndabc",
".ace": "application/x-ace-compressed",
".acgi": "text/html",
".afl": "video/animaflex",
".ai": "application/postscript",
".aif": "audio/aiff",
".aifc": "audio/aiff",
".aiff": "audio/aiff",
".aim": "application/x-aim",
".aip": "text/x-audiosoft-intra",
".alz": "application/x-alz-compressed",
".ani": "application/x-navi-animation",
".aos": "application/x-nokia-9000-communicator-add-on-software",
".aps": "application/mime",
".arc": "application/x-arc-compressed",
".arj": "application/arj",
".art": "image/x-jg",
".asf": "video/x-ms-asf",
".asm": "text/x-asm",
".asp": "text/asp",
".asx": "application/x-mplayer2",
".au": "audio/basic",
".avi": "video/x-msvideo",
".avs": "video/avs-video",
".bcpio": "application/x-bcpio",
".bin": "application/mac-binary",
".bmp": "image/bmp",
".boo": "application/book",
".book": "application/book",
".boz": "application/x-bzip2",
".bsh": "application/x-bsh",
".bz2": "application/x-bzip2",
".bz": "application/x-bzip",
".c++": "text/plain",
".c": "text/x-c",
".cab": "application/vnd.ms-cab-compressed",
".cat": "application/vndms-pkiseccat",
".cc": "text/x-c",
".ccad": "application/clariscad",
".cco": "application/x-cocoa",
".cdf": "application/cdf",
".cer": "application/pkix-cert",
".cha": "application/x-chat",
".chat": "application/x-chat",
".chrt": "application/vnd.kde.kchart",
".class": "application/java",
".com": "text/plain",
".conf": "text/plain",
".cpio": "application/x-cpio",
".cpp": "text/x-c",
".cpt": "application/mac-compactpro",
".crl": "application/pkcs-crl",
".crt": "application/pkix-cert",
".crx": "application/x-chrome-extension",
".csh": "text/x-scriptcsh",
".css": "text/css",
".csv": "text/csv",
".cxx": "text/plain",
".dar": "application/x-dar",
".dcr": "application/x-director",
".deb": "application/x-debian-package",
".deepv": "application/x-deepv",
".def": "text/plain",
".der": "application/x-x509-ca-cert",
".dif": "video/x-dv",
".dir": "application/x-director",
".divx": "video/divx",
".dl": "video/dl",
".dmg": "application/x-apple-diskimage",
".doc": "application/msword",
".dot": "application/msword",
".dp": "application/commonground",
".drw": "application/drafting",
".dump": "application/octet-stream",
".dv": "video/x-dv",
".dvi": "application/x-dvi",
".dwf": "drawing/x-dwf=(old)",
".dwg": "application/acad",
".dxf": "application/dxf",
".dxr": "application/x-director",
".el": "text/x-scriptelisp",
".elc": "application/x-bytecodeelisp=(compiled=elisp)",
".eml": "message/rfc822",
".env": "application/x-envoy",
".eps": "application/postscript",
".es": "application/x-esrehber",
".etx": "text/x-setext",
".evy": "application/envoy",
".exe": "application/octet-stream",
".f77": "text/x-fortran",
".f90": "text/x-fortran",
".f": "text/x-fortran",
".fdf": "application/vndfdf",
".fif": "application/fractals",
".fli": "video/fli",
".flo": "image/florian",
".flv": "video/x-flv",
".flx": "text/vndfmiflexstor",
".fmf": "video/x-atomic3d-feature",
".for": "text/x-fortran",
".fpx": "image/vndfpx",
".frl": "application/freeloader",
".funk": "audio/make",
".g3": "image/g3fax",
".g": "text/plain",
".gif": "image/gif",
".gl": "video/gl",
".gsd": "audio/x-gsm",
".gsm": "audio/x-gsm",
".gsp": "application/x-gsp",
".gss": "application/x-gss",
".gtar": "application/x-gtar",
".gz": "application/x-compressed",
".gzip": "application/x-gzip",
".h": "text/x-h",
".hdf": "application/x-hdf",
".help": "application/x-helpfile",
".hgl": "application/vndhp-hpgl",
".hh": "text/x-h",
".hlb": "text/x-script",
".hlp": "application/hlp",
".hpg": "application/vndhp-hpgl",
".hpgl": "application/vndhp-hpgl",
".hqx": "application/binhex",
".hta": "application/hta",
".htc": "text/x-component",
".htm": "text/html",
".html": "text/html",
".htmls": "text/html",
".htt": "text/webviewhtml",
".htx": "text/html",
".ice": "x-conference/x-cooltalk",
".ico": "image/x-icon",
".ics": "text/calendar",
".icz": "text/calendar",
".idc": "text/plain",
".ief": "image/ief",
".iefs": "image/ief",
".iges": "application/iges",
".igs": "application/iges",
".ima": "application/x-ima",
".imap": "application/x-httpd-imap",
".inf": "application/inf",
".ins": "application/x-internett-signup",
".ip": "application/x-ip2",
".isu": "video/x-isvideo",
".it": "audio/it",
".iv": "application/x-inventor",
".ivr": "i-world/i-vrml",
".ivy": "application/x-livescreen",
".jam": "audio/x-jam",
".jav": "text/x-java-source",
".java": "text/x-java-source",
".jcm": "application/x-java-commerce",
".jfif-tbnl": "image/jpeg",
".jfif": "image/jpeg",
".jnlp": "application/x-java-jnlp-file",
".jpe": "image/jpeg",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".jps": "image/x-jps",
".js": "application/javascript",
".json": "application/json",
".jut": "image/jutvision",
".kar": "audio/midi",
".karbon": "application/vnd.kde.karbon",
".kfo": "application/vnd.kde.kformula",
".flw": "application/vnd.kde.kivio",
".kml": "application/vnd.google-earth.kml+xml",
".kmz": "application/vnd.google-earth.kmz",
".kon": "application/vnd.kde.kontour",
".kpr": "application/vnd.kde.kpresenter",
".kpt": "application/vnd.kde.kpresenter",
".ksp": "application/vnd.kde.kspread",
".kwd": "application/vnd.kde.kword",
".kwt": "application/vnd.kde.kword",
".ksh": "text/x-scriptksh",
".la": "audio/nspaudio",
".lam": "audio/x-liveaudio",
".latex": "application/x-latex",
".lha": "application/lha",
".lhx": "application/octet-stream",
".list": "text/plain",
".lma": "audio/nspaudio",
".log": "text/plain",
".lsp": "text/x-scriptlisp",
".lst": "text/plain",
".lsx": "text/x-la-asf",
".ltx": "application/x-latex",
".lzh": "application/octet-stream",
".lzx": "application/lzx",
".m1v": "video/mpeg",
".m2a": "audio/mpeg",
".m2v": "video/mpeg",
".m3u": "audio/x-mpegurl",
".m": "text/x-m",
".man": "application/x-troff-man",
".manifest": "text/cache-manifest",
".map": "application/x-navimap",
".mar": "text/plain",
".mbd": "application/mbedlet",
".mc$": "application/x-magic-cap-package-10",
".mcd": "application/mcad",
".mcf": "text/mcf",
".mcp": "application/netmc",
".me": "application/x-troff-me",
".mht": "message/rfc822",
".mhtml": "message/rfc822",
".mid": "application/x-midi",
".midi": "application/x-midi",
".mif": "application/x-frame",
".mime": "message/rfc822",
".mjf": "audio/x-vndaudioexplosionmjuicemediafile",
".mjpg": "video/x-motion-jpeg",
".mm": "application/base64",
".mme": "application/base64",
".mod": "audio/mod",
".moov": "video/quicktime",
".mov": "video/quicktime",
".movie": "video/x-sgi-movie",
".mp2": "audio/mpeg",
".mp3": "audio/mpeg3",
".mp4": "video/mp4",
".mpa": "audio/mpeg",
".mpc": "application/x-project",
".mpe": "video/mpeg",
".mpeg": "video/mpeg",
".mpg": "video/mpeg",
".mpga": "audio/mpeg",
".mpp": "application/vndms-project",
".mpt": "application/x-project",
".mpv": "application/x-project",
".mpx": "application/x-project",
".mrc": "application/marc",
".ms": "application/x-troff-ms",
".mv": "video/x-sgi-movie",
".my": "audio/make",
".mzz": "application/x-vndaudioexplosionmzz",
".nap": "image/naplps",
".naplps": "image/naplps",
".nc": "application/x-netcdf",
".ncm": "application/vndnokiaconfiguration-message",
".nif": "image/x-niff",
".niff": "image/x-niff",
".nix": "application/x-mix-transfer",
".nsc": "application/x-conference",
".nvd": "application/x-navidoc",
".o": "application/octet-stream",
".oda": "application/oda",
".odb": "application/vnd.oasis.opendocument.database",
".odc": "application/vnd.oasis.opendocument.chart",
".odf": "application/vnd.oasis.opendocument.formula",
".odg": "application/vnd.oasis.opendocument.graphics",
".odi": "application/vnd.oasis.opendocument.image",
".odm": "application/vnd.oasis.opendocument.text-master",
".odp": "application/vnd.oasis.opendocument.presentation",
".ods": "application/vnd.oasis.opendocument.spreadsheet",
".odt": "application/vnd.oasis.opendocument.text",
".oga": "audio/ogg",
".ogg": "audio/ogg",
".ogv": "video/ogg",
".omc": "application/x-omc",
".omcd": "application/x-omcdatamaker",
".omcr": "application/x-omcregerator",
".otc": "application/vnd.oasis.opendocument.chart-template",
".otf": "application/vnd.oasis.opendocument.formula-template",
".otg": "application/vnd.oasis.opendocument.graphics-template",
".oth": "application/vnd.oasis.opendocument.text-web",
".oti": "application/vnd.oasis.opendocument.image-template",
".otm": "application/vnd.oasis.opendocument.text-master",
".otp": "application/vnd.oasis.opendocument.presentation-template",
".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
".ott": "application/vnd.oasis.opendocument.text-template",
".p10": "application/pkcs10",
".p12": "application/pkcs-12",
".p7a": "application/x-pkcs7-signature",
".p7c": "application/pkcs7-mime",
".p7m": "application/pkcs7-mime",
".p7r": "application/x-pkcs7-certreqresp",
".p7s": "application/pkcs7-signature",
".p": "text/x-pascal",
".part": "application/pro_eng",
".pas": "text/pascal",
".pbm": "image/x-portable-bitmap",
".pcl": "application/vndhp-pcl",
".pct": "image/x-pict",
".pcx": "image/x-pcx",
".pdb": "chemical/x-pdb",
".pdf": "application/pdf",
".pfunk": "audio/make",
".pgm": "image/x-portable-graymap",
".pic": "image/pict",
".pict": "image/pict",
".pkg": "application/x-newton-compatible-pkg",
".pko": "application/vndms-pkipko",
".pl": "text/x-scriptperl",
".plx": "application/x-pixclscript",
".pm4": "application/x-pagemaker",
".pm5": "application/x-pagemaker",
".pm": "text/x-scriptperl-module",
".png": "image/png",
".pnm": "application/x-portable-anymap",
".pot": "application/mspowerpoint",
".pov": "model/x-pov",
".ppa": "application/vndms-powerpoint",
".ppm": "image/x-portable-pixmap",
".pps": "application/mspowerpoint",
".ppt": "application/mspowerpoint",
".ppz": "application/mspowerpoint",
".pre": "application/x-freelance",
".prt": "application/pro_eng",
".ps": "application/postscript",
".psd": "application/octet-stream",
".pvu": "paleovu/x-pv",
".pwz": "application/vndms-powerpoint",
".py": "text/x-scriptphyton",
".pyc": "applicaiton/x-bytecodepython",
".qcp": "audio/vndqcelp",
".qd3": "x-world/x-3dmf",
".qd3d": "x-world/x-3dmf",
".qif": "image/x-quicktime",
".qt": "video/quicktime",
".qtc": "video/x-qtc",
".qti": "image/x-quicktime",
".qtif": "image/x-quicktime",
".ra": "audio/x-pn-realaudio",
".ram": "audio/x-pn-realaudio",
".rar": "application/x-rar-compressed",
".ras": "application/x-cmu-raster",
".rast": "image/cmu-raster",
".rexx": "text/x-scriptrexx",
".rf": "image/vndrn-realflash",
".rgb": "image/x-rgb",
".rm": "application/vndrn-realmedia",
".rmi": "audio/mid",
".rmm": "audio/x-pn-realaudio",
".rmp": "audio/x-pn-realaudio",
".rng": "application/ringing-tones",
".rnx": "application/vndrn-realplayer",
".roff": "application/x-troff",
".rp": "image/vndrn-realpix",
".rpm": "audio/x-pn-realaudio-plugin",
".rt": "text/vndrn-realtext",
".rtf": "text/richtext",
".rtx": "text/richtext",
".rv": "video/vndrn-realvideo",
".s": "text/x-asm",
".s3m": "audio/s3m",
".s7z": "application/x-7z-compressed",
".saveme": "application/octet-stream",
".sbk": "application/x-tbook",
".scm": "text/x-scriptscheme",
".sdml": "text/plain",
".sdp": "application/sdp",
".sdr": "application/sounder",
".sea": "application/sea",
".set": "application/set",
".sgm": "text/x-sgml",
".sgml": "text/x-sgml",
".sh": "text/x-scriptsh",
".shar": "application/x-bsh",
".shtml": "text/x-server-parsed-html",
".sid": "audio/x-psid",
".skd": "application/x-koan",
".skm": "application/x-koan",
".skp": "application/x-koan",
".skt": "application/x-koan",
".sit": "application/x-stuffit",
".sitx": "application/x-stuffitx",
".sl": "application/x-seelogo",
".smi": "application/smil",
".smil": "application/smil",
".snd": "audio/basic",
".sol": "application/solids",
".spc": "text/x-speech",
".spl": "application/futuresplash",
".spr": "application/x-sprite",
".sprite": "application/x-sprite",
".spx": "audio/ogg",
".src": "application/x-wais-source",
".ssi": "text/x-server-parsed-html",
".ssm": "application/streamingmedia",
".sst": "application/vndms-pkicertstore",
".step": "application/step",
".stl": "application/sla",
".stp": "application/step",
".sv4cpio": "application/x-sv4cpio",
".sv4crc": "application/x-sv4crc",
".svf": "image/vnddwg",
".svg": "image/svg+xml",
".svr": "application/x-world",
".swf": "application/x-shockwave-flash",
".t": "application/x-troff",
".talk": "text/x-speech",
".tar": "application/x-tar",
".tbk": "application/toolbook",
".tcl": "text/x-scripttcl",
".tcsh": "text/x-scripttcsh",
".tex": "application/x-tex",
".texi": "application/x-texinfo",
".texinfo": "application/x-texinfo",
".text": "text/plain",
".tgz": "application/gnutar",
".tif": "image/tiff",
".tiff": "image/tiff",
".tr": "application/x-troff",
".tsi": "audio/tsp-audio",
".tsp": "application/dsptype",
".tsv": "text/tab-separated-values",
".turbot": "image/florian",
".txt": "text/plain",
".uil": "text/x-uil",
".uni": "text/uri-list",
".unis": "text/uri-list",
".unv": "application/i-deas",
".uri": "text/uri-list",
".uris": "text/uri-list",
".ustar": "application/x-ustar",
".uu": "text/x-uuencode",
".uue": "text/x-uuencode",
".vcd": "application/x-cdlink",
".vcf": "text/x-vcard",
".vcard": "text/x-vcard",
".vcs": "text/x-vcalendar",
".vda": "application/vda",
".vdo": "video/vdo",
".vew": "application/groupwise",
".viv": "video/vivo",
".vivo": "video/vivo",
".vmd": "application/vocaltec-media-desc",
".vmf": "application/vocaltec-media-file",
".voc": "audio/voc",
".vos": "video/vosaic",
".vox": "audio/voxware",
".vqe": "audio/x-twinvq-plugin",
".vqf": "audio/x-twinvq",
".vql": "audio/x-twinvq-plugin",
".vrml": "application/x-vrml",
".vrt": "x-world/x-vrt",
".vsd": "application/x-visio",
".vst": "application/x-visio",
".vsw": "application/x-visio",
".w60": "application/wordperfect60",
".w61": "application/wordperfect61",
".w6w": "application/msword",
".wav": "audio/wav",
".wb1": "application/x-qpro",
".wbmp": "image/vnd.wap.wbmp",
".web": "application/vndxara",
".wiz": "application/msword",
".wk1": "application/x-123",
".wmf": "windows/metafile",
".wml": "text/vnd.wap.wml",
".wmlc": "application/vnd.wap.wmlc",
".wmls": "text/vnd.wap.wmlscript",
".wmlsc": "application/vnd.wap.wmlscriptc",
".word": "application/msword",
".wp5": "application/wordperfect",
".wp6": "application/wordperfect",
".wp": "application/wordperfect",
".wpd": "application/wordperfect",
".wq1": "application/x-lotus",
".wri": "application/mswrite",
".wrl": "application/x-world",
".wrz": "model/vrml",
".wsc": "text/scriplet",
".wsrc": "application/x-wais-source",
".wtk": "application/x-wintalk",
".x-png": "image/png",
".xbm": "image/x-xbitmap",
".xdr": "video/x-amt-demorun",
".xgz": "xgl/drawing",
".xif": "image/vndxiff",
".xl": "application/excel",
".xla": "application/excel",
".xlb": "application/excel",
".xlc": "application/excel",
".xld": "application/excel",
".xlk": "application/excel",
".xll": "application/excel",
".xlm": "application/excel",
".xls": "application/excel",
".xlt": "application/excel",
".xlv": "application/excel",
".xlw": "application/excel",
".xm": "audio/xm",
".xml": "text/xml",
".xmz": "xgl/movie",
".xpix": "application/x-vndls-xpix",
".xpm": "image/x-xpixmap",
".xsr": "video/x-amt-showrun",
".xwd": "image/x-xwd",
".xyz": "chemical/x-pdb",
".z": "application/x-compress",
".zip": "application/zip",
".zoo": "application/octet-stream",
".zsh": "text/x-scriptzsh",
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
".docm": "application/vnd.ms-word.document.macroEnabled.12",
".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
".dotm": "application/vnd.ms-word.template.macroEnabled.12",
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12",
".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
".xltm": "application/vnd.ms-excel.template.macroEnabled.12",
".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
".pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
".ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
".potm": "application/vnd.ms-powerpoint.template.macroEnabled.12",
".ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12",
".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
".sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12",
".thmx": "application/vnd.ms-officetheme",
".onetoc": "application/onenote",
".onetoc2": "application/onenote",
".onetmp": "application/onenote",
".onepkg": "application/onenote",
".key": "application/x-iwork-keynote-sffkey",
".kth": "application/x-iwork-keynote-sffkth",
".nmbtemplate": "application/x-iwork-numbers-sfftemplate",
".numbers": "application/x-iwork-numbers-sffnumbers",
".pages": "application/x-iwork-pages-sffpages",
".template": "application/x-iwork-pages-sfftemplate",
".xpi": "application/x-xpinstall",
".oex": "application/x-opera-extension",
".mustache": "text/html",
}
func initMime() error {
for k, v := range mimemaps {
mime.AddExtensionType(k, v)
}
return nil
}

View File

@ -1,36 +0,0 @@
package beego
type BeeModel struct {
driver string
}
func (this *BeeModel) Insert() {
}
func (this *BeeModel) MultipleInsert() {
}
func (this *BeeModel) Update() {
}
func (this *BeeModel) Query() {
}
//Deletes from table with clauses where and using.
func (this *BeeModel) Delete() {
}
//Start a transaction
func (this *BeeModel) Transaction() {
}
//commit transaction
func (this *BeeModel) Commit() {
}

390
namespace.go Normal file
View File

@ -0,0 +1,390 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"net/http"
"strings"
beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
)
type namespaceCond func(*beecontext.Context) bool
type innnerNamespace func(*Namespace)
// Namespace is store all the info
type Namespace struct {
prefix string
handlers *ControllerRegistor
}
// get new Namespace
func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
ns := &Namespace{
prefix: prefix,
handlers: NewControllerRegister(),
}
for _, p := range params {
p(ns)
}
return ns
}
// set condtion function
// if cond return true can run this namespace, else can't
// usage:
// ns.Cond(func (ctx *context.Context) bool{
// if ctx.Input.Domain() == "api.beego.me" {
// return true
// }
// return false
// })
// Cond as the first filter
func (n *Namespace) Cond(cond namespaceCond) *Namespace {
fn := func(ctx *beecontext.Context) {
if !cond(ctx) {
middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed")
}
}
if v, ok := n.handlers.filters[BeforeRouter]; ok {
mr := new(FilterRouter)
mr.tree = NewTree()
mr.pattern = "*"
mr.filterFunc = fn
mr.tree.AddRouter("*", true)
n.handlers.filters[BeforeRouter] = append([]*FilterRouter{mr}, v...)
} else {
n.handlers.InsertFilter("*", BeforeRouter, fn)
}
return n
}
// add filter in the Namespace
// action has before & after
// FilterFunc
// usage:
// Filter("before", func (ctx *context.Context){
// _, ok := ctx.Input.Session("uid").(int)
// if !ok && ctx.Request.RequestURI != "/login" {
// ctx.Redirect(302, "/login")
// }
// })
func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace {
var a int
if action == "before" {
a = BeforeRouter
} else if action == "after" {
a = FinishRouter
}
for _, f := range filter {
n.handlers.InsertFilter("*", a, f)
}
return n
}
// same as beego.Rourer
// refer: https://godoc.org/github.com/astaxie/beego#Router
func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace {
n.handlers.Add(rootpath, c, mappingMethods...)
return n
}
// same as beego.AutoRouter
// refer: https://godoc.org/github.com/astaxie/beego#AutoRouter
func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace {
n.handlers.AddAuto(c)
return n
}
// same as beego.AutoPrefix
// refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix
func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace {
n.handlers.AddAutoPrefix(prefix, c)
return n
}
// same as beego.Get
// refer: https://godoc.org/github.com/astaxie/beego#Get
func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace {
n.handlers.Get(rootpath, f)
return n
}
// same as beego.Post
// refer: https://godoc.org/github.com/astaxie/beego#Post
func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace {
n.handlers.Post(rootpath, f)
return n
}
// same as beego.Delete
// refer: https://godoc.org/github.com/astaxie/beego#Delete
func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace {
n.handlers.Delete(rootpath, f)
return n
}
// same as beego.Put
// refer: https://godoc.org/github.com/astaxie/beego#Put
func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace {
n.handlers.Put(rootpath, f)
return n
}
// same as beego.Head
// refer: https://godoc.org/github.com/astaxie/beego#Head
func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace {
n.handlers.Head(rootpath, f)
return n
}
// same as beego.Options
// refer: https://godoc.org/github.com/astaxie/beego#Options
func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace {
n.handlers.Options(rootpath, f)
return n
}
// same as beego.Patch
// refer: https://godoc.org/github.com/astaxie/beego#Patch
func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace {
n.handlers.Patch(rootpath, f)
return n
}
// same as beego.Any
// refer: https://godoc.org/github.com/astaxie/beego#Any
func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace {
n.handlers.Any(rootpath, f)
return n
}
// same as beego.Handler
// refer: https://godoc.org/github.com/astaxie/beego#Handler
func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace {
n.handlers.Handler(rootpath, h)
return n
}
// add include class
// refer: https://godoc.org/github.com/astaxie/beego#Include
func (n *Namespace) Include(cList ...ControllerInterface) *Namespace {
n.handlers.Include(cList...)
return n
}
// nest Namespace
// usage:
//ns := beego.NewNamespace(“/v1”).
//Namespace(
// beego.NewNamespace("/shop").
// Get("/:id", func(ctx *context.Context) {
// ctx.Output.Body([]byte("shopinfo"))
// }),
// beego.NewNamespace("/order").
// Get("/:id", func(ctx *context.Context) {
// ctx.Output.Body([]byte("orderinfo"))
// }),
// beego.NewNamespace("/crm").
// Get("/:id", func(ctx *context.Context) {
// ctx.Output.Body([]byte("crminfo"))
// }),
//)
func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
for _, ni := range ns {
for k, v := range ni.handlers.routers {
if t, ok := n.handlers.routers[k]; ok {
addPrefix(v, ni.prefix)
n.handlers.routers[k].AddTree(ni.prefix, v)
} else {
t = NewTree()
t.AddTree(ni.prefix, v)
addPrefix(t, ni.prefix)
n.handlers.routers[k] = t
}
}
if n.handlers.enableFilter {
for pos, filterList := range ni.handlers.filters {
for _, mr := range filterList {
t := NewTree()
t.AddTree(ni.prefix, mr.tree)
mr.tree = t
n.handlers.insertFilterRouter(pos, mr)
}
}
}
}
return n
}
// register Namespace into beego.Handler
// support multi Namespace
func AddNamespace(nl ...*Namespace) {
for _, n := range nl {
for k, v := range n.handlers.routers {
if t, ok := BeeApp.Handlers.routers[k]; ok {
addPrefix(v, n.prefix)
BeeApp.Handlers.routers[k].AddTree(n.prefix, v)
} else {
t = NewTree()
t.AddTree(n.prefix, v)
addPrefix(t, n.prefix)
BeeApp.Handlers.routers[k] = t
}
}
if n.handlers.enableFilter {
for pos, filterList := range n.handlers.filters {
for _, mr := range filterList {
t := NewTree()
t.AddTree(n.prefix, mr.tree)
mr.tree = t
BeeApp.Handlers.insertFilterRouter(pos, mr)
}
}
}
}
}
func addPrefix(t *Tree, prefix string) {
for _, v := range t.fixrouters {
addPrefix(v, prefix)
}
if t.wildcard != nil {
addPrefix(t.wildcard, prefix)
}
for _, l := range t.leaves {
if c, ok := l.runObject.(*controllerInfo); ok {
if !strings.HasPrefix(c.pattern, prefix) {
c.pattern = prefix + c.pattern
}
}
}
}
// Namespace Condition
func NSCond(cond namespaceCond) innnerNamespace {
return func(ns *Namespace) {
ns.Cond(cond)
}
}
// Namespace BeforeRouter filter
func NSBefore(filiterList ...FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Filter("before", filiterList...)
}
}
// Namespace FinishRouter filter
func NSAfter(filiterList ...FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Filter("after", filiterList...)
}
}
// Namespace Include ControllerInterface
func NSInclude(cList ...ControllerInterface) innnerNamespace {
return func(ns *Namespace) {
ns.Include(cList...)
}
}
// Namespace Router
func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) innnerNamespace {
return func(ns *Namespace) {
ns.Router(rootpath, c, mappingMethods...)
}
}
// Namespace Get
func NSGet(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Get(rootpath, f)
}
}
// Namespace Post
func NSPost(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Post(rootpath, f)
}
}
// Namespace Head
func NSHead(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Head(rootpath, f)
}
}
// Namespace Put
func NSPut(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Put(rootpath, f)
}
}
// Namespace Delete
func NSDelete(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Delete(rootpath, f)
}
}
// Namespace Any
func NSAny(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Any(rootpath, f)
}
}
// Namespace Options
func NSOptions(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Options(rootpath, f)
}
}
// Namespace Patch
func NSPatch(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Patch(rootpath, f)
}
}
//Namespace AutoRouter
func NSAutoRouter(c ControllerInterface) innnerNamespace {
return func(ns *Namespace) {
ns.AutoRouter(c)
}
}
// Namespace AutoPrefix
func NSAutoPrefix(prefix string, c ControllerInterface) innnerNamespace {
return func(ns *Namespace) {
ns.AutoPrefix(prefix, c)
}
}
// Namespace add sub Namespace
func NSNamespace(prefix string, params ...innnerNamespace) innnerNamespace {
return func(ns *Namespace) {
n := NewNamespace(prefix, params...)
ns.Namespace(n)
}
}

171
namespace_test.go Normal file
View File

@ -0,0 +1,171 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"net/http"
"net/http/httptest"
"strconv"
"testing"
"github.com/astaxie/beego/context"
)
func TestNamespaceGet(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/user", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Get("/user", func(ctx *context.Context) {
ctx.Output.Body([]byte("v1_user"))
})
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "v1_user" {
t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String())
}
}
func TestNamespacePost(t *testing.T) {
r, _ := http.NewRequest("POST", "/v1/user/123", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Post("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
})
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" {
t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceNest(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/admin/order", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Namespace(
NewNamespace("/admin").
Get("/order", func(ctx *context.Context) {
ctx.Output.Body([]byte("order"))
}),
)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "order" {
t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceNestParam(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/admin/order/123", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Namespace(
NewNamespace("/admin").
Get("/order/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}),
)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" {
t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceRouter(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/api/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Router("/api/list", &TestController{}, "*:List")
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "i am list" {
t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceAutoFunc(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/test/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.AutoRouter(&TestController{})
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "i am list" {
t.Errorf("user define func can't run")
}
}
func TestNamespaceFilter(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/user/123", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Filter("before", func(ctx *context.Context) {
ctx.Output.Body([]byte("this is Filter"))
}).
Get("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
})
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "this is Filter" {
t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceCond(t *testing.T) {
r, _ := http.NewRequest("GET", "/v2/test/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v2")
ns.Cond(func(ctx *context.Context) bool {
if ctx.Input.Domain() == "beego.me" {
return true
}
return false
}).
AutoRouter(&TestController{})
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Code != 405 {
t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code))
}
}
func TestNamespaceInside(t *testing.T) {
r, _ := http.NewRequest("GET", "/v3/shop/order/123", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v3",
NSAutoRouter(&TestController{}),
NSNamespace("/shop",
NSGet("/order/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}),
),
)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" {
t.Errorf("TestNamespaceInside can't run, get the response is " + w.Body.String())
}
}

158
orm/README.md Normal file
View File

@ -0,0 +1,158 @@
# beego orm
[![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest)
A powerful orm framework for go.
It is heavily influenced by Django ORM, SQLAlchemy.
now, beta, unstable, may be changing some api make your app build failed.
**Support Database:**
* MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
* PostgreSQL: [github.com/lib/pq](https://github.com/lib/pq)
* Sqlite3: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
Passed all test, but need more feedback.
**Features:**
* full go type support
* easy for usage, simple CRUD operation
* auto join with relation table
* cross DataBase compatible query
* Raw SQL query / mapper without orm model
* full test keep stable and strong
more features please read the docs
**Install:**
go get github.com/astaxie/beego/orm
## Changelog
* 2013-08-19: support table auto create
* 2013-08-13: update test for database types
* 2013-08-13: go type support, such as int8, uint8, byte, rune
* 2013-08-13: date / datetime timezone support very well
## Quick Start
#### Simple Usage
```go
package main
import (
"fmt"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql" // import your used driver
)
// Model Struct
type User struct {
Id int `orm:"auto"`
Name string `orm:"size(100)"`
}
func init() {
// register model
orm.RegisterModel(new(User))
// set default database
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
}
func main() {
o := orm.NewOrm()
user := User{Name: "slene"}
// insert
id, err := o.Insert(&user)
// update
user.Name = "astaxie"
num, err := o.Update(&user)
// read one
u := User{Id: user.Id}
err = o.Read(&u)
// delete
num, err = o.Delete(&u)
}
```
#### Next with relation
```go
type Post struct {
Id int `orm:"auto"`
Title string `orm:"size(100)"`
User *User `orm:"rel(fk)"`
}
var posts []*Post
qs := o.QueryTable("post")
num, err := qs.Filter("User__Name", "slene").All(&posts)
```
#### Use Raw sql
If you don't like ORMuse Raw SQL to query / mapping without ORM setting
```go
var maps []Params
num, err := o.Raw("SELECT id FROM user WHERE name = ?", "slene").Values(&maps)
if num > 0 {
fmt.Println(maps[0]["id"])
}
```
#### Transaction
```go
o.Begin()
...
user := User{Name: "slene"}
id, err := o.Insert(&user)
if err != nil {
o.Commit()
} else {
o.Rollback()
}
```
#### Debug Log Queries
In development env, you can simple use
```go
func main() {
orm.Debug = true
...
```
enable log queries.
output include all queries, such as exec / prepare / transaction.
like this:
```go
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [ db.Exec / 0.4ms] - [INSERT INTO `user` (`name`) VALUES (?)] - `slene`
...
```
note: not recommend use this in product env.
## Docs
more details and examples in docs and test
[documents](http://beego.me/docs/mvc/model/overview.md)

283
orm/cmd.go Normal file
View File

@ -0,0 +1,283 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package orm
import (
"flag"
"fmt"
"os"
"strings"
)
type commander interface {
Parse([]string)
Run() error
}
var (
commands = make(map[string]commander)
)
// print help.
func printHelp(errs ...string) {
content := `orm command usage:
syncdb - auto create tables
sqlall - print sql of create tables
help - print this help
`
if len(errs) > 0 {
fmt.Println(errs[0])
}
fmt.Println(content)
os.Exit(2)
}
// listen for orm command and then run it if command arguments passed.
func RunCommand() {
if len(os.Args) < 2 || os.Args[1] != "orm" {
return
}
BootStrap()
args := argString(os.Args[2:])
name := args.Get(0)
if name == "help" {
printHelp()
}
if cmd, ok := commands[name]; ok {
cmd.Parse(os.Args[3:])
cmd.Run()
os.Exit(0)
} else {
if name == "" {
printHelp()
} else {
printHelp(fmt.Sprintf("unknown command %s", name))
}
}
}
// sync database struct command interface.
type commandSyncDb struct {
al *alias
force bool
verbose bool
noInfo bool
rtOnError bool
}
// parse orm command line arguments.
func (d *commandSyncDb) Parse(args []string) {
var name string
flagSet := flag.NewFlagSet("orm command: syncdb", flag.ExitOnError)
flagSet.StringVar(&name, "db", "default", "DataBase alias name")
flagSet.BoolVar(&d.force, "force", false, "drop tables before create")
flagSet.BoolVar(&d.verbose, "v", false, "verbose info")
flagSet.Parse(args)
d.al = getDbAlias(name)
}
// run orm line command.
func (d *commandSyncDb) Run() error {
var drops []string
if d.force {
drops = getDbDropSql(d.al)
}
db := d.al.DB
if d.force {
for i, mi := range modelCache.allOrdered() {
query := drops[i]
if !d.noInfo {
fmt.Printf("drop table `%s`\n", mi.table)
}
_, err := db.Exec(query)
if d.verbose {
fmt.Printf(" %s\n\n", query)
}
if err != nil {
if d.rtOnError {
return err
}
fmt.Printf(" %s\n", err.Error())
}
}
}
sqls, indexes := getDbCreateSql(d.al)
tables, err := d.al.DbBaser.GetTables(db)
if err != nil {
if d.rtOnError {
return err
}
fmt.Printf(" %s\n", err.Error())
}
for i, mi := range modelCache.allOrdered() {
if tables[mi.table] {
if !d.noInfo {
fmt.Printf("table `%s` already exists, skip\n", mi.table)
}
var fields []*fieldInfo
columns, err := d.al.DbBaser.GetColumns(db, mi.table)
if err != nil {
if d.rtOnError {
return err
}
fmt.Printf(" %s\n", err.Error())
}
for _, fi := range mi.fields.fieldsDB {
if _, ok := columns[fi.column]; ok == false {
fields = append(fields, fi)
}
}
for _, fi := range fields {
query := getColumnAddQuery(d.al, fi)
if !d.noInfo {
fmt.Printf("add column `%s` for table `%s`\n", fi.fullName, mi.table)
}
_, err := db.Exec(query)
if d.verbose {
fmt.Printf(" %s\n", query)
}
if err != nil {
if d.rtOnError {
return err
}
fmt.Printf(" %s\n", err.Error())
}
}
for _, idx := range indexes[mi.table] {
if d.al.DbBaser.IndexExists(db, idx.Table, idx.Name) == false {
if !d.noInfo {
fmt.Printf("create index `%s` for table `%s`\n", idx.Name, idx.Table)
}
query := idx.Sql
_, err := db.Exec(query)
if d.verbose {
fmt.Printf(" %s\n", query)
}
if err != nil {
if d.rtOnError {
return err
}
fmt.Printf(" %s\n", err.Error())
}
}
}
continue
}
if !d.noInfo {
fmt.Printf("create table `%s` \n", mi.table)
}
queries := []string{sqls[i]}
for _, idx := range indexes[mi.table] {
queries = append(queries, idx.Sql)
}
for _, query := range queries {
_, err := db.Exec(query)
if d.verbose {
query = " " + strings.Join(strings.Split(query, "\n"), "\n ")
fmt.Println(query)
}
if err != nil {
if d.rtOnError {
return err
}
fmt.Printf(" %s\n", err.Error())
}
}
if d.verbose {
fmt.Println("")
}
}
return nil
}
// database creation commander interface implement.
type commandSqlAll struct {
al *alias
}
// parse orm command line arguments.
func (d *commandSqlAll) Parse(args []string) {
var name string
flagSet := flag.NewFlagSet("orm command: sqlall", flag.ExitOnError)
flagSet.StringVar(&name, "db", "default", "DataBase alias name")
flagSet.Parse(args)
d.al = getDbAlias(name)
}
// run orm line command.
func (d *commandSqlAll) Run() error {
sqls, indexes := getDbCreateSql(d.al)
var all []string
for i, mi := range modelCache.allOrdered() {
queries := []string{sqls[i]}
for _, idx := range indexes[mi.table] {
queries = append(queries, idx.Sql)
}
sql := strings.Join(queries, "\n")
all = append(all, sql)
}
fmt.Println(strings.Join(all, "\n\n"))
return nil
}
func init() {
commands["syncdb"] = new(commandSyncDb)
commands["sqlall"] = new(commandSqlAll)
}
// run syncdb command line.
// name means table's alias name. default is "default".
// force means run next sql if the current is error.
// verbose means show all info when running command or not.
func RunSyncdb(name string, force bool, verbose bool) error {
BootStrap()
al := getDbAlias(name)
cmd := new(commandSyncDb)
cmd.al = al
cmd.force = force
cmd.noInfo = !verbose
cmd.verbose = verbose
cmd.rtOnError = true
return cmd.Run()
}

Some files were not shown because too many files have changed in this diff Show More