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

425 Commits

Author SHA1 Message Date
1c9898dee5 Merge branch 'develop' 2015-02-27 22:52:42 +08:00
2cf7c6a58a change the jQuery URL 2015-02-27 22:51:20 +08:00
2cee46ab2b change the jQuery URL 2015-02-27 22:50:25 +08:00
020bfbcc9c Merge branch 'develop' 2015-02-27 22:47:59 +08:00
3f8252bffd change the version from 1.4.2 to 1.4.3 2015-02-27 22:47:21 +08:00
6d313aa15f fix #985 2015-02-27 22:37:41 +08:00
2a4e2d4a71 delete the group route, because we already has namespace 2015-02-27 22:37:07 +08:00
f96a6285bf fix #978 2015-02-27 22:21:58 +08:00
e938876c4a fix the cycle import 2015-02-27 00:12:10 +08:00
6e9d2dc965 add more error functions 2015-02-26 23:49:24 +08:00
3aceaf8838 error support controller 2015-02-26 23:34:43 +08:00
71b9854f48 Merge pull request #1044 from fuxiaohei/develop
code style simplify
2015-02-23 22:25:34 +08:00
f59ccd3a35 Merge pull request #1043 from Hepri/develop
Added support to parse slices of ints and strings in ParseForm func
2015-02-23 21:58:32 +08:00
181a7c35fe code simplify for package middleware 2015-02-23 11:50:45 +08:00
2ed272aeb2 code simplify for package middleware 2015-02-23 11:50:13 +08:00
77c1109134 code simplify for package logs 2015-02-23 11:42:46 +08:00
29d4823866 code simplify for package httplib 2015-02-23 11:30:59 +08:00
24cf06d288 code style simplify for context package 2015-02-23 11:15:55 +08:00
0c31c2d689 Added support to parse slices of ints and strings in ParseForm func 2015-02-22 22:13:06 +05:00
f988f035e5 redis provider for session and cache support select db 2015-02-16 21:56:32 +08:00
1b4158c15b Merge pull request #1039 from astaxie/revert-1000-group_by_queryseter
Revert "Add GroupBy to QuerySeter"
2015-02-14 20:40:52 +08:00
433e8f2ce3 Revert "Add GroupBy to QuerySeter" 2015-02-14 20:40:43 +08:00
22ba7fdce4 Merge pull request #1000 from pdf/group_by_queryseter
Add GroupBy to QuerySeter
2015-02-14 20:33:06 +08:00
2a0f87e810 Merge pull request #1010 from BlackLee/master
add compare_not/not_nil methods for template
2015-02-14 20:31:18 +08:00
19db4b67f6 Merge pull request #1025 from kongjian/develop
Update task tpl
2015-02-14 20:29:28 +08:00
b1baf4503d beego task list update for task spec list and task run url error 2015-02-04 18:07:31 +08:00
d536f5b8dc Merge pull request #1021 from kmulvey/readme-typo
typos in the readme
2015-02-03 09:07:00 +08:00
1cc1d57f55 development 2015-02-02 09:33:59 -05:00
73370ade90 modular 2015-02-02 09:33:27 -05:00
0d3b7dcd07 Merge pull request #911 from supar/add-column-default-attribute
Add attribute default to the column on create or alter commands. Skip co...
2015-01-16 10:40:11 -08:00
d7fe5ef435 Merge pull request #1004 from fugr/patch-2
Transaction
2015-01-13 11:19:03 -08:00
8bd902814f Transaction
err处理写反了
2015-01-13 11:09:43 +08:00
378356a65e Merge pull request #1001 from johndeng/develop
Fixed the status code issue at error handler.
2015-01-12 09:54:26 -08:00
30871e2617 Fixed the status issue at error handler. 2015-01-10 17:35:35 +08:00
3731088b4a Add GroupBy to QuerySeter
Adds support for GroupBy to QuerySeter SELECT operations.
2015-01-10 15:26:41 +11:00
d46833c6d8 Merge pull request #997 from dockercn/master
增加session模块中的ledisdb的动态配置
2015-01-09 13:58:13 +08:00
18659e16ba add compare_not/not_nil methods for template 2015-01-05 16:38:57 +08:00
5d8187d005 Merge pull request #977 from athurg/patch-1
Fix RequestURI nil caused template parse failed
2014-12-25 11:40:26 +08:00
d961ae4cd8 Fix RequestURI nil caused template parse failed
Sometime RequestURI is not set, e.g. running after a front proxy server.

We should always follow the document's directive, to use Request.URL instead of RequestURI.

Refer: http://golang.org/pkg/net/http/#Request
2014-12-25 11:23:04 +08:00
0e1a0049d1 Merge pull request #971 from athurg/get_request_params_with_default
Support default value for controller’s params get
2014-12-19 16:03:10 +08:00
0c933643e2 improve the empty router 2014-12-19 15:33:51 +08:00
d2c5daa5ee Update comments for controller's GetXXX functions 2014-12-19 15:28:18 +08:00
d3ab157915 fix the cache test 2014-12-19 14:40:16 +08:00
75d28d49c5 Merge pull request #965 from shuoli84/develop
Fix subdomain, add test, space and comment fix
2014-12-19 13:22:36 +08:00
76bb4827d0 Merge pull request #953 from kristen1980/patch-2
Allow absolute path for filesystem cache
2014-12-18 21:24:17 +08:00
3caba06189 Merge pull request #967 from athurg/support_all_type_on_urlfor
Add all type support for UrlFor’s params
2014-12-18 21:14:44 +08:00
572508ddd8 Clean json config. Fix DefaultStrings 2014-12-17 17:02:46 +08:00
e34f8479bb Add all type support for UrlFor’s params 2014-12-17 15:52:48 +08:00
daf85f06f8 Support default value for controller’s params get 2014-12-17 15:23:11 +08:00
22671c524e Fix subdomain, add test, space and comment fix 2014-12-17 12:06:53 +08:00
ab99d5f1e2 Merge pull request #957 from athurg/patch-2
Fix paginator attributes cannot be modified bug
2014-12-11 19:59:50 +08:00
c52f634d9c Fix paginator attributes cannot be modified bug
We can only use SetPaginator to create a pagination.

After that, we always need to modify something, like the totalNum, perPageNum.

These change should be seen in the view.

So we should give the view a pointer than a object.
2014-12-11 16:42:50 +08:00
9c665afc04 improve the error tips 2014-12-08 14:57:45 +08:00
77ed151243 Allow absolute path for filesystem cache
Gives more flexibility by making it an absolute path. A relative path can easily be created by the user.
2014-12-07 10:00:35 -07:00
29d98731c6 add sess_ledis select db config 2014-11-25 14:41:51 -08:00
934dd2e8d2 Merge branch 'master' of https://github.com/astaxie/beego 2014-11-25 14:27:13 -08:00
e65d87974a Merge pull request #940 from hilyjiang/develop
make Content-Type header more human-readable
2014-11-24 23:26:15 +08:00
db04c3cbb4 make Content-Type header more human-readable 2014-11-24 23:12:09 +08:00
802aa16136 Merge pull request #935 from mnhkahn/master
beego1.4.2,beego.AppConfig.Strings与老版本代码不兼容问题
2014-11-24 21:47:12 +08:00
f2df07f630 Merge pull request #933 from rbastic/develop
Reword message about reloading packages..
2014-11-24 13:18:05 +08:00
dc89f844f3 Reword message about reloading packages.. 2014-11-23 18:22:45 +01:00
b80cdef20f Merge pull request #932 from DeanThompson/master
count log file lines
2014-11-23 23:57:27 +08:00
98dcee0643 Merge pull request #926 from xuewuhen/master
SubDomains function bugfixed
2014-11-23 22:57:40 +08:00
0ad75cb5fa Merge pull request #928 from lei-cao/develop
Return the response directly if it's a options PreflightHeader request
2014-11-21 23:22:59 +08:00
6a9d04c269 count log file lines 2014-11-21 18:12:39 +08:00
93ca11f83d Return the response directly if it's a options PreflightHeader request 2014-11-21 01:35:30 +08:00
git
d0b43ef4f5 beego.AppConfig.Strings bug 2014-11-20 16:35:04 +08:00
c9bb9d6a09 SubDomains function bugfixed 2014-11-18 22:54:48 +08:00
f96245786a fix #912 2014-11-08 15:10:47 +08:00
1a1b0c14b9 Add attribute default to the column on create or alter commands. Skip columns which are keys and date or time data type 2014-11-06 17:43:53 +03:00
07c628c7e9 fix the commentsRouter init sequence 2014-11-06 17:30:50 +08:00
1e92d17605 fix the repeat commentsRouters 2014-11-06 16:25:47 +08:00
bb795847da fix the not exist config file application 2014-11-06 11:12:00 +08:00
54ba307f7f change this to short name 2014-11-05 22:40:31 +08:00
950ff91d87 hotfix for parsefiel 2014-11-05 22:23:54 +08:00
b43401b9f6 Merge pull request #907 from astaxie/develop
1.4.1 released
2014-11-04 22:39:03 +08:00
fe50269b3f change 1.4.1 to 1.4.2 2014-11-04 22:38:40 +08:00
000033e2a7 update the test case 2014-11-04 22:07:38 +08:00
15242d89ce simple the session init 2014-11-04 19:08:06 +08:00
76522d43af simple the session 2014-11-04 19:07:49 +08:00
52df1234bd Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-11-04 19:04:43 +08:00
fc6b9ce009 fix #620 simple the sessionID generate 2014-11-04 19:04:26 +08:00
b9fdd67519 add test case fot date & stringbool 2014-11-04 16:39:17 +08:00
7743eecfd4 support #761
type Test struct {
    Date    time.Time    `form:"Date, 2006-01-02"`
    Save    bool            `form:"Save"`
}
2014-11-04 16:19:46 +08:00
c4d8e4a244 fix #759 2014-11-04 15:29:33 +08:00
9d4ec508bb parse for github.com replace the . to _ 2014-11-04 10:19:30 +08:00
8b747f54bc fix #770 2014-11-03 23:33:11 +08:00
a2428af8a7 compatibility for warn & info function add one more depth 2014-11-03 16:48:45 +08:00
90a7ce5c6a split the file for logs 2014-11-03 16:45:42 +08:00
ef3c7c127b fix the variable 2014-11-03 16:44:05 +08:00
88caf1ed70 if read the log.go then calldepth add 1 2014-11-03 16:43:07 +08:00
304beaf89f update the log call deep 2014-11-03 16:40:08 +08:00
2288ac868c remove the deep Caller 2014-11-03 16:34:36 +08:00
8d797a4a5e file the static filter 2014-11-03 16:14:40 +08:00
10db97b193 add some tips for the admin server start 2014-11-03 15:08:51 +08:00
716962672f fix #751
add config ListenTCP4

when user want to listen on the TCP4, because now almost use the ipv4.
but default lister on the ipv6
2014-11-03 15:06:25 +08:00
90cff5f042 fix #824 2014-11-02 21:01:51 +08:00
da127bbc22 fix #855 #859 2014-10-31 16:31:23 +08:00
945b1da3a8 fix the gofmt 2014-10-31 15:48:57 +08:00
94c84b846f fix the init logger 2014-10-31 09:02:16 +08:00
db43892fe6 improve the Put #896 2014-10-31 00:28:51 +08:00
71149218d1 fix the log level 2014-10-30 17:43:32 +08:00
1636a7271c Revert "fix the log test"
This reverts commit ddbfc25e56.
2014-10-30 16:57:55 +08:00
68c3bdfdd4 Revert "logs:default support fileline"
This reverts commit 1f26852610.
2014-10-30 16:57:48 +08:00
ecd0a5487e fix the import cycle not allowed 2014-10-30 16:12:54 +08:00
fda841208d fix #893 2014-10-30 16:05:48 +08:00
57e62e5e57 update the file upload to io.Pipe 2014-10-30 11:16:09 +08:00
824e3f8f5b fix the only file upload param 2014-10-29 16:00:08 +08:00
1822dd95ac Merge pull request #892 from dockercn/master
Add a beego session backend using LedisDB
2014-10-29 14:18:20 +08:00
ddbfc25e56 fix the log test 2014-10-28 19:34:11 +08:00
1f26852610 logs:default support fileline 2014-10-28 19:33:14 +08:00
0188fb3711 Merge pull request #1 from chliang2030598/master
add session store in ledis
2014-10-27 06:02:19 +00:00
0bcd828d73 add session store in ledis 2014-10-26 22:56:00 -07:00
e11a27f1d1 Merge pull request #888 from astaxie/revert-887-add-column-default-attribute
Revert "Add column default attribute"
2014-10-26 10:41:34 +08:00
90caeb4cf7 Revert "Add column default attribute" 2014-10-26 10:41:22 +08:00
6c9249034d Merge pull request #887 from supar/add-column-default-attribute
Add column default attribute
2014-10-26 10:28:56 +08:00
14114018ea config ini support include 2014-10-24 19:03:27 +08:00
6f5162461e Add column DEFAULT attribute. Do not add if field is key or in
relations.
2014-10-24 14:51:35 +04:00
c34c514bba Skip add DEFAULT if the field is in relations (rel or reverse) 2014-10-24 14:37:46 +04:00
8ac2b9bf66 Merge branch 'master' into develop 2014-10-24 15:10:11 +08:00
2a85c79ce5 Merge pull request #881 from astaxie/revert-870-add-column-default-attribute
Revert "Add column default attribute"
2014-10-24 14:58:27 +08:00
767083bd56 Revert "Add column default attribute" 2014-10-24 14:58:17 +08:00
1a79513293 Merge branch 'master' into develop 2014-10-24 14:50:53 +08:00
c6cb1f92e8 Merge pull request #870 from supar/add-column-default-attribute
Add column default attribute
2014-10-24 14:49:53 +08:00
9c0aad06c5 Merge pull request #880 from chenghuama/patch-3
Update ini.go
2014-10-24 14:15:49 +08:00
180c6aafac Update ini.go
支持BOM格式的ini文件
2014-10-24 13:45:00 +08:00
710f5b6234 fix the test fun for pull request 873 2014-10-20 22:23:29 +08:00
dbf944adce Merge pull request #873 from WithGJR/develop
add new feature to 'renderform' function, user could add HTML id and class now
2014-10-20 22:03:52 +08:00
6c9ff81fc1 fix: if user didn't set id or class, then it won't be displayed in HTML code 2014-10-20 18:59:46 +08:00
ec6383c07d Merge pull request #858 from bsingr/develop
Allow to use fastcgi via standard io.
2014-10-20 18:30:10 +08:00
76db5cded4 fix the init mime 2014-10-20 18:21:17 +08:00
1b3e7de463 add new feature to 'renderform' function, user could add HTML id and class now 2014-10-20 17:49:16 +08:00
ab28edaf25 Fix comma in the switch, fix wronf function name 2014-10-17 13:02:18 +04:00
04431a7a15 Fix function name fmt.Stprintf -> fmt.Sprintf 2014-10-17 12:59:24 +04:00
b00c42b3df Fix undefind variable fieldType 2014-10-17 12:56:44 +04:00
4cae7af3f9 Add attribute DEFAULT '' to the CREAT, ALTER constructors 2014-10-17 12:53:59 +04:00
e4988b714e Add property colDefault to fieldInfo object, set its true if there is
orm configuration default `orm:"default(1)"`
2014-10-17 12:27:53 +04:00
24489df63d Merge pull request #867 from WithGJR/develop
fix router bug: when the request is PUT or DELETE, router can't find the...
2014-10-16 23:07:20 +08:00
fb8e9ae1a3 Merge pull request #868 from reterVision/patch-2
Use SETEX command to set session
2014-10-16 21:42:23 +08:00
1eb9aef687 Use SETEX command to set session
In order to be compatible with older version Redis, use `SETEX` command instead of `SET x y EX 360`.
2014-10-16 20:16:17 +08:00
efc14a1e8d fix router bug with more better way 2014-10-16 18:58:12 +08:00
fa1281002e fix router bug: when the request is PUT or DELETE, router can't find the actual route and will throw 404 page to user 2014-10-16 18:26:01 +08:00
812950b60d Allow to use fastcgi via standard io. 2014-10-13 13:47:44 +02:00
f9e991b538 Merge pull request #853 from tossp/email
支持发送邮件内嵌附件
2014-10-12 11:02:53 +08:00
fc07419938 Update mail.go 2014-10-11 00:42:01 +08:00
d69eee23f0 添加错误返回
不知道英文区的人能否看懂Cnglish。。。
2014-10-11 00:38:31 +08:00
41de7c7db6 fix
修改一个错误。
看到text/template包的写法,和你的想法是一致的。
2014-10-11 00:02:36 +08:00
6a33647f30 修改参数类型
为了保持向后兼容,
2014-10-10 23:40:02 +08:00
e5134873be 支持发送邮件内嵌附件
为*Email.AttachFile和Email.Attach增加了一个参数"id".
当id不为空时,设置头部信息Content-Disposition为inline,并添加Content-ID头的值为id
2014-10-10 14:40:07 +08:00
8d20ea04b0 Merge pull request #852 from pabdavis/statistics-json
[Proposal] Ability to get statistics data unformatted
2014-10-10 10:59:30 +08:00
5c1e8e42b9 Reworked implementation to not return encoded json 2014-10-09 17:07:28 -04:00
ca3e7568a1 Add ability to get statistics in json format 2014-10-09 16:32:56 -04:00
2823167848 Merge pull request #849 from pabdavis/runmode-env
Support run mode set by environment variable
2014-10-10 00:50:16 +08:00
a27f5c0dc0 Remove dependency of third party lib 2014-10-09 09:17:10 -04:00
8af0475251 fix #833 2014-10-09 18:47:22 +08:00
9c07332cfc Update README.md 2014-10-09 09:34:22 +08:00
a06e0f27ad Support run mode set by env var BEEGO_RUNMODE 2014-10-08 15:00:07 -04:00
a760e46f98 Merge pull request #837 from bsingr/develop
Insert pagination utilities from beego/wetalk. Refs #835.
2014-10-08 23:00:46 +08:00
262665f4e5 Remove PaginationController interface and pass context instead. Refs #835. 2014-10-08 16:01:42 +02:00
0b3763cc67 Cleanup pagination documentation. Refs #835. 2014-10-08 16:00:00 +02:00
c147f26cd1 Merge pull request #847 from pabdavis/multifilter-fix2
Changes to handle multi filters on execution pt
2014-10-08 21:24:28 +08:00
1ba7847913 Changing check from nil to len based on slice 2014-10-08 09:21:34 -04:00
52df979aca update the godocs 2014-10-08 14:02:57 +08:00
b6f789c497 Changes to handle multi filters on execution pt 2014-10-07 16:35:30 -04:00
fa6cbc08d9 Document usage of utils/pagination. Refs #835. 2014-10-07 11:23:08 +02:00
c4f8f45da4 Move pagination to utils/pagination. Refs #837, #835. 2014-10-06 11:37:08 +02:00
6fca4a8218 Insert pagination utilities from beego/wetalk. Refs #835. 2014-10-02 11:40:46 +02:00
aae89576c6 fix #814 2014-10-01 22:31:44 +08:00
a907a86476 fix #814 2014-10-01 22:28:49 +08:00
8716185de8 fix #794 2014-10-01 22:10:33 +08:00
31e6133413 beego: improve static file index.html simple code 2014-10-01 08:57:10 +08:00
3a5de83ec2 beego: support router case sensitive 2014-09-28 22:10:43 +08:00
f5f3395560 update the isdir 2014-09-24 14:48:08 +08:00
8164367762 beego: flash add success & Set 2014-09-23 23:54:38 +08:00
e1475b72b9 Merge pull request #826 from SnailKnows/patch-2
Update beego.go
2014-09-23 00:31:21 +08:00
f267ee8a12 fix the same name controller for UrlFor 2014-09-23 00:26:07 +08:00
7e060e6e5c fix the static file dir 2014-09-23 00:03:47 +08:00
727d2f9ea1 fix not found when has mulit static dir
robot &robots
2014-09-22 23:44:50 +08:00
67c0c232a1 Update beego.go 2014-09-20 18:56:46 +08:00
6c62198b59 remove the go style 2014-09-15 23:01:12 +08:00
e48e1ddaa9 Merge pull request #812 from ZhengYang/develop
More SQL keywords added and code cleanup
2014-09-11 20:48:18 +08:00
1f9281c830 minor code refactor 2014-09-11 15:17:48 +08:00
ccab9a7044 add more sql keywords 2014-09-11 13:48:39 +08:00
9013f5c6c7 Merge pull request #808 from ZhengYang/develop
more complete support for sql language
2014-09-09 14:21:49 +08:00
29b7ff84e1 more complete support for sql language 2014-09-09 14:17:12 +08:00
fb0cc55822 update the orm read me 2014-09-09 11:55:28 +08:00
0820e21738 Merge pull request #804 from ZhengYang/develop
QueryBuilder for building SQL queries quickly
2014-09-08 19:24:39 +08:00
38eb29fa7b err msg spell correction 2014-09-08 18:41:42 +08:00
cca0a3f76d name correction: QueryBuilder instead of QueryWriter 2014-09-08 18:31:32 +08:00
f9a9b5a905 new query builder based on driver 2014-09-08 17:56:55 +08:00
c667895ce5 added new querybuilder 2014-09-08 17:47:15 +08:00
b2cdabb8a0 added query builder for orm 2014-09-08 17:37:01 +08:00
647a47517d httplib: fix the header function for User-Agent 2014-09-05 23:21:41 +08:00
f7cd1479ba beego: improve the log debug for running server 2014-09-05 17:04:02 +08:00
fcc359af11 beego: fix the Upper for the _method value 2014-09-04 22:13:03 +08:00
08b3e4191e Merge branch 'master' into develop 2014-09-04 21:58:45 +08:00
4f4f7ce257 beego: fix the router for *.* with other regexp 2014-09-04 21:58:17 +08:00
d06e02474f Merge pull request #795 from mvpmvh/context_params
Context params
2014-09-04 09:11:35 +08:00
4d65330ca1 changing my package namespace to astaxie 2014-09-03 19:47:09 -05:00
2dfe1fc61c Merge pull request #792 from haowang1013/develop
fixed uninitialized return error if StartAndGC fails
2014-09-03 23:21:17 +08:00
29b60d6058 fixed uninitialized return error if StartAndGC fails 2014-09-03 22:43:06 +08:00
6eee223352 beego: fix the Upper for the _method value 2014-09-03 09:25:34 +08:00
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
db51ddab96 GetInt(), GetInt8(), GetInt16(), GetInt32(), GetInt64() and Example tests 2014-08-23 20:24:29 -05:00
baf2c63d5c Merge branch 'astaxie-master' 2014-08-23 10:09:37 -05: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
175 changed files with 8857 additions and 4379 deletions

1
.gitignore vendored
View File

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

View File

@ -1,39 +1,37 @@
## beego ## Beego
[![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest) [![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest)
[![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego)
beego is a Go Framework inspired by tornado and sinatra. beego is an open-source, high-performance, modular, full-stack web framework.
It is a simple & powerful web framework.
More info [beego.me](http://beego.me) More info [beego.me](http://beego.me)
## Installation
go get github.com/astaxie/beego
## Features ## Features
* RESTful support * RESTful support
* MVC architecture * MVC architecture
* Session support (store in memory, file, Redis or MySQL) * Modularity
* Cache support (store in memory, Redis or Memcache) * Auto API documents
* Global Config * Annotation router
* Intelligent routing * Namespace
* Thread-safe map * Powerful development tools
* Friendly displaying of errors * Full stack for Web & API
* Useful template functions
## Documentation ## Documentation
[English](http://beego.me/docs/intro/) * [English](http://beego.me/docs/intro/)
* [中文文档](http://beego.me/docs/intro/)
[API](http://gowalker.org/github.com/astaxie/beego) ## Community
[中文文档](http://beego.me/docs/intro/)
* [http://beego.me/community](http://beego.me/community)
## LICENSE ## LICENSE
beego is licensed under the Apache Licence, Version 2.0 beego is licensed under the Apache Licence, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0.html). (http://www.apache.org/licenses/LICENSE-2.0.html).
[![Clone in Koding](http://learn.koding.com/btn/clone_d.png)][koding]
[koding]: https://koding.com/Teamwork?import=https://github.com/astaxie/beego/archive/master.zip&c=git1

413
admin.go
View File

@ -1,15 +1,25 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "text/template"
"time" "time"
"github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/toolbox"
@ -46,7 +56,6 @@ func init() {
beeAdminApp.Route("/prof", profIndex) beeAdminApp.Route("/prof", profIndex)
beeAdminApp.Route("/healthcheck", healthcheck) beeAdminApp.Route("/healthcheck", healthcheck)
beeAdminApp.Route("/task", taskStatus) beeAdminApp.Route("/task", taskStatus)
beeAdminApp.Route("/runtask", runTask)
beeAdminApp.Route("/listconf", listConf) beeAdminApp.Route("/listconf", listConf)
FilterMonitorFunc = func(string, string, time.Duration) bool { return true } FilterMonitorFunc = func(string, string, time.Duration) bool { return true }
} }
@ -54,22 +63,24 @@ func init() {
// AdminIndex is the default http.Handler for admin module. // AdminIndex is the default http.Handler for admin module.
// it matches url pattern "/". // it matches url pattern "/".
func adminIndex(rw http.ResponseWriter, r *http.Request) { func adminIndex(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte("<html><head><title>beego admin dashboard</title></head><body>")) tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
rw.Write([]byte("Welcome to Admin Dashboard<br>\n")) tmpl = template.Must(tmpl.Parse(indexTpl))
rw.Write([]byte("There are servral functions:<br>\n")) tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
rw.Write([]byte("1. Record all request and request time, <a href='/qps'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/qps</a><br>\n")) data := make(map[interface{}]interface{})
rw.Write([]byte("2. Get runtime profiling data by the pprof, <a href='/prof'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/prof</a><br>\n")) tmpl.Execute(rw, data)
rw.Write([]byte("3. Get healthcheck result from <a href='/healthcheck'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/healthcheck</a><br>\n"))
rw.Write([]byte("4. Get current task infomation from task <a href='/task'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/task</a><br> \n"))
rw.Write([]byte("5. To run a task passed a param <a href='/runtask'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/runtask</a><br>\n"))
rw.Write([]byte("6. Get all confige & router infomation <a href='/listconf'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/listconf</a><br>\n"))
rw.Write([]byte("</body></html>"))
} }
// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter. // 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. // it's registered with url pattern "/qbs" in admin module.
func qpsIndex(rw http.ResponseWriter, r *http.Request) { func qpsIndex(rw http.ResponseWriter, r *http.Request) {
toolbox.StatisticsMap.GetMap(rw) 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. // ListConf is the http.Handler of displaying all beego configuration values as key/value pair.
@ -78,112 +89,210 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
command := r.Form.Get("command") command := r.Form.Get("command")
if command != "" { if command != "" {
data := make(map[interface{}]interface{})
switch command { switch command {
case "conf": case "conf":
fmt.Fprintln(rw, "list all beego's conf:") m := make(map[string]interface{})
fmt.Fprintln(rw, "AppName:", AppName)
fmt.Fprintln(rw, "AppPath:", AppPath) m["AppName"] = AppName
fmt.Fprintln(rw, "AppConfigPath:", AppConfigPath) m["AppPath"] = AppPath
fmt.Fprintln(rw, "StaticDir:", StaticDir) m["AppConfigPath"] = AppConfigPath
fmt.Fprintln(rw, "StaticExtensionsToGzip:", StaticExtensionsToGzip) m["StaticDir"] = StaticDir
fmt.Fprintln(rw, "HttpAddr:", HttpAddr) m["StaticExtensionsToGzip"] = StaticExtensionsToGzip
fmt.Fprintln(rw, "HttpPort:", HttpPort) m["HttpAddr"] = HttpAddr
fmt.Fprintln(rw, "HttpTLS:", EnableHttpTLS) m["HttpPort"] = HttpPort
fmt.Fprintln(rw, "HttpCertFile:", HttpCertFile) m["HttpTLS"] = EnableHttpTLS
fmt.Fprintln(rw, "HttpKeyFile:", HttpKeyFile) m["HttpCertFile"] = HttpCertFile
fmt.Fprintln(rw, "RecoverPanic:", RecoverPanic) m["HttpKeyFile"] = HttpKeyFile
fmt.Fprintln(rw, "AutoRender:", AutoRender) m["RecoverPanic"] = RecoverPanic
fmt.Fprintln(rw, "ViewsPath:", ViewsPath) m["AutoRender"] = AutoRender
fmt.Fprintln(rw, "RunMode:", RunMode) m["ViewsPath"] = ViewsPath
fmt.Fprintln(rw, "SessionOn:", SessionOn) m["RunMode"] = RunMode
fmt.Fprintln(rw, "SessionProvider:", SessionProvider) m["SessionOn"] = SessionOn
fmt.Fprintln(rw, "SessionName:", SessionName) m["SessionProvider"] = SessionProvider
fmt.Fprintln(rw, "SessionGCMaxLifetime:", SessionGCMaxLifetime) m["SessionName"] = SessionName
fmt.Fprintln(rw, "SessionSavePath:", SessionSavePath) m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
fmt.Fprintln(rw, "SessionHashFunc:", SessionHashFunc) m["SessionSavePath"] = SessionSavePath
fmt.Fprintln(rw, "SessionHashKey:", SessionHashKey) m["SessionCookieLifeTime"] = SessionCookieLifeTime
fmt.Fprintln(rw, "SessionCookieLifeTime:", SessionCookieLifeTime) m["UseFcgi"] = UseFcgi
fmt.Fprintln(rw, "UseFcgi:", UseFcgi) m["MaxMemory"] = MaxMemory
fmt.Fprintln(rw, "MaxMemory:", MaxMemory) m["EnableGzip"] = EnableGzip
fmt.Fprintln(rw, "EnableGzip:", EnableGzip) m["DirectoryIndex"] = DirectoryIndex
fmt.Fprintln(rw, "DirectoryIndex:", DirectoryIndex) m["HttpServerTimeOut"] = HttpServerTimeOut
fmt.Fprintln(rw, "HttpServerTimeOut:", HttpServerTimeOut) m["ErrorsShow"] = ErrorsShow
fmt.Fprintln(rw, "ErrorsShow:", ErrorsShow) m["XSRFKEY"] = XSRFKEY
fmt.Fprintln(rw, "XSRFKEY:", XSRFKEY) m["EnableXSRF"] = EnableXSRF
fmt.Fprintln(rw, "EnableXSRF:", EnableXSRF) m["XSRFExpire"] = XSRFExpire
fmt.Fprintln(rw, "XSRFExpire:", XSRFExpire) m["CopyRequestBody"] = CopyRequestBody
fmt.Fprintln(rw, "CopyRequestBody:", CopyRequestBody) m["TemplateLeft"] = TemplateLeft
fmt.Fprintln(rw, "TemplateLeft:", TemplateLeft) m["TemplateRight"] = TemplateRight
fmt.Fprintln(rw, "TemplateRight:", TemplateRight) m["BeegoServerName"] = BeegoServerName
fmt.Fprintln(rw, "BeegoServerName:", BeegoServerName) m["EnableAdmin"] = EnableAdmin
fmt.Fprintln(rw, "EnableAdmin:", EnableAdmin) m["AdminHttpAddr"] = AdminHttpAddr
fmt.Fprintln(rw, "AdminHttpAddr:", AdminHttpAddr) m["AdminHttpPort"] = AdminHttpPort
fmt.Fprintln(rw, "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": case "router":
fmt.Fprintln(rw, "Print all router infomation:") 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 { for method, t := range BeeApp.Handlers.routers {
fmt.Fprintln(rw)
fmt.Fprintln(rw) resultList := new([][]string)
fmt.Fprintln(rw, " Method:", method)
printTree(rw, t) printTree(resultList, t)
methods = append(methods, method)
methodsData[method] = resultList
} }
// @todo print routers
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": case "filter":
fmt.Fprintln(rw, "Print all filter infomation:") 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 { if BeeApp.Handlers.enableFilter {
fmt.Fprintln(rw, "BeforeRouter:") var filterType string
if bf, ok := BeeApp.Handlers.filters[BeforeRouter]; ok { if bf, ok := BeeApp.Handlers.filters[BeforeRouter]; ok {
filterType = "Before Router"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf { for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
var result = []string{
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
} }
filterTypeData[filterType] = resultList
} }
fmt.Fprintln(rw, "BeforeExec:")
if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok { if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
filterType = "Before Exec"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf { for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
var result = []string{
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
} }
filterTypeData[filterType] = resultList
} }
fmt.Fprintln(rw, "AfterExec:")
if bf, ok := BeeApp.Handlers.filters[AfterExec]; ok { if bf, ok := BeeApp.Handlers.filters[AfterExec]; ok {
filterType = "After Exec"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf { for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
var result = []string{
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
} }
filterTypeData[filterType] = resultList
} }
fmt.Fprintln(rw, "FinishRouter:")
if bf, ok := BeeApp.Handlers.filters[FinishRouter]; ok { if bf, ok := BeeApp.Handlers.filters[FinishRouter]; ok {
filterType = "Finish Router"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf { for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
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: default:
rw.Write([]byte("command not support")) rw.Write([]byte("command not support"))
} }
} else { } else {
rw.Write([]byte("<html><head><title>beego admin dashboard</title></head><body>"))
rw.Write([]byte("ListConf support this command:<br>\n"))
rw.Write([]byte("1. <a href='?command=conf'>command=conf</a><br>\n"))
rw.Write([]byte("2. <a href='?command=router'>command=router</a><br>\n"))
rw.Write([]byte("3. <a href='?command=filter'>command=filter</a><br>\n"))
rw.Write([]byte("</body></html>"))
} }
} }
func printTree(rw http.ResponseWriter, t *Tree) { func printTree(resultList *[][]string, t *Tree) {
for _, tr := range t.fixrouters { for _, tr := range t.fixrouters {
printTree(rw, tr) printTree(resultList, tr)
} }
if t.wildcard != nil { if t.wildcard != nil {
printTree(rw, t.wildcard) printTree(resultList, t.wildcard)
} }
for _, l := range t.leaves { for _, l := range t.leaves {
if v, ok := l.runObject.(*controllerInfo); ok { if v, ok := l.runObject.(*controllerInfo); ok {
if v.routerType == routerTypeBeego { if v.routerType == routerTypeBeego {
fmt.Fprintln(rw, v.pattern, v.methods, v.controllerType.Name()) 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 { } else if v.routerType == routerTypeRESTFul {
fmt.Fprintln(rw, v.pattern, v.methods) var result = []string{
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf("%s", v.methods),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeHandler { } else if v.routerType == routerTypeHandler {
fmt.Fprintln(rw, v.pattern, "handler") var result = []string{
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf(""),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
} }
} }
} }
@ -194,58 +303,135 @@ func printTree(rw http.ResponseWriter, t *Tree) {
func profIndex(rw http.ResponseWriter, r *http.Request) { func profIndex(rw http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
command := r.Form.Get("command") command := r.Form.Get("command")
format := r.Form.Get("format")
data := make(map[string]interface{})
var result bytes.Buffer
if command != "" { if command != "" {
toolbox.ProcessInput(command, rw) 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 { } else {
rw.Write([]byte("<html><head><title>beego admin dashboard</title></head><body>"))
rw.Write([]byte("request url like '/prof?command=lookup goroutine'<br>\n"))
rw.Write([]byte("the command have below types:<br>\n"))
rw.Write([]byte("1. <a href='?command=lookup goroutine'>lookup goroutine</a><br>\n"))
rw.Write([]byte("2. <a href='?command=lookup heap'>lookup heap</a><br>\n"))
rw.Write([]byte("3. <a href='?command=lookup threadcreate'>lookup threadcreate</a><br>\n"))
rw.Write([]byte("4. <a href='?command=lookup block'>lookup block</a><br>\n"))
rw.Write([]byte("5. <a href='?command=start cpuprof'>start cpuprof</a><br>\n"))
rw.Write([]byte("6. <a href='?command=stop cpuprof'>stop cpuprof</a><br>\n"))
rw.Write([]byte("7. <a href='?command=get memprof'>get memprof</a><br>\n"))
rw.Write([]byte("8. <a href='?command=gc summary'>gc summary</a><br>\n"))
rw.Write([]byte("</body></html>"))
} }
} }
// Healthcheck is a http.Handler calling health checking and showing the result. // Healthcheck is a http.Handler calling health checking and showing the result.
// it's in "/healthcheck" pattern in admin module. // it's in "/healthcheck" pattern in admin module.
func healthcheck(rw http.ResponseWriter, req *http.Request) { 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 { for name, h := range toolbox.AdminCheckList {
if err := h.Check(); err != nil { if err := h.Check(); err != nil {
fmt.Fprintf(rw, "%s : %s\n", name, err.Error()) result = []string{
fmt.Sprintf("error"),
fmt.Sprintf("%s", name),
fmt.Sprintf("%s", err.Error()),
}
} else { } else {
fmt.Fprintf(rw, "%s : ok\n", name) 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). // TaskStatus is a http.Handler with running task status (task name, status and the last execution).
// it's in "/task" pattern in admin module. // it's in "/task" pattern in admin module.
func taskStatus(rw http.ResponseWriter, req *http.Request) { func taskStatus(rw http.ResponseWriter, req *http.Request) {
for tname, tk := range toolbox.AdminTaskList { data := make(map[interface{}]interface{})
fmt.Fprintf(rw, "%s:%s:%s", tname, tk.GetStatus(), tk.GetPrev().String())
}
}
// RunTask is a http.Handler to run a Task from the "query string. // Run Task
// the request url likes /runtask?taskname=sendmail.
func runTask(rw http.ResponseWriter, req *http.Request) {
req.ParseForm() req.ParseForm()
taskname := req.Form.Get("taskname") taskname := req.Form.Get("taskname")
if t, ok := toolbox.AdminTaskList[taskname]; ok { if taskname != "" {
err := t.Run()
if err != nil { if t, ok := toolbox.AdminTaskList[taskname]; ok {
fmt.Fprintf(rw, "%v", err) 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 <br>%s", taskname, t.GetStatus())}
} else {
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
} }
fmt.Fprintf(rw, "%s run success,Now the Status is %s", taskname, t.GetStatus())
} else {
fmt.Fprintf(rw, "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 Status"),
fmt.Sprintf("Last Time"),
fmt.Sprintf(""),
}
for tname, tk := range toolbox.AdminTaskList {
result = []string{
fmt.Sprintf("%s", tname),
fmt.Sprintf("%s", tk.GetSpec()),
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. // adminApp is an http.HandlerFunc map used as beeAdminApp.
@ -272,6 +458,7 @@ func (admin *adminApp) Run() {
for p, f := range admin.routers { for p, f := range admin.routers {
http.Handle(p, f) http.Handle(p, f)
} }
BeeLogger.Info("Admin server Running on %s", addr)
err := http.ListenAndServe(addr, nil) err := http.ListenAndServe(addr, nil)
if err != nil { if err != nil {
BeeLogger.Critical("Admin ListenAndServe: ", err) BeeLogger.Critical("Admin ListenAndServe: ", err)

355
adminui.go Normal file

File diff suppressed because one or more lines are too long

84
app.go
View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
@ -11,14 +19,12 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/fcgi" "net/http/fcgi"
"os"
"time" "time"
"github.com/astaxie/beego/context" "github.com/astaxie/beego/utils"
) )
// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)
// App defines beego application with a new PatternServeMux. // App defines beego application with a new PatternServeMux.
type App struct { type App struct {
Handlers *ControllerRegistor Handlers *ControllerRegistor
@ -40,8 +46,6 @@ func (app *App) Run() {
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort) addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
} }
BeeLogger.Info("Running on %s", addr)
var ( var (
err error err error
l net.Listener l net.Listener
@ -49,15 +53,28 @@ func (app *App) Run() {
endRunning := make(chan bool, 1) endRunning := make(chan bool, 1)
if UseFcgi { if UseFcgi {
if HttpPort == 0 { if UseStdIo {
l, err = net.Listen("unix", addr) err = fcgi.Serve(nil, app.Handlers) // standard I/O
if err == nil {
BeeLogger.Info("Use FCGI via standard I/O")
} else {
BeeLogger.Info("Cannot use FCGI via standard I/O", err)
}
} else { } else {
l, err = net.Listen("tcp", addr) if HttpPort == 0 {
// remove the Socket file before start
if utils.FileExists(addr) {
os.Remove(addr)
}
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)
} }
if err != nil {
BeeLogger.Critical("Listen: ", err)
}
err = fcgi.Serve(l, app.Handlers)
} else { } else {
app.Server.Addr = addr app.Server.Addr = addr
app.Server.Handler = app.Handlers app.Server.Handler = app.Handlers
@ -66,9 +83,11 @@ func (app *App) Run() {
if EnableHttpTLS { if EnableHttpTLS {
go func() { go func() {
time.Sleep(20 * time.Microsecond)
if HttpsPort != 0 { if HttpsPort != 0 {
app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
} }
BeeLogger.Info("https server Running on %s", app.Server.Addr)
err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
if err != nil { if err != nil {
BeeLogger.Critical("ListenAndServeTLS: ", err) BeeLogger.Critical("ListenAndServeTLS: ", err)
@ -80,11 +99,30 @@ func (app *App) Run() {
if EnableHttpListen { if EnableHttpListen {
go func() { go func() {
err := app.Server.ListenAndServe() app.Server.Addr = addr
if err != nil { BeeLogger.Info("http server Running on %s", app.Server.Addr)
BeeLogger.Critical("ListenAndServe: ", err) if ListenTCP4 && HttpAddr == "" {
time.Sleep(100 * time.Microsecond) ln, err := net.Listen("tcp4", app.Server.Addr)
endRunning <- true if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
err = app.Server.Serve(ln)
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
} else {
err := app.Server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
} }
}() }()
} }

137
beego.go
View File

@ -1,9 +1,28 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
import ( import (
@ -14,83 +33,15 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
) )
// beego web framework version. // beego web framework version.
const VERSION = "1.3.0" const VERSION = "1.4.3"
type hookfunc func() error //hook function to run type hookfunc func() error //hook function to run
var hooks []hookfunc //hook function slice to store the hookfunc var hooks []hookfunc //hook function slice to store the hookfunc
type groupRouter struct {
pattern string
controller ControllerInterface
mappingMethods string
}
// RouterGroups which will store routers
type GroupRouters []groupRouter
// Get a new GroupRouters
func NewGroupRouters() GroupRouters {
return make(GroupRouters, 0)
}
// 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],
}
} else {
newRG = groupRouter{
pattern,
c,
"",
}
}
*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. // Router adds a patterned controller handler to BeeApp.
// it's an alias method of App.Router. // it's an alias method of App.Router.
// usage: // usage:
@ -261,15 +212,6 @@ func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
return BeeApp 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. // SetViewsPath sets view directory path in beego application.
func SetViewsPath(path string) *App { func SetViewsPath(path string) *App {
ViewsPath = path ViewsPath = path
@ -289,15 +231,20 @@ func SetStaticPath(url string, path string) *App {
// DelStaticPath removes the static folder setting in this url pattern in beego application. // DelStaticPath removes the static folder setting in this url pattern in beego application.
func DelStaticPath(url string) *App { func DelStaticPath(url string) *App {
if !strings.HasPrefix(url, "/") {
url = "/" + url
}
url = strings.TrimRight(url, "/")
delete(StaticDir, url) delete(StaticDir, url)
return BeeApp return BeeApp
} }
// InsertFilter adds a FilterFunc with pattern condition and action constant. // InsertFilter adds a FilterFunc with pattern condition and action constant.
// The pos means action constant including // The pos means action constant including
// beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. // beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
func InsertFilter(pattern string, pos int, filter FilterFunc) *App { // The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
BeeApp.Handlers.InsertFilter(pattern, pos, filter) func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App {
BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...)
return BeeApp return BeeApp
} }
@ -340,6 +287,9 @@ func initBeforeHttpRun() {
} }
} }
//init mime
AddAPPStartHook(initMime)
// do hooks function // do hooks function
for _, hk := range hooks { for _, hk := range hooks {
err := hk() err := hk()
@ -354,11 +304,10 @@ func initBeforeHttpRun() {
if sessionConfig == "" { if sessionConfig == "" {
sessionConfig = `{"cookieName":"` + SessionName + `",` + sessionConfig = `{"cookieName":"` + SessionName + `",` +
`"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
`"providerConfig":"` + SessionSavePath + `",` + `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` +
`"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
`"sessionIDHashFunc":"` + SessionHashFunc + `",` +
`"sessionIDHashKey":"` + SessionHashKey + `",` +
`"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
`"domain":"` + SessionDomain + `",` +
`"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
} }
GlobalSessions, err = session.NewManager(SessionProvider, GlobalSessions, err = session.NewManager(SessionProvider,
@ -376,14 +325,10 @@ func initBeforeHttpRun() {
} }
} }
middleware.VERSION = VERSION registerDefaultErrorHandler()
middleware.AppName = AppName
middleware.RegisterErrorHandler()
for u, _ := range StaticDir {
Get(u+"/*", serverStaticRouter)
}
if EnableDocs { if EnableDocs {
Get("/docs", serverDocs)
Get("/docs/*", serverDocs) Get("/docs/*", serverDocs)
} }
} }
@ -404,6 +349,4 @@ func TestBeegoInit(apppath string) {
func init() { func init() {
hooks = make([]hookfunc, 0) hooks = make([]hookfunc, 0)
//init mime
AddAPPStartHook(initMime)
} }

2
cache/README.md vendored
View File

@ -22,7 +22,7 @@ First you must import it
Then init a Cache (example with 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:

55
cache/cache.go vendored
View File

@ -1,9 +1,33 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package cache
import ( import (
@ -13,7 +37,7 @@ import (
// Cache interface contains all behaviors for cache adapter. // Cache interface contains all behaviors for cache adapter.
// usage: // usage:
// cache.Register("file",cache.NewFileCache()) // this operation is run in init method of file.go. // cache.Register("file",cache.NewFileCache()) // this operation is run in init method of file.go.
// c := cache.NewCache("file","{....}") // c,err := cache.NewCache("file","{....}")
// c.Put("key",value,3600) // c.Put("key",value,3600)
// v := c.Get("key") // v := c.Get("key")
// //
@ -31,11 +55,11 @@ type Cache interface {
Incr(key string) error Incr(key string) error
// decrease cached int value by key, as a counter. // decrease cached int value by key, as a counter.
Decr(key string) error Decr(key string) error
// check cached value is existed or not. // check if cached value exists or not.
IsExist(key string) bool IsExist(key string) bool
// clear all cache. // clear all cache.
ClearAll() error ClearAll() error
// start gc routine via config string setting. // start gc routine based on config string settings.
StartAndGC(config string) error StartAndGC(config string) error
} }
@ -48,23 +72,24 @@ func Register(name string, adapter Cache) {
if adapter == nil { if adapter == nil {
panic("cache: Register adapter is 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) panic("cache: Register called twice for adapter " + name)
} }
adapters[name] = adapter adapters[name] = adapter
} }
// Create a new cache driver by adapter and config string. // Create a new cache driver by adapter name and config string.
// config need to be correct JSON as string: {"interval":360}. // config need to be correct JSON as string: {"interval":360}.
// it will start gc automatically. // it will start gc automatically.
func NewCache(adapterName, config string) (Cache, error) { func NewCache(adapterName, config string) (adapter Cache, err error) {
adapter, ok := adapters[adapterName] adapter, ok := adapters[adapterName]
if !ok { if !ok {
return nil, fmt.Errorf("cache: unknown adaptername %q (forgotten import?)", adapterName) err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
return
} }
err := adapter.StartAndGC(config) err = adapter.StartAndGC(config)
if err != nil { if err != nil {
return nil, err adapter = nil
} }
return adapter, nil return
} }

22
cache/cache_test.go vendored
View File

@ -1,12 +1,21 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package cache
import ( import (
"os"
"testing" "testing"
"time" "time"
) )
@ -59,7 +68,7 @@ func TestCache(t *testing.T) {
} }
func TestFileCache(t *testing.T) { func TestFileCache(t *testing.T) {
bm, err := NewCache("file", `{"CachePath":"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`) bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)
if err != nil { if err != nil {
t.Error("init err") t.Error("init err")
} }
@ -104,4 +113,5 @@ func TestFileCache(t *testing.T) {
if v := bm.Get("astaxie"); v.(string) != "author" { if v := bm.Get("astaxie"); v.(string) != "author" {
t.Error("get err") t.Error("get err")
} }
os.RemoveAll("cache")
} }

60
cache/conv.go vendored
View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package cache
@ -19,12 +27,11 @@ func GetString(v interface{}) string {
case []byte: case []byte:
return string(result) return string(result)
default: default:
if v == nil { if v != nil {
return ""
} else {
return fmt.Sprintf("%v", result) return fmt.Sprintf("%v", result)
} }
} }
return ""
} }
// convert interface to int. // convert interface to int.
@ -37,12 +44,9 @@ func GetInt(v interface{}) int {
case int64: case int64:
return int(result) return int(result)
default: default:
d := GetString(v) if d := GetString(v); d != "" {
if d != "" { value, _ := strconv.Atoi(d)
value, err := strconv.Atoi(d) return value
if err == nil {
return value
}
} }
} }
return 0 return 0
@ -58,12 +62,10 @@ func GetInt64(v interface{}) int64 {
case int64: case int64:
return result return result
default: default:
d := GetString(v)
if d != "" { if d := GetString(v); d != "" {
result, err := strconv.ParseInt(d, 10, 64) value, _ := strconv.ParseInt(d, 10, 64)
if err == nil { return value
return result
}
} }
} }
return 0 return 0
@ -75,12 +77,9 @@ func GetFloat64(v interface{}) float64 {
case float64: case float64:
return result return result
default: default:
d := GetString(v) if d := GetString(v); d != "" {
if d != "" { value, _ := strconv.ParseFloat(d, 64)
value, err := strconv.ParseFloat(d, 64) return value
if err == nil {
return value
}
} }
} }
return 0 return 0
@ -92,12 +91,9 @@ func GetBool(v interface{}) bool {
case bool: case bool:
return result return result
default: default:
d := GetString(v) if d := GetString(v); d != "" {
if d != "" { value, _ := strconv.ParseBool(d)
result, err := strconv.ParseBool(d) return value
if err == nil {
return result
}
} }
} }
return false return false

18
cache/conv_test.go vendored
View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package cache

136
cache/file.go vendored
View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package cache
@ -57,12 +65,10 @@ func NewFileCache() *FileCache {
// Start and begin gc for file cache. // Start and begin gc for file cache.
// the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0} // the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}
func (this *FileCache) StartAndGC(config string) error { func (fc *FileCache) StartAndGC(config string) error {
var cfg map[string]string var cfg map[string]string
json.Unmarshal([]byte(config), &cfg) json.Unmarshal([]byte(config), &cfg)
//fmt.Println(cfg)
//fmt.Println(config)
if _, ok := cfg["CachePath"]; !ok { if _, ok := cfg["CachePath"]; !ok {
cfg["CachePath"] = FileCachePath cfg["CachePath"] = FileCachePath
} }
@ -75,69 +81,51 @@ func (this *FileCache) StartAndGC(config string) error {
if _, ok := cfg["EmbedExpiry"]; !ok { if _, ok := cfg["EmbedExpiry"]; !ok {
cfg["EmbedExpiry"] = strconv.FormatInt(FileCacheEmbedExpiry, 10) cfg["EmbedExpiry"] = strconv.FormatInt(FileCacheEmbedExpiry, 10)
} }
this.CachePath = cfg["CachePath"] fc.CachePath = cfg["CachePath"]
this.FileSuffix = cfg["FileSuffix"] fc.FileSuffix = cfg["FileSuffix"]
this.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"]) fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"])
this.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"]) fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"])
this.Init() fc.Init()
return nil return nil
} }
// Init will make new dir for file cache if not exist. // Init will make new dir for file cache if not exist.
func (this *FileCache) Init() { func (fc *FileCache) Init() {
app := filepath.Dir(os.Args[0]) if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
this.CachePath = filepath.Join(app, this.CachePath) _ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
ok, err := exists(this.CachePath)
if err != nil { // print error
//fmt.Println(err)
} }
if !ok {
if err = os.Mkdir(this.CachePath, os.ModePerm); err != nil {
//fmt.Println(err);
}
}
//fmt.Println(this.getCacheFileName("123456"));
} }
// get cached file name. it's md5 encoded. // get cached file name. it's md5 encoded.
func (this *FileCache) getCacheFileName(key string) string { func (fc *FileCache) getCacheFileName(key string) string {
m := md5.New() m := md5.New()
io.WriteString(m, key) io.WriteString(m, key)
keyMd5 := hex.EncodeToString(m.Sum(nil)) keyMd5 := hex.EncodeToString(m.Sum(nil))
cachePath := this.CachePath cachePath := fc.CachePath
//fmt.Println("cachepath : " , cachePath) switch fc.DirectoryLevel {
//fmt.Println("md5" , keyMd5);
switch this.DirectoryLevel {
case 2: case 2:
cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4]) cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4])
case 1: case 1:
cachePath = filepath.Join(cachePath, keyMd5[0:2]) cachePath = filepath.Join(cachePath, keyMd5[0:2])
} }
ok, err := exists(cachePath) if ok, _ := exists(cachePath); !ok { // todo : error handle
if err != nil { _ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle
//fmt.Println(err)
} }
if !ok {
if err = os.MkdirAll(cachePath, os.ModePerm); err != nil { return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix))
//fmt.Println(err);
}
}
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, this.FileSuffix))
} }
// Get value from file cache. // Get value from file cache.
// if non-exist or expired, return empty string. // if non-exist or expired, return empty string.
func (this *FileCache) Get(key string) interface{} { func (fc *FileCache) Get(key string) interface{} {
filename := this.getCacheFileName(key) fileData, err := File_get_contents(fc.getCacheFileName(key))
filedata, err := File_get_contents(filename)
//fmt.Println("get length:" , len(filedata));
if err != nil { if err != nil {
return "" return ""
} }
var to FileCacheItem var to FileCacheItem
Gob_decode(filedata, &to) Gob_decode(fileData, &to)
if to.Expired < time.Now().Unix() { if to.Expired < time.Now().Unix() {
return "" return ""
} }
@ -147,12 +135,10 @@ func (this *FileCache) Get(key string) interface{} {
// Put value into file cache. // Put value into file cache.
// timeout means how long to keep this file, unit of ms. // timeout means how long to keep this file, unit of ms.
// if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever. // if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever.
func (this *FileCache) Put(key string, val interface{}, timeout int64) error { func (fc *FileCache) Put(key string, val interface{}, timeout int64) error {
gob.Register(val) gob.Register(val)
filename := this.getCacheFileName(key) item := FileCacheItem{Data: val}
var item FileCacheItem
item.Data = val
if timeout == FileCacheEmbedExpiry { if timeout == FileCacheEmbedExpiry {
item.Expired = time.Now().Unix() + (86400 * 365 * 10) // ten years item.Expired = time.Now().Unix() + (86400 * 365 * 10) // ten years
} else { } else {
@ -163,13 +149,12 @@ func (this *FileCache) Put(key string, val interface{}, timeout int64) error {
if err != nil { if err != nil {
return err return err
} }
err = File_put_contents(filename, data) return File_put_contents(fc.getCacheFileName(key), data)
return err
} }
// Delete file cache value. // Delete file cache value.
func (this *FileCache) Delete(key string) error { func (fc *FileCache) Delete(key string) error {
filename := this.getCacheFileName(key) filename := fc.getCacheFileName(key)
if ok, _ := exists(filename); ok { if ok, _ := exists(filename); ok {
return os.Remove(filename) return os.Remove(filename)
} }
@ -177,44 +162,41 @@ func (this *FileCache) Delete(key string) error {
} }
// Increase cached int value. // Increase cached int value.
// this value is saving forever unless Delete. // fc value is saving forever unless Delete.
func (this *FileCache) Incr(key string) error { func (fc *FileCache) Incr(key string) error {
data := this.Get(key) data := fc.Get(key)
var incr int var incr int
//fmt.Println(reflect.TypeOf(data).Name())
if reflect.TypeOf(data).Name() != "int" { if reflect.TypeOf(data).Name() != "int" {
incr = 0 incr = 0
} else { } else {
incr = data.(int) + 1 incr = data.(int) + 1
} }
this.Put(key, incr, FileCacheEmbedExpiry) fc.Put(key, incr, FileCacheEmbedExpiry)
return nil return nil
} }
// Decrease cached int value. // Decrease cached int value.
func (this *FileCache) Decr(key string) error { func (fc *FileCache) Decr(key string) error {
data := this.Get(key) data := fc.Get(key)
var decr int var decr int
if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 { if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 {
decr = 0 decr = 0
} else { } else {
decr = data.(int) - 1 decr = data.(int) - 1
} }
this.Put(key, decr, FileCacheEmbedExpiry) fc.Put(key, decr, FileCacheEmbedExpiry)
return nil return nil
} }
// Check value is exist. // Check value is exist.
func (this *FileCache) IsExist(key string) bool { func (fc *FileCache) IsExist(key string) bool {
filename := this.getCacheFileName(key) ret, _ := exists(fc.getCacheFileName(key))
ret, _ := exists(filename)
return ret return ret
} }
// Clean cached files. // Clean cached files.
// not implemented. // not implemented.
func (this *FileCache) ClearAll() error { func (fc *FileCache) ClearAll() error {
//this.CachePath
return nil return nil
} }
@ -232,22 +214,22 @@ func exists(path string) (bool, error) {
// Get bytes to file. // Get bytes to file.
// if non-exist, create this file. // if non-exist, create this file.
func File_get_contents(filename string) ([]byte, error) { func File_get_contents(filename string) (data []byte, e error) {
f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm) f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil { if e != nil {
return []byte(""), err return
} }
defer f.Close() defer f.Close()
stat, err := f.Stat() stat, e := f.Stat()
if err != nil { if e != nil {
return []byte(""), err return
} }
data := make([]byte, stat.Size()) data = make([]byte, stat.Size())
result, err := f.Read(data) result, e := f.Read(data)
if int64(result) == stat.Size() { if e != nil || int64(result) != stat.Size() {
return data, err return nil, e
} }
return []byte(""), err return
} }
// Put bytes to file. // Put bytes to file.

View File

@ -1,24 +1,48 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 // 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 ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"strings"
"github.com/beego/memcache" "github.com/bradfitz/gomemcache/memcache"
"github.com/astaxie/beego/cache" "github.com/astaxie/beego/cache"
) )
// Memcache adapter. // Memcache adapter.
type MemcacheCache struct { type MemcacheCache struct {
c *memcache.Connection conn *memcache.Client
conninfo string conninfo []string
} }
// create new memcache adapter. // create new memcache adapter.
@ -28,32 +52,21 @@ func NewMemCache() *MemcacheCache {
// get value from memcache. // get value from memcache.
func (rc *MemcacheCache) Get(key string) interface{} { func (rc *MemcacheCache) Get(key string) interface{} {
if rc.c == nil { if rc.conn == nil {
var err error if err := rc.connectInit(); err != nil {
rc.c, err = rc.connectInit()
if err != nil {
return err return err
} }
} }
v, err := rc.c.Get(key) if item, err := rc.conn.Get(key); err == nil {
if err != nil { return string(item.Value)
return nil
} }
var contain interface{} return nil
if len(v) > 0 {
contain = string(v[0].Value)
} else {
contain = nil
}
return contain
} }
// put value to memcache. only support string. // put value to memcache. only support string.
func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error { func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error {
if rc.c == nil { if rc.conn == nil {
var err error if err := rc.connectInit(); err != nil {
rc.c, err = rc.connectInit()
if err != nil {
return err return err
} }
} }
@ -61,69 +74,64 @@ func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error {
if !ok { if !ok {
return errors.New("val must string") return errors.New("val must string")
} }
stored, err := rc.c.Set(key, 0, uint64(timeout), []byte(v)) item := memcache.Item{Key: key, Value: []byte(v), Expiration: int32(timeout)}
if err == nil && stored == false { return rc.conn.Set(&item)
return errors.New("stored fail")
}
return err
} }
// delete value in memcache. // delete value in memcache.
func (rc *MemcacheCache) Delete(key string) error { func (rc *MemcacheCache) Delete(key string) error {
if rc.c == nil { if rc.conn == nil {
var err error if err := rc.connectInit(); err != nil {
rc.c, err = rc.connectInit()
if err != nil {
return err return err
} }
} }
_, err := rc.c.Delete(key) 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 return err
} }
// [Not Support]
// increase counter.
func (rc *MemcacheCache) Incr(key string) error {
return errors.New("not support in memcache")
}
// [Not Support]
// decrease counter. // decrease counter.
func (rc *MemcacheCache) Decr(key string) error { func (rc *MemcacheCache) Decr(key string) error {
return errors.New("not support in memcache") 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. // check value exists in memcache.
func (rc *MemcacheCache) IsExist(key string) bool { func (rc *MemcacheCache) IsExist(key string) bool {
if rc.c == nil { if rc.conn == nil {
var err error if err := rc.connectInit(); err != nil {
rc.c, err = rc.connectInit()
if err != nil {
return false return false
} }
} }
v, err := rc.c.Get(key) _, err := rc.conn.Get(key)
if err != nil { if err != nil {
return false return false
} }
if len(v) == 0 { return true
return false
} else {
return true
}
} }
// clear all cached in memcache. // clear all cached in memcache.
func (rc *MemcacheCache) ClearAll() error { func (rc *MemcacheCache) ClearAll() error {
if rc.c == nil { if rc.conn == nil {
var err error if err := rc.connectInit(); err != nil {
rc.c, err = rc.connectInit()
if err != nil {
return err return err
} }
} }
err := rc.c.FlushAll() return rc.conn.FlushAll()
return err
} }
// start memcache adapter. // start memcache adapter.
@ -135,24 +143,19 @@ func (rc *MemcacheCache) StartAndGC(config string) error {
if _, ok := cf["conn"]; !ok { if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key") return errors.New("config has no conn key")
} }
rc.conninfo = cf["conn"] rc.conninfo = strings.Split(cf["conn"], ";")
var err error if rc.conn == nil {
if rc.c != nil { if err := rc.connectInit(); err != nil {
rc.c, err = rc.connectInit() return err
if err != nil {
return errors.New("dial tcp conn error")
} }
} }
return nil return nil
} }
// connect to memcache and keep the connection. // connect to memcache and keep the connection.
func (rc *MemcacheCache) connectInit() (*memcache.Connection, error) { func (rc *MemcacheCache) connectInit() error {
c, err := memcache.Connect(rc.conninfo) rc.conn = memcache.New(rc.conninfo...)
if err != nil { return nil
return nil, err
}
return c, nil
} }
func init() { func init() {

42
cache/memory.go vendored
View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package cache
@ -46,15 +54,14 @@ func NewMemoryCache() *MemoryCache {
func (bc *MemoryCache) Get(name string) interface{} { func (bc *MemoryCache) Get(name string) interface{} {
bc.lock.RLock() bc.lock.RLock()
defer bc.lock.RUnlock() defer bc.lock.RUnlock()
itm, ok := bc.items[name] if itm, ok := bc.items[name]; ok {
if !ok { if (time.Now().Unix() - itm.Lastaccess.Unix()) > itm.expired {
return nil go bc.Delete(name)
return nil
}
return itm.val
} }
if (time.Now().Unix() - itm.Lastaccess.Unix()) > itm.expired { return nil
go bc.Delete(name)
return nil
}
return itm.val
} }
// Put cache to memory. // Put cache to memory.
@ -62,12 +69,11 @@ func (bc *MemoryCache) Get(name string) interface{} {
func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error { func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error {
bc.lock.Lock() bc.lock.Lock()
defer bc.lock.Unlock() defer bc.lock.Unlock()
t := MemoryItem{ bc.items[name] = &MemoryItem{
val: value, val: value,
Lastaccess: time.Now(), Lastaccess: time.Now(),
expired: expired, expired: expired,
} }
bc.items[name] = &t
return nil return nil
} }
@ -79,8 +85,7 @@ func (bc *MemoryCache) Delete(name string) error {
return errors.New("key not exist") return errors.New("key not exist")
} }
delete(bc.items, name) delete(bc.items, name)
_, valid := bc.items[name] if _, ok := bc.items[name]; ok {
if valid {
return errors.New("delete key error") return errors.New("delete key error")
} }
return nil return nil
@ -211,8 +216,7 @@ func (bc *MemoryCache) item_expired(name string) bool {
if !ok { if !ok {
return true return true
} }
sec := time.Now().Unix() - itm.Lastaccess.Unix() if time.Now().Unix()-itm.Lastaccess.Unix() >= itm.expired {
if sec >= itm.expired {
delete(bc.items, name) delete(bc.items, name)
return true return true
} }

117
cache/redis/redis.go vendored
View File

@ -1,17 +1,41 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 // 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 ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"strconv"
"time" "time"
"github.com/beego/redigo/redis" "github.com/garyburd/redigo/redis"
"github.com/astaxie/beego/cache" "github.com/astaxie/beego/cache"
) )
@ -25,6 +49,7 @@ var (
type RedisCache struct { type RedisCache struct {
p *redis.Pool // redis connection pool p *redis.Pool // redis connection pool
conninfo string conninfo string
dbNum int
key string key string
} }
@ -43,57 +68,78 @@ func (rc *RedisCache) do(commandName string, args ...interface{}) (reply interfa
// Get cache from redis. // Get cache from redis.
func (rc *RedisCache) Get(key string) interface{} { func (rc *RedisCache) Get(key string) interface{} {
v, err := rc.do("HGET", rc.key, key) if v, err := rc.do("GET", key); err == nil {
if err != nil { return v
return nil
} }
return nil
return v
} }
// put cache to redis. // put cache to redis.
// timeout is ignored.
func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error {
_, err := rc.do("HSET", rc.key, key, val) var err error
if _, err = rc.do("SETEX", key, timeout, val); err != nil {
return err
}
if _, err = rc.do("HSET", rc.key, key, true); err != nil {
return err
}
return err return err
} }
// delete cache in redis. // delete cache in redis.
func (rc *RedisCache) Delete(key string) error { func (rc *RedisCache) Delete(key string) error {
_, err := rc.do("HDEL", rc.key, key) var err error
if _, err = rc.do("DEL", key); err != nil {
return err
}
_, err = rc.do("HDEL", rc.key, key)
return err return err
} }
// check cache exist in redis. // check cache's existence in redis.
func (rc *RedisCache) IsExist(key string) bool { func (rc *RedisCache) IsExist(key string) bool {
v, err := redis.Bool(rc.do("HEXISTS", rc.key, key)) v, err := redis.Bool(rc.do("EXISTS", key))
if err != nil { if err != nil {
return false return false
} }
if v == false {
if _, err = rc.do("HDEL", rc.key, key); err != nil {
return false
}
}
return v return v
} }
// increase counter in redis. // increase counter in redis.
func (rc *RedisCache) Incr(key string) error { func (rc *RedisCache) Incr(key string) error {
_, err := redis.Bool(rc.do("HINCRBY", rc.key, key, 1)) _, err := redis.Bool(rc.do("INCRBY", key, 1))
return err return err
} }
// decrease counter in redis. // decrease counter in redis.
func (rc *RedisCache) Decr(key string) error { func (rc *RedisCache) Decr(key string) error {
_, err := redis.Bool(rc.do("HINCRBY", rc.key, key, -1)) _, err := redis.Bool(rc.do("INCRBY", key, -1))
return err return err
} }
// clean all cache in redis. delete this redis collection. // clean all cache in redis. delete this redis collection.
func (rc *RedisCache) ClearAll() error { func (rc *RedisCache) ClearAll() error {
_, err := rc.do("DEL", rc.key) 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 return err
} }
// start redis cache adapter. // start redis cache adapter.
// config is like {"key":"collection key","conn":"connection info"} // config is like {"key":"collection key","conn":"connection info","dbNum":"0"}
// the cache item in redis are stored forever, // the cache item in redis are stored forever,
// so no gc operation. // so no gc operation.
func (rc *RedisCache) StartAndGC(config string) error { func (rc *RedisCache) StartAndGC(config string) error {
@ -107,33 +153,36 @@ func (rc *RedisCache) StartAndGC(config string) error {
if _, ok := cf["conn"]; !ok { if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key") return errors.New("config has no conn key")
} }
if _, ok := cf["dbNum"]; !ok {
cf["dbNum"] = "0"
}
rc.key = cf["key"] rc.key = cf["key"]
rc.conninfo = cf["conn"] rc.conninfo = cf["conn"]
rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
rc.connectInit() rc.connectInit()
c := rc.p.Get() c := rc.p.Get()
defer c.Close() defer c.Close()
if err := c.Err(); err != nil {
return err
}
return nil return c.Err()
} }
// connect to redis. // connect to redis.
func (rc *RedisCache) connectInit() { func (rc *RedisCache) connectInit() {
dialFunc := func() (c redis.Conn, err error) {
c, err = redis.Dial("tcp", rc.conninfo)
_, selecterr := c.Do("SELECT", rc.dbNum)
if selecterr != nil {
c.Close()
return nil, selecterr
}
return
}
// initialize a new pool // initialize a new pool
rc.p = &redis.Pool{ rc.p = &redis.Pool{
MaxIdle: 3, MaxIdle: 3,
IdleTimeout: 180 * time.Second, IdleTimeout: 180 * time.Second,
Dial: func() (redis.Conn, error) { Dial: dialFunc,
c, err := redis.Dial("tcp", rc.conninfo)
if err != nil {
return nil, err
}
return c, nil
},
} }
} }

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

542
config.go
View File

@ -1,13 +1,20 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
import ( import (
"errors"
"fmt" "fmt"
"html/template" "html/template"
"os" "os"
@ -33,6 +40,7 @@ var (
EnableHttpListen bool EnableHttpListen bool
HttpAddr string HttpAddr string
HttpPort int HttpPort int
ListenTCP4 bool
EnableHttpTLS bool EnableHttpTLS bool
HttpsPort int HttpsPort int
HttpCertFile string HttpCertFile string
@ -40,19 +48,19 @@ var (
RecoverPanic bool // flag of auto recover panic RecoverPanic bool // flag of auto recover panic
AutoRender bool // flag of render template automatically AutoRender bool // flag of render template automatically
ViewsPath string ViewsPath string
RunMode string // run mode, "dev" or "prod" AppConfig *beegoAppConfig
AppConfig config.ConfigContainer RunMode string // run mode, "dev" or "prod"
GlobalSessions *session.Manager // global session mananger GlobalSessions *session.Manager // global session mananger
SessionOn bool // flag of starting session auto. default is false. SessionOn bool // flag of starting session auto. default is false.
SessionProvider string // default session provider, memory, mysql , redis ,etc. SessionProvider string // default session provider, memory, mysql , redis ,etc.
SessionName string // the cookie name when saving session id into cookie. SessionName string // the cookie name when saving session id into cookie.
SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session. SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session.
SessionSavePath string // if use mysql/redis/file provider, define save path to connection info. 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. SessionCookieLifeTime int // the life time of session id in cookie.
SessionAutoSetCookie bool // auto setcookie SessionAutoSetCookie bool // auto setcookie
SessionDomain string // the cookie domain default is empty
UseFcgi bool UseFcgi bool
UseStdIo bool
MaxMemory int64 MaxMemory int64
EnableGzip bool // flag of enable gzip EnableGzip bool // flag of enable gzip
DirectoryIndex bool // flag of display directory index. default is false. DirectoryIndex bool // flag of display directory index. default is false.
@ -72,8 +80,111 @@ var (
FlashSeperator string // used to seperate flash key:value FlashSeperator string // used to seperate flash key:value
AppConfigProvider string // config provider AppConfigProvider string // config provider
EnableDocs bool // enable generate docs & server docs API Swagger EnableDocs bool // enable generate docs & server docs API Swagger
RouterCaseSensitive bool // router case sensitive default is true
AccessLogs bool // print access logs, default is false
) )
type beegoAppConfig struct {
innerConfig config.ConfigContainer
}
func newAppConfig(AppConfigProvider, AppConfigPath string) (*beegoAppConfig, error) {
ac, err := config.NewConfig(AppConfigProvider, AppConfigPath)
if err != nil {
return nil, err
}
rac := &beegoAppConfig{ac}
return rac, nil
}
func (b *beegoAppConfig) Set(key, val string) error {
return b.innerConfig.Set(key, val)
}
func (b *beegoAppConfig) String(key string) string {
v := b.innerConfig.String(RunMode + "::" + key)
if v == "" {
return b.innerConfig.String(key)
}
return v
}
func (b *beegoAppConfig) Strings(key string) []string {
v := b.innerConfig.Strings(RunMode + "::" + key)
if v[0] == "" {
return b.innerConfig.Strings(key)
}
return v
}
func (b *beegoAppConfig) Int(key string) (int, error) {
v, err := b.innerConfig.Int(RunMode + "::" + key)
if err != nil {
return b.innerConfig.Int(key)
}
return v, nil
}
func (b *beegoAppConfig) Int64(key string) (int64, error) {
v, err := b.innerConfig.Int64(RunMode + "::" + key)
if err != nil {
return b.innerConfig.Int64(key)
}
return v, nil
}
func (b *beegoAppConfig) Bool(key string) (bool, error) {
v, err := b.innerConfig.Bool(RunMode + "::" + key)
if err != nil {
return b.innerConfig.Bool(key)
}
return v, nil
}
func (b *beegoAppConfig) Float(key string) (float64, error) {
v, err := b.innerConfig.Float(RunMode + "::" + key)
if err != nil {
return b.innerConfig.Float(key)
}
return v, nil
}
func (b *beegoAppConfig) DefaultString(key string, defaultval string) string {
return b.innerConfig.DefaultString(key, defaultval)
}
func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string {
return b.innerConfig.DefaultStrings(key, defaultval)
}
func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int {
return b.innerConfig.DefaultInt(key, defaultval)
}
func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 {
return b.innerConfig.DefaultInt64(key, defaultval)
}
func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool {
return b.innerConfig.DefaultBool(key, defaultval)
}
func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 {
return b.innerConfig.DefaultFloat(key, defaultval)
}
func (b *beegoAppConfig) DIY(key string) (interface{}, error) {
return b.innerConfig.DIY(key)
}
func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) {
return b.innerConfig.GetSection(section)
}
func (b *beegoAppConfig) SaveConfigFile(filename string) error {
return b.innerConfig.SaveConfigFile(filename)
}
func init() { func init() {
// create beego application // create beego application
BeeApp = NewApp() BeeApp = NewApp()
@ -125,12 +236,11 @@ func init() {
SessionName = "beegosessionID" SessionName = "beegosessionID"
SessionGCMaxLifetime = 3600 SessionGCMaxLifetime = 3600
SessionSavePath = "" SessionSavePath = ""
SessionHashFunc = "sha1"
SessionHashKey = "beegoserversessionkey"
SessionCookieLifeTime = 0 //set cookie default is the brower life SessionCookieLifeTime = 0 //set cookie default is the brower life
SessionAutoSetCookie = true SessionAutoSetCookie = true
UseFcgi = false UseFcgi = false
UseStdIo = false
MaxMemory = 1 << 26 //64MB MaxMemory = 1 << 26 //64MB
@ -155,6 +265,8 @@ func init() {
FlashName = "BEEGO_FLASH" FlashName = "BEEGO_FLASH"
FlashSeperator = "BEEGOFLASH" FlashSeperator = "BEEGOFLASH"
RouterCaseSensitive = true
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
// init BeeLogger // init BeeLogger
@ -163,251 +275,211 @@ func init() {
if err != nil { if err != nil {
fmt.Println("init console log error:", err) fmt.Println("init console log error:", err)
} }
SetLogFuncCall(true)
err = ParseConfig() err = ParseConfig()
if err != nil && !os.IsNotExist(err) { if err != nil && os.IsNotExist(err) {
// for init if doesn't have app.conf will not panic // for init if doesn't have app.conf will not panic
Info(err) ac := config.NewFakeConfig()
AppConfig = &beegoAppConfig{ac}
Warning(err)
} }
} }
// ParseConfig parsed default config file. // ParseConfig parsed default config file.
// now only support ini, next will support json. // now only support ini, next will support json.
func ParseConfig() (err error) { func ParseConfig() (err error) {
AppConfig, err = config.NewConfig(AppConfigProvider, AppConfigPath) AppConfig, err = newAppConfig(AppConfigProvider, AppConfigPath)
if err != nil { if err != nil {
AppConfig = config.NewFakeConfig()
return err return err
} else { }
envRunMode := os.Getenv("BEEGO_RUNMODE")
// set the runmode first
if envRunMode != "" {
RunMode = envRunMode
} else if runmode := AppConfig.String("RunMode"); runmode != "" {
RunMode = runmode
}
if v, err := getConfig("string", "HttpAddr"); err == nil { HttpAddr = AppConfig.String("HttpAddr")
HttpAddr = v.(string)
if v, err := AppConfig.Int("HttpPort"); err == nil {
HttpPort = v
}
if v, err := AppConfig.Bool("ListenTCP4"); err == nil {
ListenTCP4 = v
}
if v, err := AppConfig.Bool("EnableHttpListen"); err == nil {
EnableHttpListen = v
}
if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil {
MaxMemory = maxmemory
}
if appname := AppConfig.String("AppName"); appname != "" {
AppName = appname
}
if autorender, err := AppConfig.Bool("AutoRender"); err == nil {
AutoRender = autorender
}
if autorecover, err := AppConfig.Bool("RecoverPanic"); err == nil {
RecoverPanic = autorecover
}
if views := AppConfig.String("ViewsPath"); views != "" {
ViewsPath = views
}
if sessionon, err := AppConfig.Bool("SessionOn"); err == nil {
SessionOn = sessionon
}
if sessProvider := AppConfig.String("SessionProvider"); sessProvider != "" {
SessionProvider = sessProvider
}
if sessName := AppConfig.String("SessionName"); sessName != "" {
SessionName = sessName
}
if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" {
SessionSavePath = sesssavepath
}
if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
SessionGCMaxLifetime = sessMaxLifeTime
}
if sesscookielifetime, err := AppConfig.Int("SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
SessionCookieLifeTime = sesscookielifetime
}
if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil {
UseFcgi = usefcgi
}
if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil {
EnableGzip = enablegzip
}
if directoryindex, err := AppConfig.Bool("DirectoryIndex"); err == nil {
DirectoryIndex = directoryindex
}
if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil {
HttpServerTimeOut = timeout
}
if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil {
ErrorsShow = errorsshow
}
if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil {
CopyRequestBody = copyrequestbody
}
if xsrfkey := AppConfig.String("XSRFKEY"); xsrfkey != "" {
XSRFKEY = xsrfkey
}
if enablexsrf, err := AppConfig.Bool("EnableXSRF"); err == nil {
EnableXSRF = enablexsrf
}
if expire, err := AppConfig.Int("XSRFExpire"); err == nil {
XSRFExpire = expire
}
if tplleft := AppConfig.String("TemplateLeft"); tplleft != "" {
TemplateLeft = tplleft
}
if tplright := AppConfig.String("TemplateRight"); tplright != "" {
TemplateRight = tplright
}
if httptls, err := AppConfig.Bool("EnableHttpTLS"); err == nil {
EnableHttpTLS = httptls
}
if httpsport, err := AppConfig.Int("HttpsPort"); err == nil {
HttpsPort = httpsport
}
if certfile := AppConfig.String("HttpCertFile"); certfile != "" {
HttpCertFile = certfile
}
if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" {
HttpKeyFile = keyfile
}
if serverName := AppConfig.String("BeegoServerName"); serverName != "" {
BeegoServerName = serverName
}
if flashname := AppConfig.String("FlashName"); flashname != "" {
FlashName = flashname
}
if flashseperator := AppConfig.String("FlashSeperator"); flashseperator != "" {
FlashSeperator = flashseperator
}
if sd := AppConfig.String("StaticDir"); sd != "" {
for k := range StaticDir {
delete(StaticDir, k)
} }
sds := strings.Fields(sd)
if v, err := getConfig("int", "HttpPort"); err == nil { for _, v := range sds {
HttpPort = v.(int) if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
} StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1]
} else {
if v, err := getConfig("bool", "EnableHttpListen"); err == nil { StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0]
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] if sgz := AppConfig.String("StaticExtensionsToGzip"); sgz != "" {
} else { extensions := strings.Split(sgz, ",")
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0] if len(extensions) > 0 {
StaticExtensionsToGzip = []string{}
for _, ext := range extensions {
if len(ext) == 0 {
continue
} }
} extWithDot := ext
} if extWithDot[:1] != "." {
extWithDot = "." + extWithDot
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)
} }
StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot)
} }
} }
}
if enableadmin, err := getConfig("bool", "EnableAdmin"); err == nil { if enableadmin, err := AppConfig.Bool("EnableAdmin"); err == nil {
EnableAdmin = enableadmin.(bool) EnableAdmin = enableadmin
} }
if adminhttpaddr, _ := getConfig("string", "AdminHttpAddr"); adminhttpaddr != "" { if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" {
AdminHttpAddr = adminhttpaddr.(string) AdminHttpAddr = adminhttpaddr
} }
if adminhttpport, err := getConfig("int", "AdminHttpPort"); err == nil { if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil {
AdminHttpPort = adminhttpport.(int) AdminHttpPort = adminhttpport
} }
if enabledocs, err := getConfig("bool", "EnableDocs"); err == nil { if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil {
EnableDocs = enabledocs.(bool) EnableDocs = enabledocs
} }
if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil {
RouterCaseSensitive = casesensitive
} }
return nil return nil
} }
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")
}

View File

@ -1,9 +1,44 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package config
import ( import (
@ -19,12 +54,21 @@ type ConfigContainer interface {
Int64(key string) (int64, error) Int64(key string) (int64, error)
Bool(key string) (bool, error) Bool(key string) (bool, error)
Float(key string) (float64, 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) 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. // Config is the adapter interface for parsing config file to get raw data to ConfigContainer.
type Config interface { type Config interface {
Parse(key string) (ConfigContainer, error) Parse(key string) (ConfigContainer, error)
ParseData(data []byte) (ConfigContainer, error)
} }
var adapters = make(map[string]Config) var adapters = make(map[string]Config)
@ -36,7 +80,7 @@ func Register(name string, adapter Config) {
if adapter == nil { if adapter == nil {
panic("config: Register adapter is nil") panic("config: Register adapter is nil")
} }
if _, dup := adapters[name]; dup { if _, ok := adapters[name]; ok {
panic("config: Register called twice for adapter " + name) panic("config: Register called twice for adapter " + name)
} }
adapters[name] = adapter adapters[name] = adapter
@ -51,3 +95,13 @@ func NewConfig(adapterName, fileaname string) (ConfigContainer, error) {
} }
return adapter.Parse(fileaname) 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)
}

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package config
@ -17,13 +25,11 @@ type fakeConfigContainer struct {
} }
func (c *fakeConfigContainer) getData(key string) string { func (c *fakeConfigContainer) getData(key string) string {
key = strings.ToLower(key) return c.data[strings.ToLower(key)]
return c.data[key]
} }
func (c *fakeConfigContainer) Set(key, val string) error { func (c *fakeConfigContainer) Set(key, val string) error {
key = strings.ToLower(key) c.data[strings.ToLower(key)] = val
c.data[key] = val
return nil return nil
} }
@ -31,34 +37,89 @@ func (c *fakeConfigContainer) String(key string) string {
return c.getData(key) 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 { func (c *fakeConfigContainer) Strings(key string) []string {
return strings.Split(c.getData(key), ";") 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) { func (c *fakeConfigContainer) Int(key string) (int, error) {
return strconv.Atoi(c.getData(key)) 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) { func (c *fakeConfigContainer) Int64(key string) (int64, error) {
return strconv.ParseInt(c.getData(key), 10, 64) 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) { func (c *fakeConfigContainer) Bool(key string) (bool, error) {
return strconv.ParseBool(c.getData(key)) 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) { func (c *fakeConfigContainer) Float(key string) (float64, error) {
return strconv.ParseFloat(c.getData(key), 64) 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) { func (c *fakeConfigContainer) DIY(key string) (interface{}, error) {
key = strings.ToLower(key) if v, ok := c.data[strings.ToLower(key)]; ok {
if v, ok := c.data[key]; ok {
return v, nil return v, nil
} }
return nil, errors.New("key not find") 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) var _ ConfigContainer = new(fakeConfigContainer)
func NewFakeConfig() ConfigContainer { func NewFakeConfig() ConfigContainer {

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package config
@ -10,11 +18,15 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"unicode" "unicode"
) )
@ -27,6 +39,7 @@ var (
bDQuote = []byte{'"'} // quote signal bDQuote = []byte{'"'} // quote signal
sectionStart = []byte{'['} // section start signal sectionStart = []byte{'['} // section start signal
sectionEnd = []byte{']'} // section end signal sectionEnd = []byte{']'} // section end signal
lineBreak = "\n"
) )
// IniConfig implements Config to parse ini file. // IniConfig implements Config to parse ini file.
@ -35,6 +48,10 @@ type IniConfig struct {
// ParseFile creates a new Config and parses the file configuration from the named file. // ParseFile creates a new Config and parses the file configuration from the named file.
func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
return ini.parseFile(name)
}
func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
file, err := os.Open(name) file, err := os.Open(name)
if err != nil { if err != nil {
return nil, err return nil, err
@ -53,6 +70,13 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
var comment bytes.Buffer var comment bytes.Buffer
buf := bufio.NewReader(file) buf := bufio.NewReader(file)
// check the BOM
head, err := buf.Peek(3)
if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 {
for i := 1; i <= 3; i++ {
buf.ReadByte()
}
}
section := DEFAULT_SECTION section := DEFAULT_SECTION
for { for {
line, _, err := buf.ReadLine() line, _, err := buf.ReadLine()
@ -80,8 +104,7 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
} }
if bytes.HasPrefix(line, sectionStart) && bytes.HasSuffix(line, sectionEnd) { if bytes.HasPrefix(line, sectionStart) && bytes.HasSuffix(line, sectionEnd) {
section = string(line[1 : len(line)-1]) section = strings.ToLower(string(line[1 : len(line)-1])) // section name case insensitive
section = strings.ToLower(section) // section name case insensitive
if comment.Len() > 0 { if comment.Len() > 0 {
cfg.sectionComment[section] = comment.String() cfg.sectionComment[section] = comment.String()
comment.Reset() comment.Reset()
@ -89,74 +112,237 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
if _, ok := cfg.data[section]; !ok { if _, ok := cfg.data[section]; !ok {
cfg.data[section] = make(map[string]string) cfg.data[section] = make(map[string]string)
} }
} else { continue
if _, ok := cfg.data[section]; !ok { }
cfg.data[section] = make(map[string]string)
}
keyval := bytes.SplitN(line, bEqual, 2)
val := bytes.TrimSpace(keyval[1])
if bytes.HasPrefix(val, bDQuote) {
val = bytes.Trim(val, `"`)
}
key := string(bytes.TrimSpace(keyval[0])) // key name case insensitive if _, ok := cfg.data[section]; !ok {
key = strings.ToLower(key) cfg.data[section] = make(map[string]string)
cfg.data[section][key] = string(val) }
if comment.Len() > 0 { keyValue := bytes.SplitN(line, bEqual, 2)
cfg.keycomment[section+"."+key] = comment.String()
comment.Reset() key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
key = strings.ToLower(key)
// handle include "other.conf"
if len(keyValue) == 1 && strings.HasPrefix(key, "include") {
includefiles := strings.Fields(key)
if includefiles[0] == "include" && len(includefiles) == 2 {
otherfile := strings.Trim(includefiles[1], "\"")
if !path.IsAbs(otherfile) {
otherfile = path.Join(path.Dir(name), otherfile)
}
i, err := ini.parseFile(otherfile)
if err != nil {
return nil, err
}
for sec, dt := range i.data {
if _, ok := cfg.data[sec]; !ok {
cfg.data[sec] = make(map[string]string)
}
for k, v := range dt {
cfg.data[sec][k] = v
}
}
for sec, comm := range i.sectionComment {
cfg.sectionComment[sec] = comm
}
for k, comm := range i.keyComment {
cfg.keyComment[k] = comm
}
continue
} }
} }
if len(keyValue) != 2 {
return nil, errors.New("read the content error: \"" + string(line) + "\", should key = val")
}
val := bytes.TrimSpace(keyValue[1])
if bytes.HasPrefix(val, bDQuote) {
val = bytes.Trim(val, `"`)
}
cfg.data[section][key] = string(val)
if comment.Len() > 0 {
cfg.keyComment[section+"."+key] = comment.String()
comment.Reset()
}
} }
return cfg, nil 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. // A Config represents the ini configuration.
// When set and get value, support key as section:name type. // When set and get value, support key as section:name type.
type IniConfigContainer struct { type IniConfigContainer struct {
filename string filename string
data map[string]map[string]string // section=> key:val data map[string]map[string]string // section=> key:val
sectionComment map[string]string // section : comment sectionComment map[string]string // section : comment
keycomment map[string]string // id: []{comment, key...}; id 1 is for main comment. keyComment map[string]string // id: []{comment, key...}; id 1 is for main comment.
sync.RWMutex sync.RWMutex
} }
// Bool returns the boolean value for a given key. // Bool returns the boolean value for a given key.
func (c *IniConfigContainer) Bool(key string) (bool, error) { func (c *IniConfigContainer) Bool(key string) (bool, error) {
key = strings.ToLower(key)
return strconv.ParseBool(c.getdata(key)) 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. // Int returns the integer value for a given key.
func (c *IniConfigContainer) Int(key string) (int, error) { func (c *IniConfigContainer) Int(key string) (int, error) {
key = strings.ToLower(key)
return strconv.Atoi(c.getdata(key)) 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. // Int64 returns the int64 value for a given key.
func (c *IniConfigContainer) Int64(key string) (int64, error) { func (c *IniConfigContainer) Int64(key string) (int64, error) {
key = strings.ToLower(key)
return strconv.ParseInt(c.getdata(key), 10, 64) 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. // Float returns the float value for a given key.
func (c *IniConfigContainer) Float(key string) (float64, error) { func (c *IniConfigContainer) Float(key string) (float64, error) {
key = strings.ToLower(key)
return strconv.ParseFloat(c.getdata(key), 64) 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. // String returns the string value for a given key.
func (c *IniConfigContainer) String(key string) string { func (c *IniConfigContainer) String(key string) string {
key = strings.ToLower(key)
return c.getdata(key) 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. // Strings returns the []string value for a given key.
func (c *IniConfigContainer) Strings(key string) []string { func (c *IniConfigContainer) Strings(key string) []string {
return strings.Split(c.String(key), ";") 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. // WriteValue writes a new value for key.
// if write to one section, the key need be "section::key". // if write to one section, the key need be "section::key".
// if the section is not existed, it panics. // if the section is not existed, it panics.
@ -167,16 +353,19 @@ func (c *IniConfigContainer) Set(key, value string) error {
return errors.New("key is empty") return errors.New("key is empty")
} }
var section, k string var (
key = strings.ToLower(key) section, k string
sectionkey := strings.Split(key, "::") sectionKey []string = strings.Split(key, "::")
if len(sectionkey) >= 2 { )
section = sectionkey[0]
k = sectionkey[1] if len(sectionKey) >= 2 {
section = sectionKey[0]
k = sectionKey[1]
} else { } else {
section = DEFAULT_SECTION section = DEFAULT_SECTION
k = sectionkey[0] k = sectionKey[0]
} }
if _, ok := c.data[section]; !ok { if _, ok := c.data[section]; !ok {
c.data[section] = make(map[string]string) c.data[section] = make(map[string]string)
} }
@ -186,8 +375,7 @@ func (c *IniConfigContainer) Set(key, value string) error {
// DIY returns the raw value by a given key. // DIY returns the raw value by a given key.
func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) { func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
key = strings.ToLower(key) if v, ok := c.data[strings.ToLower(key)]; ok {
if v, ok := c.data[key]; ok {
return v, nil return v, nil
} }
return v, errors.New("key not find") return v, errors.New("key not find")
@ -195,24 +383,25 @@ func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
// section.key or key // section.key or key
func (c *IniConfigContainer) getdata(key string) string { func (c *IniConfigContainer) getdata(key string) string {
c.RLock()
defer c.RUnlock()
if len(key) == 0 { if len(key) == 0 {
return "" return ""
} }
c.RLock()
defer c.RUnlock()
var section, k string var (
key = strings.ToLower(key) section, k string
sectionkey := strings.Split(key, "::") sectionKey []string = strings.Split(strings.ToLower(key), "::")
if len(sectionkey) >= 2 { )
section = sectionkey[0] if len(sectionKey) >= 2 {
k = sectionkey[1] section = sectionKey[0]
k = sectionKey[1]
} else { } else {
section = DEFAULT_SECTION section = DEFAULT_SECTION
k = sectionkey[0] k = sectionKey[0]
} }
if v, ok := c.data[section]; ok { if v, ok := c.data[section]; ok {
if vv, o := v[k]; o { if vv, ok := v[k]; ok {
return vv return vv
} }
} }

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package config

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package config
@ -26,17 +34,23 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
return nil, err return nil, err
} }
defer file.Close() defer file.Close()
x := &JsonConfigContainer{
data: make(map[string]interface{}),
}
content, err := ioutil.ReadAll(file) content, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = json.Unmarshal(content, &x.data)
return js.ParseData(content)
}
// ParseData returns a ConfigContainer with json string
func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) {
x := &JsonConfigContainer{
data: make(map[string]interface{}),
}
err := json.Unmarshal(data, &x.data)
if err != nil { if err != nil {
var wrappingArray []interface{} var wrappingArray []interface{}
err2 := json.Unmarshal(content, &wrappingArray) err2 := json.Unmarshal(data, &wrappingArray)
if err2 != nil { if err2 != nil {
return nil, err return nil, err
} }
@ -54,80 +68,152 @@ type JsonConfigContainer struct {
// Bool returns the boolean value for a given key. // Bool returns the boolean value for a given key.
func (c *JsonConfigContainer) Bool(key string) (bool, error) { func (c *JsonConfigContainer) Bool(key string) (bool, error) {
val := c.getdata(key) val := c.getData(key)
if val != nil { if val != nil {
if v, ok := val.(bool); ok { if v, ok := val.(bool); ok {
return v, nil return v, nil
} else {
return false, errors.New("not bool value")
} }
} else { return false, errors.New("not bool value")
return false, errors.New("not exist key:" + key)
} }
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 v
}
return defaultval
} }
// Int returns the integer value for a given key. // Int returns the integer value for a given key.
func (c *JsonConfigContainer) Int(key string) (int, error) { func (c *JsonConfigContainer) Int(key string) (int, error) {
val := c.getdata(key) val := c.getData(key)
if val != nil { if val != nil {
if v, ok := val.(float64); ok { if v, ok := val.(float64); ok {
return int(v), nil return int(v), nil
} else {
return 0, errors.New("not int value")
} }
} else { return 0, errors.New("not int value")
return 0, errors.New("not exist key:" + key)
} }
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 v
}
return defaultval
} }
// Int64 returns the int64 value for a given key. // Int64 returns the int64 value for a given key.
func (c *JsonConfigContainer) Int64(key string) (int64, error) { func (c *JsonConfigContainer) Int64(key string) (int64, error) {
val := c.getdata(key) val := c.getData(key)
if val != nil { if val != nil {
if v, ok := val.(float64); ok { if v, ok := val.(float64); ok {
return int64(v), nil return int64(v), nil
} else {
return 0, errors.New("not int64 value")
} }
} else { return 0, errors.New("not int64 value")
return 0, errors.New("not exist key:" + key)
} }
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 v
}
return defaultval
} }
// Float returns the float value for a given key. // Float returns the float value for a given key.
func (c *JsonConfigContainer) Float(key string) (float64, error) { func (c *JsonConfigContainer) Float(key string) (float64, error) {
val := c.getdata(key) val := c.getData(key)
if val != nil { if val != nil {
if v, ok := val.(float64); ok { if v, ok := val.(float64); ok {
return v, nil return v, nil
} else {
return 0.0, errors.New("not float64 value")
} }
} else { return 0.0, errors.New("not float64 value")
return 0.0, errors.New("not exist key:" + key)
} }
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 v
}
return defaultval
} }
// String returns the string value for a given key. // String returns the string value for a given key.
func (c *JsonConfigContainer) String(key string) string { func (c *JsonConfigContainer) String(key string) string {
val := c.getdata(key) val := c.getData(key)
if val != nil { if val != nil {
if v, ok := val.(string); ok { if v, ok := val.(string); ok {
return v return v
} else {
return ""
} }
} else {
return ""
} }
return ""
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
func (c *JsonConfigContainer) DefaultString(key string, defaultval string) string {
// TODO FIXME should not use "" to replace non existance
if v := c.String(key); v != "" {
return v
}
return defaultval
} }
// Strings returns the []string value for a given key. // Strings returns the []string value for a given key.
func (c *JsonConfigContainer) Strings(key string) []string { func (c *JsonConfigContainer) Strings(key string) []string {
stringVal := c.String(key)
if stringVal == "" {
return []string{}
}
return strings.Split(c.String(key), ";") return strings.Split(c.String(key), ";")
} }
// WriteValue writes a new value for 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 v
}
return defaultval
}
// 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
}
return nil, errors.New("nonexist section " + section)
}
// 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
}
// Set writes a new value for key.
func (c *JsonConfigContainer) Set(key, val string) error { func (c *JsonConfigContainer) Set(key, val string) error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
@ -137,39 +223,39 @@ func (c *JsonConfigContainer) Set(key, val string) error {
// DIY returns the raw value by a given key. // DIY returns the raw value by a given key.
func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) { func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) {
val := c.getdata(key) val := c.getData(key)
if val != nil { if val != nil {
return val, nil return val, nil
} else {
return nil, errors.New("not exist key")
} }
return nil, errors.New("not exist key")
} }
// section.key or key // section.key or key
func (c *JsonConfigContainer) getdata(key string) interface{} { func (c *JsonConfigContainer) getData(key string) interface{} {
c.RLock()
defer c.RUnlock()
if len(key) == 0 { if len(key) == 0 {
return nil return nil
} }
sectionkey := strings.Split(key, "::")
if len(sectionkey) >= 2 { c.RLock()
cruval, ok := c.data[sectionkey[0]] defer c.RUnlock()
sectionKeys := strings.Split(key, "::")
if len(sectionKeys) >= 2 {
curValue, ok := c.data[sectionKeys[0]]
if !ok { if !ok {
return nil return nil
} }
for _, key := range sectionkey[1:] { for _, key := range sectionKeys[1:] {
if v, ok := cruval.(map[string]interface{}); !ok { if v, ok := curValue.(map[string]interface{}); ok {
return nil if curValue, ok = v[key]; !ok {
} else if cruval, ok = v[key]; !ok { return nil
return nil }
} }
} }
return cruval return curValue
} else { }
if v, ok := c.data[key]; ok { if v, ok := c.data[key]; ok {
return v return v
}
} }
return nil return nil
} }

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package config
@ -13,6 +21,7 @@ import (
var jsoncontext = `{ var jsoncontext = `{
"appname": "beeapi", "appname": "beeapi",
"testnames": "foo;bar",
"httpport": 8080, "httpport": 8080,
"mysqlport": 3600, "mysqlport": 3600,
"PI": 3.1415976, "PI": 3.1415976,
@ -20,8 +29,8 @@ var jsoncontext = `{
"autorender": false, "autorender": false,
"copyrequestbody": true, "copyrequestbody": true,
"database": { "database": {
"host": "host", "host": "host",
"port": "port", "port": "port",
"database": "database", "database": "database",
"username": "username", "username": "username",
"password": "password", "password": "password",
@ -61,13 +70,13 @@ func TestJsonStartsWithArray(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
rootArray, err := jsonconf.DIY("rootArray") rootArray, err := jsonconf.DIY("rootArray")
if (err != nil) { if err != nil {
t.Error("array does not exist as element") t.Error("array does not exist as element")
} }
rootArrayCasted := rootArray.([]interface{}) rootArrayCasted := rootArray.([]interface{})
if (rootArrayCasted == nil) { if rootArrayCasted == nil {
t.Error("array from root is nil") t.Error("array from root is nil")
}else { } else {
elem := rootArrayCasted[0].(map[string]interface{}) elem := rootArrayCasted[0].(map[string]interface{})
if elem["url"] != "user" || elem["serviceAPI"] != "http://www.test.com/user" { if elem["url"] != "user" || elem["serviceAPI"] != "http://www.test.com/user" {
t.Error("array[0] values are not valid") t.Error("array[0] values are not valid")
@ -114,6 +123,12 @@ func TestJson(t *testing.T) {
if jsonconf.String("runmode") != "dev" { if jsonconf.String("runmode") != "dev" {
t.Fatal("runmode not equal to dev") t.Fatal("runmode not equal to dev")
} }
if v := jsonconf.Strings("unknown"); len(v) > 0 {
t.Fatal("unknown strings, the length should be 0")
}
if v := jsonconf.Strings("testnames"); len(v) != 2 {
t.Fatal("testnames length should be 2")
}
if v, err := jsonconf.Bool("autorender"); err != nil || v != false { if v, err := jsonconf.Bool("autorender"); err != nil || v != false {
t.Error(v) t.Error(v)
t.Fatal(err) t.Fatal(err)
@ -171,4 +186,8 @@ func TestJson(t *testing.T) {
if _, err := jsonconf.Bool("unknown"); err == nil { if _, err := jsonconf.Bool("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting a Bool") t.Error("unknown keys should return an error when expecting a Bool")
} }
if !jsonconf.DefaultBool("unknow", true) {
t.Error("unknown keys with default value wrong")
}
} }

View File

@ -1,18 +1,45 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 // 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 ( import (
"encoding/xml"
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"github.com/astaxie/beego/config" "github.com/astaxie/beego/config"
"github.com/beego/x2j" "github.com/beego/x2j"
@ -21,31 +48,41 @@ import (
// XmlConfig is a xml config parser and implements Config interface. // XmlConfig is a xml config parser and implements Config interface.
// xml configurations should be included in <config></config> tag. // xml configurations should be included in <config></config> tag.
// only support key/value pair as <key>value</key> as each item. // only support key/value pair as <key>value</key> as each item.
type XMLConfig struct { type XMLConfig struct{}
}
// Parse returns a ConfigContainer with parsed xml config map. // Parse returns a ConfigContainer with parsed xml config map.
func (xmls *XMLConfig) Parse(filename string) (config.ConfigContainer, error) { func (xc *XMLConfig) Parse(filename string) (config.ConfigContainer, error) {
file, err := os.Open(filename) file, err := os.Open(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer file.Close() defer file.Close()
x := &XMLConfigContainer{
data: make(map[string]interface{}), x := &XMLConfigContainer{data: make(map[string]interface{})}
}
content, err := ioutil.ReadAll(file) content, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
d, err := x2j.DocToMap(string(content)) d, err := x2j.DocToMap(string(content))
if err != nil { if err != nil {
return nil, err return nil, err
} }
x.data = d["config"].(map[string]interface{}) x.data = d["config"].(map[string]interface{})
return x, nil 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. // A Config represents the xml configuration.
type XMLConfigContainer struct { type XMLConfigContainer struct {
data map[string]interface{} data map[string]interface{}
@ -57,21 +94,61 @@ func (c *XMLConfigContainer) Bool(key string) (bool, error) {
return strconv.ParseBool(c.data[key].(string)) 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. // Int returns the integer value for a given key.
func (c *XMLConfigContainer) Int(key string) (int, error) { func (c *XMLConfigContainer) Int(key string) (int, error) {
return strconv.Atoi(c.data[key].(string)) 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. // Int64 returns the int64 value for a given key.
func (c *XMLConfigContainer) Int64(key string) (int64, error) { func (c *XMLConfigContainer) Int64(key string) (int64, error) {
return strconv.ParseInt(c.data[key].(string), 10, 64) 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. // Float returns the float value for a given key.
func (c *XMLConfigContainer) Float(key string) (float64, error) { func (c *XMLConfigContainer) Float(key string) (float64, error) {
return strconv.ParseFloat(c.data[key].(string), 64) 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. // String returns the string value for a given key.
func (c *XMLConfigContainer) String(key string) string { func (c *XMLConfigContainer) String(key string) string {
if v, ok := c.data[key].(string); ok { if v, ok := c.data[key].(string); ok {
@ -80,11 +157,56 @@ func (c *XMLConfigContainer) String(key string) string {
return "" 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. // Strings returns the []string value for a given key.
func (c *XMLConfigContainer) Strings(key string) []string { func (c *XMLConfigContainer) Strings(key string) []string {
return strings.Split(c.String(key), ";") 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. // WriteValue writes a new value for key.
func (c *XMLConfigContainer) Set(key, val string) error { func (c *XMLConfigContainer) Set(key, val string) error {
c.Lock() c.Lock()

View File

@ -1,10 +1,18 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package xml
import ( import (
"os" "os"

View File

@ -1,59 +1,92 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 // 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 ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"path"
"strings" "strings"
"sync" "sync"
"time"
"github.com/astaxie/beego/config" "github.com/astaxie/beego/config"
"github.com/beego/goyaml2" "github.com/beego/goyaml2"
) )
// YAMLConfig is a yaml config parser and implements Config interface. // YAMLConfig is a yaml config parser and implements Config interface.
type YAMLConfig struct { type YAMLConfig struct{}
}
// Parse returns a ConfigContainer with parsed yaml config map. // Parse returns a ConfigContainer with parsed yaml config map.
func (yaml *YAMLConfig) Parse(filename string) (config.ConfigContainer, error) { func (yaml *YAMLConfig) Parse(filename string) (y config.ConfigContainer, err error) {
y := &YAMLConfigContainer{
data: make(map[string]interface{}),
}
cnf, err := ReadYmlReader(filename) cnf, err := ReadYmlReader(filename)
if err != nil { 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 nil, err
} }
y.data = cnf return yaml.Parse(tmpName)
return y, nil
} }
// Read yaml file to map. // Read yaml file to map.
// if json like, use json package, unless goyaml2 package. // if json like, use json package, unless goyaml2 package.
func ReadYmlReader(path string) (cnf map[string]interface{}, err error) { func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
err = nil
f, err := os.Open(path) f, err := os.Open(path)
if err != nil { if err != nil {
return return
} }
defer f.Close() defer f.Close()
err = nil
buf, err := ioutil.ReadAll(f) buf, err := ioutil.ReadAll(f)
if err != nil || len(buf) < 3 { if err != nil || len(buf) < 3 {
return return
} }
if string(buf[0:1]) == "{" { if string(buf[0:1]) == "{" {
log.Println("Look lile a Json, try it") log.Println("Look like a Json, try json umarshal")
err = json.Unmarshal(buf, &cnf) err = json.Unmarshal(buf, &cnf)
if err == nil { if err == nil {
log.Println("It is Json Map") log.Println("It is Json Map")
@ -61,19 +94,19 @@ func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
} }
} }
_map, _err := goyaml2.Read(bytes.NewBuffer(buf)) data, err := goyaml2.Read(bytes.NewBuffer(buf))
if _err != nil { if err != nil {
log.Println("Goyaml2 ERR>", string(buf), _err) log.Println("Goyaml2 ERR>", string(buf), err)
//err = goyaml.Unmarshal(buf, &cnf)
err = _err
return return
} }
if _map == nil {
if data == nil {
log.Println("Goyaml2 output nil? Pls report bug\n" + string(buf)) log.Println("Goyaml2 output nil? Pls report bug\n" + string(buf))
return
} }
cnf, ok := _map.(map[string]interface{}) cnf, ok := data.(map[string]interface{})
if !ok { if !ok {
log.Println("Not a Map? >> ", string(buf), _map) log.Println("Not a Map? >> ", string(buf), data)
cnf = nil cnf = nil
} }
return return
@ -93,6 +126,16 @@ func (c *YAMLConfigContainer) Bool(key string) (bool, error) {
return false, errors.New("not bool value") 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. // Int returns the integer value for a given key.
func (c *YAMLConfigContainer) Int(key string) (int, error) { func (c *YAMLConfigContainer) Int(key string) (int, error) {
if v, ok := c.data[key].(int64); ok { if v, ok := c.data[key].(int64); ok {
@ -101,6 +144,16 @@ func (c *YAMLConfigContainer) Int(key string) (int, error) {
return 0, errors.New("not int value") 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. // Int64 returns the int64 value for a given key.
func (c *YAMLConfigContainer) Int64(key string) (int64, error) { func (c *YAMLConfigContainer) Int64(key string) (int64, error) {
if v, ok := c.data[key].(int64); ok { if v, ok := c.data[key].(int64); ok {
@ -109,6 +162,16 @@ func (c *YAMLConfigContainer) Int64(key string) (int64, error) {
return 0, errors.New("not bool value") 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. // Float returns the float value for a given key.
func (c *YAMLConfigContainer) Float(key string) (float64, error) { func (c *YAMLConfigContainer) Float(key string) (float64, error) {
if v, ok := c.data[key].(float64); ok { if v, ok := c.data[key].(float64); ok {
@ -117,6 +180,16 @@ func (c *YAMLConfigContainer) Float(key string) (float64, error) {
return 0.0, errors.New("not float64 value") 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. // String returns the string value for a given key.
func (c *YAMLConfigContainer) String(key string) string { func (c *YAMLConfigContainer) String(key string) string {
if v, ok := c.data[key].(string); ok { if v, ok := c.data[key].(string); ok {
@ -125,11 +198,52 @@ func (c *YAMLConfigContainer) String(key string) string {
return "" 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. // Strings returns the []string value for a given key.
func (c *YAMLConfigContainer) Strings(key string) []string { func (c *YAMLConfigContainer) Strings(key string) []string {
return strings.Split(c.String(key), ";") 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. // WriteValue writes a new value for key.
func (c *YAMLConfigContainer) Set(key, val string) error { func (c *YAMLConfigContainer) Set(key, val string) error {
c.Lock() c.Lock()

View File

@ -1,10 +1,18 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package yaml
import ( import (
"os" "os"

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego

View File

@ -1,9 +1,24 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package context
import ( import (
@ -16,7 +31,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/astaxie/beego/middleware" "github.com/astaxie/beego/utils"
) )
// Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter. // Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter.
@ -26,39 +41,27 @@ type Context struct {
Output *BeegoOutput Output *BeegoOutput
Request *http.Request Request *http.Request
ResponseWriter http.ResponseWriter ResponseWriter http.ResponseWriter
_xsrf_token string
} }
// Redirect does redirection to localurl with http header status code. // Redirect does redirection to localurl with http header status code.
// It sends http response header directly. // It sends http response header directly.
func (ctx *Context) Redirect(status int, localurl string) { func (ctx *Context) Redirect(status int, localurl string) {
ctx.Output.Header("Location", localurl) ctx.Output.Header("Location", localurl)
ctx.Output.SetStatus(status) ctx.ResponseWriter.WriteHeader(status)
} }
// Abort stops this request. // Abort stops this request.
// if middleware.ErrorMaps exists, panic body. // if beego.ErrorMaps exists, panic body.
// if middleware.HTTPExceptionMaps exists, panic HTTPException struct with status and body string.
func (ctx *Context) Abort(status int, body string) { func (ctx *Context) Abort(status int, body string) {
ctx.Output.SetStatus(status) 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) panic(body)
} }
// Write string to response body. // Write string to response body.
// it sends response body. // it sends response body.
func (ctx *Context) WriteString(content string) { func (ctx *Context) WriteString(content string) {
ctx.Output.Body([]byte(content)) ctx.ResponseWriter.Write([]byte(content))
} }
// Get cookie from request by a given key. // Get cookie from request by a given key.
@ -110,3 +113,38 @@ func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interf
cookie := strings.Join([]string{vs, timestamp, sig}, "|") cookie := strings.Join([]string{vs, timestamp, sig}, "|")
ctx.Output.Cookie(name, cookie, others...) 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")
return false
}
if ctx._xsrf_token != token {
ctx.Abort(403, "XSRF cookie does not match POST argument")
return false
}
return true
}

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package context
@ -19,7 +27,7 @@ import (
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
) )
// BeegoInput operates the http request header ,data ,cookie and body. // BeegoInput operates the http request header, data, cookie and body.
// it also contains router params and current session. // it also contains router params and current session.
type BeegoInput struct { type BeegoInput struct {
CruSession session.SessionStore CruSession session.SessionStore
@ -52,7 +60,7 @@ func (input *BeegoInput) Uri() string {
// Url returns request url path (without query string, fragment). // Url returns request url path (without query string, fragment).
func (input *BeegoInput) Url() string { func (input *BeegoInput) Url() string {
return input.Request.URL.String() return input.Request.URL.Path
} }
// Site returns base site url as scheme://domain type. // Site returns base site url as scheme://domain type.
@ -64,11 +72,11 @@ func (input *BeegoInput) Site() string {
func (input *BeegoInput) Scheme() string { func (input *BeegoInput) Scheme() string {
if input.Request.URL.Scheme != "" { if input.Request.URL.Scheme != "" {
return input.Request.URL.Scheme return input.Request.URL.Scheme
} else if input.Request.TLS == nil {
return "http"
} else {
return "https"
} }
if input.Request.TLS == nil {
return "http"
}
return "https"
} }
// Domain returns host name. // Domain returns host name.
@ -145,12 +153,12 @@ func (input *BeegoInput) IsSecure() bool {
return input.Scheme() == "https" return input.Scheme() == "https"
} }
// IsSecure returns boolean of this request is in webSocket. // IsWebsocket returns boolean of this request is in webSocket.
func (input *BeegoInput) IsWebsocket() bool { func (input *BeegoInput) IsWebsocket() bool {
return input.Header("Upgrade") == "websocket" return input.Header("Upgrade") == "websocket"
} }
// IsSecure returns boolean of whether file uploads in this request or not.. // IsUpload returns boolean of whether file uploads in this request or not..
func (input *BeegoInput) IsUpload() bool { func (input *BeegoInput) IsUpload() bool {
return strings.Contains(input.Header("Content-Type"), "multipart/form-data") return strings.Contains(input.Header("Content-Type"), "multipart/form-data")
} }
@ -181,16 +189,24 @@ func (input *BeegoInput) Proxy() []string {
return []string{} return []string{}
} }
// Referer returns http referer header.
func (input *BeegoInput) Referer() string {
return input.Header("Referer")
}
// Refer returns http referer header. // Refer returns http referer header.
func (input *BeegoInput) Refer() string { func (input *BeegoInput) Refer() string {
return input.Header("Referer") return input.Referer()
} }
// SubDomains returns sub domain string. // SubDomains returns sub domain string.
// if aa.bb.domain.com, returns aa.bb . // if aa.bb.domain.com, returns aa.bb .
func (input *BeegoInput) SubDomains() string { func (input *BeegoInput) SubDomains() string {
parts := strings.Split(input.Host(), ".") parts := strings.Split(input.Host(), ".")
return strings.Join(parts[len(parts)-2:], ".") if len(parts) >= 3 {
return strings.Join(parts[:len(parts)-2], ".")
}
return ""
} }
// Port returns request client port. // Port returns request client port.
@ -229,6 +245,7 @@ func (input *BeegoInput) Query(key string) string {
} }
// Header returns request header item string by a given string. // Header returns request header item string by a given string.
// if non-existed, return empty string.
func (input *BeegoInput) Header(key string) string { func (input *BeegoInput) Header(key string) string {
return input.Request.Header.Get(key) return input.Request.Header.Get(key)
} }
@ -244,11 +261,12 @@ func (input *BeegoInput) Cookie(key string) string {
} }
// Session returns current session item value by a given key. // Session returns current session item value by a given key.
// if non-existed, return empty string.
func (input *BeegoInput) Session(key interface{}) interface{} { func (input *BeegoInput) Session(key interface{}) interface{} {
return input.CruSession.Get(key) return input.CruSession.Get(key)
} }
// Body returns the raw request body data as bytes. // CopyBody returns the raw request body data as bytes.
func (input *BeegoInput) CopyBody() []byte { func (input *BeegoInput) CopyBody() []byte {
requestbody, _ := ioutil.ReadAll(input.Request.Body) requestbody, _ := ioutil.ReadAll(input.Request.Body)
input.Request.Body.Close() input.Request.Body.Close()

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package context
@ -62,3 +70,45 @@ func TestParse(t *testing.T) {
} }
fmt.Println(user) fmt.Println(user)
} }
func TestSubDomain(t *testing.T) {
r, _ := http.NewRequest("GET", "http://www.example.com/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
beegoInput := NewInput(r)
subdomain := beegoInput.SubDomains()
if subdomain != "www" {
t.Fatal("Subdomain parse error, got" + subdomain)
}
r, _ = http.NewRequest("GET", "http://localhost/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, should be empty, got " + beegoInput.SubDomains())
}
r, _ = http.NewRequest("GET", "http://aa.bb.example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "aa.bb" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
/* TODO Fix this
r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
*/
r, _ = http.NewRequest("GET", "http://example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
r, _ = http.NewRequest("GET", "http://aa.bb.cc.dd.example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "aa.bb.cc.dd" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
}

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package context
@ -68,6 +76,14 @@ func (output *BeegoOutput) Body(content []byte) {
} else { } else {
output.Header("Content-Length", strconv.Itoa(len(content))) 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) output_writer.Write(content)
switch output_writer.(type) { switch output_writer.(type) {
case *gzip.Writer: case *gzip.Writer:
@ -172,7 +188,7 @@ func sanitizeValue(v string) string {
// Json writes json to response body. // Json writes json to response body.
// if coding is true, it converts utf-8 to \u0000 type. // if coding is true, it converts utf-8 to \u0000 type.
func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error { func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error {
output.Header("Content-Type", "application/json;charset=UTF-8") output.Header("Content-Type", "application/json; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {
@ -193,7 +209,7 @@ func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) e
// Jsonp writes jsonp to response body. // Jsonp writes jsonp to response body.
func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error { func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/javascript;charset=UTF-8") output.Header("Content-Type", "application/javascript; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {
@ -219,7 +235,7 @@ func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
// Xml writes xml string to response body. // Xml writes xml string to response body.
func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error { func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/xml;charset=UTF-8") output.Header("Content-Type", "application/xml; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {
@ -267,7 +283,6 @@ func (output *BeegoOutput) ContentType(ext string) {
// SetStatus sets response status code. // SetStatus sets response status code.
// It writes response header directly. // It writes response header directly.
func (output *BeegoOutput) SetStatus(status int) { func (output *BeegoOutput) SetStatus(status int) {
output.Context.ResponseWriter.WriteHeader(status)
output.Status = status output.Status = status
} }

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
@ -22,7 +30,6 @@ import (
"github.com/astaxie/beego/context" "github.com/astaxie/beego/context"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
"github.com/astaxie/beego/utils"
) )
//commonly used mime-types //commonly used mime-types
@ -263,11 +270,22 @@ func (c *Controller) Redirect(url string, code int) {
// Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string. // Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string.
func (c *Controller) Abort(code string) { func (c *Controller) Abort(code string) {
status, err := strconv.Atoi(code) status, err := strconv.Atoi(code)
if err == nil { if err != nil {
c.Ctx.Abort(status, code) status = 200
} else {
c.Ctx.Abort(200, code)
} }
c.CustomAbort(status, 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.ResponseWriter.WriteHeader(status)
// first panic from ErrorMaps, is is user defined error functions.
if _, ok := ErrorMaps[body]; ok {
panic(body)
}
// last panic user string
c.Ctx.ResponseWriter.Write([]byte(body))
panic(USERSTOPRUN)
} }
// StopRun makes panic of USERSTOPRUN error and go to recover function if defined. // StopRun makes panic of USERSTOPRUN error and go to recover function if defined.
@ -277,7 +295,7 @@ func (c *Controller) StopRun() {
// UrlFor does another controller handler in this request function. // UrlFor does another controller handler in this request function.
// it goes to this controller method if endpoint is not clear. // it goes to this controller method if endpoint is not clear.
func (c *Controller) UrlFor(endpoint string, values ...string) string { func (c *Controller) UrlFor(endpoint string, values ...interface{}) string {
if len(endpoint) <= 0 { if len(endpoint) <= 0 {
return "" return ""
} }
@ -351,38 +369,144 @@ func (c *Controller) ParseForm(obj interface{}) error {
return ParseForm(c.Input(), obj) return ParseForm(c.Input(), obj)
} }
// GetString returns the input value by key string. // GetString returns the input value by key string or the default value while it's present and input is blank
func (c *Controller) GetString(key string) string { func (c *Controller) GetString(key string, def ...string) string {
return c.Ctx.Input.Query(key) var defv string
if len(def) > 0 {
defv = def[0]
}
if v := c.Ctx.Input.Query(key); v != "" {
return v
} else {
return defv
}
} }
// GetStrings returns the input string slice by key string. // GetStrings returns the input string slice by key string or the default value while it's present and input is blank
// it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection. // it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection.
func (c *Controller) GetStrings(key string) []string { func (c *Controller) GetStrings(key string, def ...[]string) []string {
var defv []string
if len(def) > 0 {
defv = def[0]
}
f := c.Input() f := c.Input()
if f == nil { if f == nil {
return []string{} return defv
} }
vs := f[key] vs := f[key]
if len(vs) > 0 { if len(vs) > 0 {
return vs return vs
} else {
return defv
} }
return []string{}
} }
// GetInt returns input value as int64. // GetInt returns input as an int or the default value while it's present and input is blank
func (c *Controller) GetInt(key string) (int64, error) { func (c *Controller) GetInt(key string, def ...int) (int, error) {
return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64) var defv int
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.Atoi(strv)
} else {
return defv, nil
}
} }
// GetBool returns input value as bool. // GetInt8 return input as an int8 or the default value while it's present and input is blank
func (c *Controller) GetBool(key string) (bool, error) { func (c *Controller) GetInt8(key string, def ...int8) (int8, error) {
return strconv.ParseBool(c.Ctx.Input.Query(key)) var defv int8
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
i64, err := strconv.ParseInt(strv, 10, 8)
i8 := int8(i64)
return i8, err
} else {
return defv, nil
}
} }
// GetFloat returns input value as float64. // GetInt16 returns input as an int16 or the default value while it's present and input is blank
func (c *Controller) GetFloat(key string) (float64, error) { func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64) var defv int16
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
i64, err := strconv.ParseInt(strv, 10, 16)
i16 := int16(i64)
return i16, err
} else {
return defv, nil
}
}
// GetInt32 returns input as an int32 or the default value while it's present and input is blank
func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
var defv int32
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32)
i32 := int32(i64)
return i32, err
} else {
return defv, nil
}
}
// GetInt64 returns input value as int64 or the default value while it's present and input is blank.
func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
var defv int64
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseInt(strv, 10, 64)
} else {
return defv, nil
}
}
// GetBool returns input value as bool or the default value while it's present and input is blank.
func (c *Controller) GetBool(key string, def ...bool) (bool, error) {
var defv bool
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseBool(strv)
} else {
return defv, nil
}
}
// GetFloat returns input value as float64 or the default value while it's present and input is blank.
func (c *Controller) GetFloat(key string, def ...float64) (float64, error) {
var defv float64
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64)
} else {
return defv, nil
}
} }
// GetFile returns the file data in file upload field named as key. // GetFile returns the file data in file upload field named as key.
@ -474,18 +598,13 @@ func (c *Controller) SetSecureCookie(Secret, name, value string, others ...inter
// XsrfToken creates a xsrf token string and returns. // XsrfToken creates a xsrf token string and returns.
func (c *Controller) XsrfToken() string { func (c *Controller) XsrfToken() string {
if c._xsrf_token == "" { if c._xsrf_token == "" {
token, ok := c.GetSecureCookie(XSRFKEY, "_xsrf") var expire int64
if !ok { if c.XSRFExpire > 0 {
var expire int64 expire = int64(c.XSRFExpire)
if c.XSRFExpire > 0 { } else {
expire = int64(c.XSRFExpire) expire = int64(XSRFExpire)
} else {
expire = int64(XSRFExpire)
}
token = string(utils.RandomCreateBytes(32))
c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire)
} }
c._xsrf_token = token c._xsrf_token = c.Ctx.XsrfToken(XSRFKEY, expire)
} }
return c._xsrf_token return c._xsrf_token
} }
@ -497,19 +616,7 @@ func (c *Controller) CheckXsrfCookie() bool {
if !c.EnableXSRF { if !c.EnableXSRF {
return true return true
} }
token := c.GetString("_xsrf") return c.Ctx.CheckXsrfCookie()
if token == "" {
token = c.Ctx.Request.Header.Get("X-Xsrftoken")
}
if token == "" {
token = c.Ctx.Request.Header.Get("X-Csrftoken")
}
if token == "" {
c.Ctx.Abort(403, "'_xsrf' argument missing from POST")
} else if c._xsrf_token != token {
c.Ctx.Abort(403, "XSRF cookie does not match POST argument")
}
return true
} }
// XsrfFormHtml writes an input field contains xsrf token value. // XsrfFormHtml writes an input field contains xsrf token value.

75
controller_test.go Normal file
View File

@ -0,0 +1,75 @@
// 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"
"github.com/astaxie/beego/context"
)
func ExampleGetInt() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt("age")
fmt.Printf("%T", val)
//Output: int
}
func ExampleGetInt8() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt8("age")
fmt.Printf("%T", val)
//Output: int8
}
func ExampleGetInt16() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt16("age")
fmt.Printf("%T", val)
//Output: int16
}
func ExampleGetInt32() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt32("age")
fmt.Printf("%T", val)
//Output: int32
}
func ExampleGetInt64() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt64("age")
fmt.Printf("%T", val)
//Output: int64
}

14
docs.go
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 beego package beego
import ( import (

490
error.go Normal file
View File

@ -0,0 +1,490 @@
// 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"
"html/template"
"net/http"
"reflect"
"runtime"
"strconv"
"strings"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/utils"
)
const (
errorTypeHandler = iota
errorTypeController
)
var tpl = `
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>beego application error</title>
<style>
html, body, body * {padding: 0; margin: 0;}
#header {background:#ffd; border-bottom:solid 2px #A31515; padding: 20px 10px;}
#header h2{ }
#footer {border-top:solid 1px #aaa; padding: 5px 10px; font-size: 12px; color:green;}
#content {padding: 5px;}
#content .stack b{ font-size: 13px; color: red;}
#content .stack pre{padding-left: 10px;}
table {}
td.t {text-align: right; padding-right: 5px; color: #888;}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<div id="header">
<h2>{{.AppError}}</h2>
</div>
<div id="content">
<table>
<tr>
<td class="t">Request Method: </td><td>{{.RequestMethod}}</td>
</tr>
<tr>
<td class="t">Request URL: </td><td>{{.RequestURL}}</td>
</tr>
<tr>
<td class="t">RemoteAddr: </td><td>{{.RemoteAddr }}</td>
</tr>
</table>
<div class="stack">
<b>Stack</b>
<pre>{{.Stack}}</pre>
</div>
</div>
<div id="footer">
<p>beego {{ .BeegoVersion }} (beego framework)</p>
<p>golang version: {{.GoVersion}}</p>
</div>
</body>
</html>
`
// render default application error page with error and stack string.
func showErr(err interface{}, ctx *context.Context, Stack string) {
t, _ := template.New("beegoerrortemp").Parse(tpl)
data := make(map[string]string)
data["AppError"] = AppName + ":" + fmt.Sprint(err)
data["RequestMethod"] = ctx.Input.Method()
data["RequestURL"] = ctx.Input.Uri()
data["RemoteAddr"] = ctx.Input.IP()
data["Stack"] = Stack
data["BeegoVersion"] = VERSION
data["GoVersion"] = runtime.Version()
ctx.Output.SetStatus(500)
t.Execute(ctx.ResponseWriter, data)
}
var errtpl = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{.Title}}</title>
<style type="text/css">
* {
margin:0;
padding:0;
}
body {
background-color:#EFEFEF;
font: .9em "Lucida Sans Unicode", "Lucida Grande", sans-serif;
}
#wrapper{
width:600px;
margin:40px auto 0;
text-align:center;
-moz-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
-webkit-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
}
#wrapper h1{
color:#FFF;
text-align:center;
margin-bottom:20px;
}
#wrapper a{
display:block;
font-size:.9em;
padding-top:20px;
color:#FFF;
text-decoration:none;
text-align:center;
}
#container {
width:600px;
padding-bottom:15px;
background-color:#FFFFFF;
}
.navtop{
height:40px;
background-color:#24B2EB;
padding:13px;
}
.content {
padding:10px 10px 25px;
background: #FFFFFF;
margin:;
color:#333;
}
a.button{
color:white;
padding:15px 20px;
text-shadow:1px 1px 0 #00A5FF;
font-weight:bold;
text-align:center;
border:1px solid #24B2EB;
margin:0px 200px;
clear:both;
background-color: #24B2EB;
border-radius:100px;
-moz-border-radius:100px;
-webkit-border-radius:100px;
}
a.button:hover{
text-decoration:none;
background-color: #24B2EB;
}
</style>
</head>
<body>
<div id="wrapper">
<div id="container">
<div class="navtop">
<h1>{{.Title}}</h1>
</div>
<div id="content">
{{.Content}}
<a href="/" title="Home" class="button">Go Home</a><br />
<br>Powered by beego {{.BeegoVersion}}
</div>
</div>
</div>
</body>
</html>
`
type errorInfo struct {
controllerType reflect.Type
handler http.HandlerFunc
method string
errorType int
}
// map of http handlers for each error string.
var ErrorMaps map[string]*errorInfo
func init() {
ErrorMaps = make(map[string]*errorInfo)
}
// 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 be authorized." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 402 Payment Required
func paymentRequired(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Payment Required"
data["Content"] = template.HTML("<br>The page you have requested Payment Required." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// 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 is forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Your address may be blocked" +
"<br>The site may be disabled" +
"<br>You need to log in" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// 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 has flown the coop." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The page has moved" +
"<br>The page no longer exists" +
"<br>You were looking for your puppy and got lost" +
"<br>You like 404 pages" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 405 Method Not Allowed
func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Method Not Allowed"
data["Content"] = template.HTML("<br>The method you have requested Not Allowed." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI" +
"<br>The response MUST include an Allow header containing a list of valid methods for the requested resource." +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// 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 is down right now." +
"<br><br><ul>" +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 501 Not Implemented.
func notImplemented(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Not Implemented"
data["Content"] = template.HTML("<br>The page you have requested is Not Implemented." +
"<br><br><ul>" +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 502 Bad Gateway.
func badGateway(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Bad Gateway"
data["Content"] = template.HTML("<br>The page you have requested is down right now." +
"<br><br><ul>" +
"<br>The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request." +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// 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 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
t.Execute(rw, data)
}
// show 504 Gateway Timeout.
func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Gateway Timeout"
data["Content"] = template.HTML("<br>The page you have requested is unavailable." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br><br>The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI." +
"<br>Please try again later." +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// register default error http handlers, 404,401,403,500 and 503.
func registerDefaultErrorHandler() {
if _, ok := ErrorMaps["401"]; !ok {
Errorhandler("401", unauthorized)
}
if _, ok := ErrorMaps["402"]; !ok {
Errorhandler("402", paymentRequired)
}
if _, ok := ErrorMaps["403"]; !ok {
Errorhandler("403", forbidden)
}
if _, ok := ErrorMaps["404"]; !ok {
Errorhandler("404", notFound)
}
if _, ok := ErrorMaps["405"]; !ok {
Errorhandler("405", methodNotAllowed)
}
if _, ok := ErrorMaps["500"]; !ok {
Errorhandler("500", internalServerError)
}
if _, ok := ErrorMaps["501"]; !ok {
Errorhandler("501", notImplemented)
}
if _, ok := ErrorMaps["502"]; !ok {
Errorhandler("502", badGateway)
}
if _, ok := ErrorMaps["503"]; !ok {
Errorhandler("503", serviceUnavailable)
}
if _, ok := ErrorMaps["504"]; !ok {
Errorhandler("504", gatewayTimeout)
}
}
// ErrorHandler registers http.HandlerFunc to each http err code string.
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
func Errorhandler(code string, h http.HandlerFunc) *App {
errinfo := &errorInfo{}
errinfo.errorType = errorTypeHandler
errinfo.handler = h
errinfo.method = code
ErrorMaps[code] = errinfo
return BeeApp
}
// ErrorController registers ControllerInterface to each http err code string.
// usage:
// beego.ErrorHandler(&controllers.ErrorController{})
func ErrorController(c ControllerInterface) *App {
reflectVal := reflect.ValueOf(c)
rt := reflectVal.Type()
ct := reflect.Indirect(reflectVal).Type()
for i := 0; i < rt.NumMethod(); i++ {
if !utils.InSlice(rt.Method(i).Name, exceptMethod) && strings.HasPrefix(rt.Method(i).Name, "Error") {
errinfo := &errorInfo{}
errinfo.errorType = errorTypeController
errinfo.controllerType = ct
errinfo.method = rt.Method(i).Name
errname := strings.TrimPrefix(rt.Method(i).Name, "Error")
ErrorMaps[errname] = errinfo
}
}
return BeeApp
}
// show error string as simple text message.
// if error string is empty, show 500 error as default.
func exception(errcode string, ctx *context.Context) {
code, err := strconv.Atoi(errcode)
if err != nil {
code = 503
}
ctx.ResponseWriter.WriteHeader(code)
if h, ok := ErrorMaps[errcode]; ok {
executeError(h, ctx)
return
} else if h, ok := ErrorMaps["503"]; ok {
executeError(h, ctx)
return
} else {
ctx.WriteString(errcode)
}
}
func executeError(err *errorInfo, ctx *context.Context) {
if err.errorType == errorTypeHandler {
err.handler(ctx.ResponseWriter, ctx.Request)
return
}
if err.errorType == errorTypeController {
//Invoke the request handler
vc := reflect.New(err.controllerType)
execController, ok := vc.Interface().(ControllerInterface)
if !ok {
panic("controller is not ControllerInterface")
}
//call the controller init function
execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface())
//call prepare function
execController.Prepare()
execController.URLMapping()
in := make([]reflect.Value, 0)
method := vc.MethodByName(err.method)
method.Call(in)
//render template
if ctx.Output.Status == 0 {
if AutoRender {
if err := execController.Render(); err != nil {
panic(err)
}
}
}
// finish all runrouter. release resource
execController.Finish()
}
}

View File

@ -17,47 +17,47 @@ type ObjectController struct {
beego.Controller beego.Controller
} }
func (this *ObjectController) Post() { func (o *ObjectController) Post() {
var ob models.Object var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob) json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
objectid := models.AddOne(ob) objectid := models.AddOne(ob)
this.Data["json"] = map[string]string{"ObjectId": objectid} o.Data["json"] = map[string]string{"ObjectId": objectid}
this.ServeJson() o.ServeJson()
} }
func (this *ObjectController) Get() { func (o *ObjectController) Get() {
objectId := this.Ctx.Input.Params[":objectId"] objectId := o.Ctx.Input.Params[":objectId"]
if objectId != "" { if objectId != "" {
ob, err := models.GetOne(objectId) ob, err := models.GetOne(objectId)
if err != nil { if err != nil {
this.Data["json"] = err o.Data["json"] = err
} else { } else {
this.Data["json"] = ob o.Data["json"] = ob
} }
} else { } else {
obs := models.GetAll() obs := models.GetAll()
this.Data["json"] = obs o.Data["json"] = obs
} }
this.ServeJson() o.ServeJson()
} }
func (this *ObjectController) Put() { func (o *ObjectController) Put() {
objectId := this.Ctx.Input.Params[":objectId"] objectId := o.Ctx.Input.Params[":objectId"]
var ob models.Object var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob) json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
err := models.Update(objectId, ob.Score) err := models.Update(objectId, ob.Score)
if err != nil { if err != nil {
this.Data["json"] = err o.Data["json"] = err
} else { } else {
this.Data["json"] = "update success!" o.Data["json"] = "update success!"
} }
this.ServeJson() o.ServeJson()
} }
func (this *ObjectController) Delete() { func (o *ObjectController) Delete() {
objectId := this.Ctx.Input.Params[":objectId"] objectId := o.Ctx.Input.Params[":objectId"]
models.Delete(objectId) models.Delete(objectId)
this.Data["json"] = "delete success!" o.Data["json"] = "delete success!"
this.ServeJson() o.ServeJson()
} }

View File

@ -1,7 +1,11 @@
// Beego (http://beego.me/) // Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language. // @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 // @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE // @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors astaxie // @authors astaxie
package main package main

View File

@ -14,7 +14,7 @@ type MainController struct {
beego.Controller beego.Controller
} }
func (this *MainController) Get() { func (m *MainController) Get() {
this.Data["host"] = this.Ctx.Request.Host m.Data["host"] = m.Ctx.Request.Host
this.TplNames = "index.tpl" m.TplNames = "index.tpl"
} }

View File

@ -150,14 +150,14 @@ type WSController struct {
} }
var upgrader = websocket.Upgrader{ var upgrader = websocket.Upgrader{
ReadBufferSize: 1024, ReadBufferSize: 1024,
WriteBufferSize: 1024, WriteBufferSize: 1024,
} }
func (this *WSController) Get() { func (w *WSController) Get() {
ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil) ws, err := upgrader.Upgrade(w.Ctx.ResponseWriter, w.Ctx.Request, nil)
if _, ok := err.(websocket.HandshakeError); ok { if _, ok := err.(websocket.HandshakeError); ok {
http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) http.Error(w.Ctx.ResponseWriter, "Not a websocket handshake", 400)
return return
} else if err != nil { } else if err != nil {
return return

View File

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<title>Chat Example</title> <title>Chat Example</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> <script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {

View File

@ -1,17 +1,31 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
import "github.com/astaxie/beego/context"
// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)
// FilterRouter defines filter operation before controller handler execution. // FilterRouter defines filter operation before controller handler execution.
// it can match patterned url and do filter function when action arrives. // it can match patterned url and do filter function when action arrives.
type FilterRouter struct { type FilterRouter struct {
filterFunc FilterFunc filterFunc FilterFunc
tree *Tree tree *Tree
pattern string pattern string
returnOnOutput bool
} }
// ValidRouter check current request is valid for this filter. // ValidRouter check current request is valid for this filter.

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
@ -24,6 +32,24 @@ func NewFlash() *FlashData {
} }
} }
// Set message to flash
func (fd *FlashData) Set(key string, msg string, args ...interface{}) {
if len(args) == 0 {
fd.Data[key] = msg
} else {
fd.Data[key] = fmt.Sprintf(msg, args...)
}
}
// Success writes success message to flash.
func (fd *FlashData) Success(msg string, args ...interface{}) {
if len(args) == 0 {
fd.Data["success"] = msg
} else {
fd.Data["success"] = fmt.Sprintf(msg, args...)
}
}
// Notice writes notice message to flash. // Notice writes notice message to flash.
func (fd *FlashData) Notice(msg string, args ...interface{}) { func (fd *FlashData) Notice(msg string, args ...interface{}) {
if len(args) == 0 { if len(args) == 0 {

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
@ -17,12 +25,12 @@ type TestFlashController struct {
Controller Controller
} }
func (this *TestFlashController) TestWriteFlash() { func (t *TestFlashController) TestWriteFlash() {
flash := NewFlash() flash := NewFlash()
flash.Notice("TestFlashString") flash.Notice("TestFlashString")
flash.Store(&this.Controller) flash.Store(&t.Controller)
// we choose to serve json because we don't want to load a template html file // we choose to serve json because we don't want to load a template html file
this.ServeJson(true) t.ServeJson(true)
} }
func TestFlashHeader(t *testing.T) { func TestFlashHeader(t *testing.T) {

View File

@ -6,53 +6,71 @@ httplib is an libs help you to curl remote url.
## GET ## GET
you can use Get to crawl data. you can use Get to crawl data.
import "httplib" import "github.com/astaxie/beego/httplib"
str, err := httplib.Get("http://beego.me/").String() str, err := httplib.Get("http://beego.me/").String()
if err != nil { if err != nil {
t.Fatal(err) // error
} }
fmt.Println(str) fmt.Println(str)
## POST ## POST
POST data to remote url POST data to remote url
b:=httplib.Post("http://beego.me/") req := httplib.Post("http://beego.me/")
b.Param("username","astaxie") req.Param("username","astaxie")
b.Param("password","123456") req.Param("password","123456")
str, err := b.String() str, err := req.String()
if err != nil { if err != nil {
t.Fatal(err) // error
} }
fmt.Println(str) fmt.Println(str)
## set timeout ## Set timeout
you can set timeout in request.default is 60 seconds.
set Get 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) httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
set post timeout: // POST
httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second) httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
- first param is connectTimeout.
- second param is readWriteTimeout
## debug ## Debug
if you want to debug the request info, set the debug on
If you want to debug the request info, set the debug on
httplib.Get("http://beego.me/").Debug(true) httplib.Get("http://beego.me/").Debug(true)
## support HTTPS client ## Set HTTP Basic Auth
if request url is https. You can set the client support TSL:
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}) httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
more info about the tls.Config please visit http://golang.org/pkg/crypto/tls/#Config More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config
## set cookie ## 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: some http request need setcookie. So set it like this:
cookie := &http.Cookie{} cookie := &http.Cookie{}
@ -60,21 +78,20 @@ some http request need setcookie. So set it like this:
cookie.Value = "astaxie" cookie.Value = "astaxie"
httplib.Get("http://beego.me/").SetCookie(cookie) httplib.Get("http://beego.me/").SetCookie(cookie)
## upload file ## Upload file
httplib support mutil file upload, use `b.PostFile()`
b:=httplib.Post("http://beego.me/") httplib support mutil file upload, use `req.PostFile()`
b.Param("username","astaxie")
b.Param("password","123456") req := httplib.Post("http://beego.me/")
b.PostFile("uploadfile1", "httplib.pdf") req.Param("username","astaxie")
b.PostFile("uploadfile2", "httplib.txt") req.PostFile("uploadfile1", "httplib.pdf")
str, err := b.String() str, err := req.String()
if err != nil { if err != nil {
t.Fatal(err) // error
} }
fmt.Println(str) fmt.Println(str)
## set HTTP version
some servers need to specify the protocol version of HTTP
httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1") See godoc for further documentation and examples.
* [godoc.org/github.com/astaxie/beego/httplib](https://godoc.org/github.com/astaxie/beego/httplib)

View File

@ -1,9 +1,33 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package httplib
import ( import (
@ -13,6 +37,7 @@ import (
"encoding/xml" "encoding/xml"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"mime/multipart" "mime/multipart"
"net" "net"
"net/http" "net/http"
@ -29,7 +54,7 @@ var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 6
var defaultCookieJar http.CookieJar var defaultCookieJar http.CookieJar
var settingMutex sync.Mutex var settingMutex sync.Mutex
// createDefaultCookieJar creates a global cookiejar to store cookies. // createDefaultCookie creates a global cookiejar to store cookies.
func createDefaultCookie() { func createDefaultCookie() {
settingMutex.Lock() settingMutex.Lock()
defer settingMutex.Unlock() defer settingMutex.Unlock()
@ -49,44 +74,42 @@ func SetDefaultSetting(setting BeegoHttpSettings) {
} }
} }
// 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. // Get returns *BeegoHttpRequest with GET method.
func Get(url string) *BeegoHttpRequest { func Get(url string) *BeegoHttpRequest {
var req http.Request return newBeegoRequest(url, "GET")
req.Method = "GET"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
} }
// Post returns *BeegoHttpRequest with POST method. // Post returns *BeegoHttpRequest with POST method.
func Post(url string) *BeegoHttpRequest { func Post(url string) *BeegoHttpRequest {
var req http.Request return newBeegoRequest(url, "POST")
req.Method = "POST"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
} }
// Put returns *BeegoHttpRequest with PUT method. // Put returns *BeegoHttpRequest with PUT method.
func Put(url string) *BeegoHttpRequest { func Put(url string) *BeegoHttpRequest {
var req http.Request return newBeegoRequest(url, "PUT")
req.Method = "PUT"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
} }
// Delete returns *BeegoHttpRequest DELETE GET method. // Delete returns *BeegoHttpRequest DELETE method.
func Delete(url string) *BeegoHttpRequest { func Delete(url string) *BeegoHttpRequest {
var req http.Request return newBeegoRequest(url, "DELETE")
req.Method = "DELETE"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
} }
// Head returns *BeegoHttpRequest with HEAD method. // Head returns *BeegoHttpRequest with HEAD method.
func Head(url string) *BeegoHttpRequest { func Head(url string) *BeegoHttpRequest {
var req http.Request return newBeegoRequest(url, "HEAD")
req.Method = "HEAD"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
} }
// BeegoHttpSettings // BeegoHttpSettings
@ -108,6 +131,8 @@ type BeegoHttpRequest struct {
params map[string]string params map[string]string
files map[string]string files map[string]string
setting BeegoHttpSettings setting BeegoHttpSettings
resp *http.Response
body []byte
} }
// Change request settings // Change request settings
@ -116,6 +141,12 @@ func (b *BeegoHttpRequest) Setting(setting BeegoHttpSettings) *BeegoHttpRequest
return b 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 // SetEnableCookie sets enable/disable cookiejar
func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest { func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest {
b.setting.EnableCookie = enable b.setting.EnableCookie = enable
@ -123,7 +154,7 @@ func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest {
} }
// SetUserAgent sets User-Agent header field // SetUserAgent sets User-Agent header field
func (b *BeegoHttpRequest) SetAgent(useragent string) *BeegoHttpRequest { func (b *BeegoHttpRequest) SetUserAgent(useragent string) *BeegoHttpRequest {
b.setting.UserAgent = useragent b.setting.UserAgent = useragent
return b return b
} }
@ -222,7 +253,63 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest {
return b return b
} }
func (b *BeegoHttpRequest) buildUrl(paramBody string) {
// build GET url with query string
if b.req.Method == "GET" && len(paramBody) > 0 {
if strings.Index(b.url, "?") != -1 {
b.url += "&" + paramBody
} else {
b.url = b.url + "?" + paramBody
}
return
}
// build POST url and body
if b.req.Method == "POST" && b.req.Body == nil {
// with files
if len(b.files) > 0 {
pr, pw := io.Pipe()
bodyWriter := multipart.NewWriter(pw)
go func() {
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
log.Fatal(err)
}
fh, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
//iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
log.Fatal(err)
}
}
for k, v := range b.params {
bodyWriter.WriteField(k, v)
}
bodyWriter.Close()
pw.Close()
}()
b.Header("Content-Type", bodyWriter.FormDataContentType())
b.req.Body = ioutil.NopCloser(pr)
return
}
// with params
if len(paramBody) > 0 {
b.Header("Content-Type", "application/x-www-form-urlencoded")
b.Body(paramBody)
}
}
}
func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
if b.resp.StatusCode != 0 {
return b.resp, nil
}
var paramBody string var paramBody string
if len(b.params) > 0 { if len(b.params) > 0 {
var buf bytes.Buffer var buf bytes.Buffer
@ -236,63 +323,13 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
paramBody = paramBody[0 : len(paramBody)-1] paramBody = paramBody[0 : len(paramBody)-1]
} }
if b.req.Method == "GET" && len(paramBody) > 0 { b.buildUrl(paramBody)
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) url, err := url.Parse(b.url)
if url.Scheme == "" {
b.url = "http://" + b.url
url, err = url.Parse(b.url)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
b.req.URL = url b.req.URL = url
if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, true)
if err != nil {
println(err.Error())
}
println(string(dump))
}
trans := b.setting.Transport trans := b.setting.Transport
@ -318,14 +355,12 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
} }
} }
var jar http.CookieJar var jar http.CookieJar = nil
if b.setting.EnableCookie { if b.setting.EnableCookie {
if defaultCookieJar == nil { if defaultCookieJar == nil {
createDefaultCookie() createDefaultCookie()
} }
jar = defaultCookieJar jar = defaultCookieJar
} else {
jar = nil
} }
client := &http.Client{ client := &http.Client{
@ -333,14 +368,23 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
Jar: jar, Jar: jar,
} }
if b.setting.UserAgent != "" { if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
b.req.Header.Set("User-Agent", 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) resp, err := client.Do(b.req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
b.resp = resp
return resp, nil return resp, nil
} }
@ -358,6 +402,9 @@ func (b *BeegoHttpRequest) String() (string, error) {
// Bytes returns the body []byte in response. // Bytes returns the body []byte in response.
// it calls Response inner. // it calls Response inner.
func (b *BeegoHttpRequest) Bytes() ([]byte, error) { func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
if b.body != nil {
return b.body, nil
}
resp, err := b.getResponse() resp, err := b.getResponse()
if err != nil { if err != nil {
return nil, err return nil, err
@ -366,11 +413,11 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
return nil, nil return nil, nil
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body) b.body, err = ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return data, nil return b.body, nil
} }
// ToFile saves the body data in response to one file. // ToFile saves the body data in response to one file.
@ -391,10 +438,7 @@ func (b *BeegoHttpRequest) ToFile(filename string) error {
} }
defer resp.Body.Close() defer resp.Body.Close()
_, err = io.Copy(f, resp.Body) _, err = io.Copy(f, resp.Body)
if err != nil { return err
return err
}
return nil
} }
// ToJson returns the map that marshals from the body bytes as json in response . // ToJson returns the map that marshals from the body bytes as json in response .
@ -404,25 +448,17 @@ func (b *BeegoHttpRequest) ToJson(v interface{}) error {
if err != nil { if err != nil {
return err return err
} }
err = json.Unmarshal(data, v) return json.Unmarshal(data, v)
if err != nil {
return err
}
return nil
} }
// ToXml returns the map that marshals from the body bytes as xml in response . // ToXml returns the map that marshals from the body bytes as xml in response .
// it calls Response inner. // it calls Response inner.
func (b *BeegoHttpRequest) ToXML(v interface{}) error { func (b *BeegoHttpRequest) ToXml(v interface{}) error {
data, err := b.Bytes() data, err := b.Bytes()
if err != nil { if err != nil {
return err return err
} }
err = xml.Unmarshal(data, v) return xml.Unmarshal(data, v)
if err != nil {
return err
}
return nil
} }
// Response executes request client gets response mannually. // Response executes request client gets response mannually.

View File

@ -1,100 +1,216 @@
// Beego (http://beego.me) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package httplib
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"os"
"strings"
"testing" "testing"
) )
func TestGetUrl(t *testing.T) { func TestResponse(t *testing.T) {
resp, err := Get("http://beego.me").Debug(true).Response() req := Get("http://httpbin.org/get")
resp, err := req.Response()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if resp.Body == nil { t.Log(resp)
t.Fatal("body is nil") }
}
data, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
t.Fatal(err)
}
if len(data) == 0 {
t.Fatal("data is no")
}
str, err := Get("http://beego.me").String() func TestGet(t *testing.T) {
req := Get("http://httpbin.org/get")
b, err := req.Bytes()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(str) == 0 { t.Log(b)
t.Fatal("has no info")
s, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(s)
if string(b) != s {
t.Fatal("request data not match")
} }
} }
func ExamplePost(t *testing.T) { func TestSimplePost(t *testing.T) {
b := Post("http://beego.me/").Debug(true) v := "smallfish"
b.Param("username", "astaxie") req := Post("http://httpbin.org/post")
b.Param("password", "hello") req.Param("username", v)
b.PostFile("uploadfile", "httplib_test.go")
str, err := b.String() str, err := req.String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
fmt.Println(str) t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in post")
}
} }
func TestSimpleGetString(t *testing.T) { //func TestPostFile(t *testing.T) {
fmt.Println("TestSimpleGetString==========================================") // v := "smallfish"
html, err := Get("http://httpbin.org/headers").SetAgent("beegoooooo").String() // req := Post("http://httpbin.org/post")
// req.Debug(true)
// 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
fmt.Println(html) t.Log(str)
fmt.Println("TestSimpleGetString==========================================")
} }
func TestSimpleGetStringWithDefaultCookie(t *testing.T) { func TestSimpleDelete(t *testing.T) {
fmt.Println("TestSimpleGetStringWithDefaultCookie==========================================") str, err := Delete("http://httpbin.org/delete").String()
html, err := Get("http://httpbin.org/cookies/set?k1=v1").SetEnableCookie(true).String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
fmt.Println(html) t.Log(str)
html, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
if err != nil {
t.Fatal(err)
}
fmt.Println(html)
fmt.Println("TestSimpleGetStringWithDefaultCookie==========================================")
} }
func TestDefaultSetting(t *testing.T) { func TestWithCookie(t *testing.T) {
fmt.Println("TestDefaultSetting==========================================") v := "smallfish"
var def BeegoHttpSettings str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
def.EnableCookie = true if err != nil {
//def.ShowDebug = true t.Fatal(err)
def.UserAgent = "UserAgent" }
//def.ConnectTimeout = 60*time.Second t.Log(str)
//def.ReadWriteTimeout = 60*time.Second
def.Transport = nil //http.DefaultTransport
SetDefaultSetting(def)
html, err := Get("http://httpbin.org/headers").String() str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
fmt.Println(html) t.Log(str)
html, err = Get("http://httpbin.org/headers").String()
if err != nil { n := strings.Index(str, v)
t.Fatal(err) if n == -1 {
t.Fatal(v + " not found in cookie")
} }
fmt.Println(html) }
fmt.Println("TestDefaultSetting==========================================")
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)
}
}
func TestHeader(t *testing.T) {
req := Get("http://httpbin.org/headers")
req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
} }

95
log.go
View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
@ -14,12 +22,14 @@ import (
// Log levels to control the logging output. // Log levels to control the logging output.
const ( const (
LevelTrace = iota LevelEmergency = iota
LevelDebug LevelAlert
LevelInfo
LevelWarning
LevelError
LevelCritical LevelCritical
LevelError
LevelWarning
LevelNotice
LevelInformational
LevelDebug
) )
// SetLogLevel sets the global log level used by the simple // SetLogLevel sets the global log level used by the simple
@ -45,29 +55,12 @@ func SetLogger(adaptername string, config string) error {
return nil return nil
} }
// Trace logs a message at trace level. func Emergency(v ...interface{}) {
func Trace(v ...interface{}) { BeeLogger.Emergency(generateFmtStr(len(v)), v...)
BeeLogger.Trace(generateFmtStr(len(v)), v...)
} }
// Debug logs a message at debug level. func Alert(v ...interface{}) {
func Debug(v ...interface{}) { BeeLogger.Alert(generateFmtStr(len(v)), v...)
BeeLogger.Debug(generateFmtStr(len(v)), v...)
}
// Info logs a message at info level.
func Info(v ...interface{}) {
BeeLogger.Info(generateFmtStr(len(v)), v...)
}
// Warning logs a message at warning level.
func Warn(v ...interface{}) {
BeeLogger.Warn(generateFmtStr(len(v)), v...)
}
// Error logs a message at error level.
func Error(v ...interface{}) {
BeeLogger.Error(generateFmtStr(len(v)), v...)
} }
// Critical logs a message at critical level. // Critical logs a message at critical level.
@ -75,6 +68,46 @@ func Critical(v ...interface{}) {
BeeLogger.Critical(generateFmtStr(len(v)), v...) 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 { func generateFmtStr(n int) string {
return strings.Repeat("%v ", n) return strings.Repeat("%v ", n)
} }

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs
@ -35,17 +43,13 @@ func NewConn() LoggerInterface {
// init connection writer with json config. // init connection writer with json config.
// json config only need key "level". // json config only need key "level".
func (c *ConnWriter) Init(jsonconfig string) error { func (c *ConnWriter) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), c) return json.Unmarshal([]byte(jsonconfig), c)
if err != nil {
return err
}
return nil
} }
// write message in connection. // write message in connection.
// if connection is down, try to re-connect. // if connection is down, try to re-connect.
func (c *ConnWriter) WriteMsg(msg string, level int) error { func (c *ConnWriter) WriteMsg(msg string, level int) error {
if level < c.Level { if level > c.Level {
return nil return nil
} }
if c.neddedConnectOnMsg() { if c.neddedConnectOnMsg() {
@ -69,10 +73,9 @@ func (c *ConnWriter) Flush() {
// destroy connection writer and close tcp listener. // destroy connection writer and close tcp listener.
func (c *ConnWriter) Destroy() { func (c *ConnWriter) Destroy() {
if c.innerWriter == nil { if c.innerWriter != nil {
return c.innerWriter.Close()
} }
c.innerWriter.Close()
} }
func (c *ConnWriter) connect() error { func (c *ConnWriter) connect() error {

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs
@ -13,5 +21,5 @@ import (
func TestConn(t *testing.T) { func TestConn(t *testing.T) {
log := NewLogger(1000) log := NewLogger(1000)
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`) log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
log.Info("info") log.Informational("informational")
} }

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs
@ -24,12 +32,14 @@ func NewBrush(color string) Brush {
} }
var colors = []Brush{ var colors = []Brush{
NewBrush("1;36"), // Trace cyan NewBrush("1;37"), // Emergency white
NewBrush("1;34"), // Debug blue NewBrush("1;36"), // Alert cyan
NewBrush("1;32"), // Info green NewBrush("1;35"), // Critical magenta
NewBrush("1;33"), // Warn yellow
NewBrush("1;31"), // Error red NewBrush("1;31"), // Error red
NewBrush("1;35"), // Critical purple 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. // ConsoleWriter implements LoggerInterface and writes messages to terminal.
@ -40,9 +50,10 @@ type ConsoleWriter struct {
// create ConsoleWriter returning as LoggerInterface. // create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface { func NewConsole() LoggerInterface {
cw := new(ConsoleWriter) cw := &ConsoleWriter{
cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime) lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
cw.Level = LevelTrace Level: LevelDebug,
}
return cw return cw
} }
@ -52,23 +63,20 @@ func (c *ConsoleWriter) Init(jsonconfig string) error {
if len(jsonconfig) == 0 { if len(jsonconfig) == 0 {
return nil return nil
} }
err := json.Unmarshal([]byte(jsonconfig), c) return json.Unmarshal([]byte(jsonconfig), c)
if err != nil {
return err
}
return nil
} }
// write message in console. // write message in console.
func (c *ConsoleWriter) WriteMsg(msg string, level int) error { func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
if level < c.Level { if level > c.Level {
return nil return nil
} }
if goos := runtime.GOOS; goos == "windows" { if goos := runtime.GOOS; goos == "windows" {
c.lg.Println(msg) c.lg.Println(msg)
} else { return nil
c.lg.Println(colors[level](msg))
} }
c.lg.Println(colors[level](msg))
return nil return nil
} }

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs
@ -10,22 +18,29 @@ import (
"testing" "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) { func TestConsole(t *testing.T) {
log := NewLogger(10000) log1 := NewLogger(10000)
log.EnableFuncCallDepth(true) log1.EnableFuncCallDepth(true)
log.SetLogger("console", "") log1.SetLogger("console", "")
log.Trace("trace") testConsoleCalls(log1)
log.Info("info")
log.Warn("warning")
log.Debug("debug")
log.Critical("critical")
log2 := NewLogger(100) log2 := NewLogger(100)
log2.SetLogger("console", `{"level":1}`) log2.SetLogger("console", `{"level":3}`)
log.Trace("trace") testConsoleCalls(log2)
log.Info("info")
log.Warn("warning")
log.Debug("debug")
log.Critical("critical")
} }
func BenchmarkConsole(b *testing.B) { func BenchmarkConsole(b *testing.B) {
@ -33,6 +48,6 @@ func BenchmarkConsole(b *testing.B) {
log.EnableFuncCallDepth(true) log.EnableFuncCallDepth(true)
log.SetLogger("console", "") log.SetLogger("console", "")
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
log.Trace("trace") log.Debug("debug")
} }
} }

View File

@ -1,16 +1,25 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -114,11 +123,7 @@ func (w *FileLogWriter) startLogger() error {
return err return err
} }
w.mw.SetFd(fd) w.mw.SetFd(fd)
err = w.initFd() return w.initFd()
if err != nil {
return err
}
return nil
} }
func (w *FileLogWriter) docheck(size int) { func (w *FileLogWriter) docheck(size int) {
@ -138,7 +143,7 @@ func (w *FileLogWriter) docheck(size int) {
// write logger message into file. // write logger message into file.
func (w *FileLogWriter) WriteMsg(msg string, level int) error { func (w *FileLogWriter) WriteMsg(msg string, level int) error {
if level < w.Level { if level > w.Level {
return nil return nil
} }
n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] " n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
@ -161,18 +166,44 @@ func (w *FileLogWriter) initFd() error {
} }
w.maxsize_cursize = int(finfo.Size()) w.maxsize_cursize = int(finfo.Size())
w.daily_opendate = time.Now().Day() w.daily_opendate = time.Now().Day()
w.maxlines_curlines = 0
if finfo.Size() > 0 { if finfo.Size() > 0 {
content, err := ioutil.ReadFile(w.Filename) count, err := w.lines()
if err != nil { if err != nil {
return err return err
} }
w.maxlines_curlines = len(strings.Split(string(content), "\n")) w.maxlines_curlines = count
} else {
w.maxlines_curlines = 0
} }
return nil return nil
} }
func (w *FileLogWriter) lines() (int, error) {
fd, err := os.Open(w.Filename)
if err != nil {
return 0, err
}
defer fd.Close()
buf := make([]byte, 32768) // 32k
count := 0
lineSep := []byte{'\n'}
for {
c, err := fd.Read(buf)
if err != nil && err != io.EOF {
return count, err
}
count += bytes.Count(buf[:c], lineSep)
if err == io.EOF {
break
}
}
return count, nil
}
// DoRotate means it need to write file in new file. // DoRotate means it need to write file in new file.
// new file name like xx.log.2013-01-01.2 // new file name like xx.log.2013-01-01.2
func (w *FileLogWriter) DoRotate() error { func (w *FileLogWriter) DoRotate() error {
@ -218,13 +249,20 @@ func (w *FileLogWriter) DoRotate() error {
func (w *FileLogWriter) deleteOldLog() { func (w *FileLogWriter) deleteOldLog() {
dir := filepath.Dir(w.Filename) dir := filepath.Dir(w.Filename)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 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 !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) { if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
os.Remove(path) os.Remove(path)
} }
} }
return nil return
}) })
} }

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs
@ -10,6 +18,7 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"os" "os"
"strconv"
"testing" "testing"
"time" "time"
) )
@ -17,12 +26,14 @@ import (
func TestFile(t *testing.T) { func TestFile(t *testing.T) {
log := NewLogger(10000) log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test.log"}`) log.SetLogger("file", `{"filename":"test.log"}`)
log.Trace("test")
log.Info("info")
log.Debug("debug") log.Debug("debug")
log.Warn("warning") log.Informational("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error") log.Error("error")
log.Alert("alert")
log.Critical("critical") log.Critical("critical")
log.Emergency("emergency")
time.Sleep(time.Second * 4) time.Sleep(time.Second * 4)
f, err := os.Open("test.log") f, err := os.Open("test.log")
if err != nil { if err != nil {
@ -39,21 +50,24 @@ func TestFile(t *testing.T) {
linenum++ linenum++
} }
} }
if linenum != 6 { var expected = LevelDebug + 1
t.Fatal(linenum, "not line 6") if linenum != expected {
t.Fatal(linenum, "not "+strconv.Itoa(expected)+" lines")
} }
os.Remove("test.log") os.Remove("test.log")
} }
func TestFile2(t *testing.T) { func TestFile2(t *testing.T) {
log := NewLogger(10000) log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test2.log","level":2}`) log.SetLogger("file", fmt.Sprintf(`{"filename":"test2.log","level":%d}`, LevelError))
log.Trace("test")
log.Info("info")
log.Debug("debug") log.Debug("debug")
log.Warn("warning") log.Info("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error") log.Error("error")
log.Alert("alert")
log.Critical("critical") log.Critical("critical")
log.Emergency("emergency")
time.Sleep(time.Second * 4) time.Sleep(time.Second * 4)
f, err := os.Open("test2.log") f, err := os.Open("test2.log")
if err != nil { if err != nil {
@ -70,8 +84,9 @@ func TestFile2(t *testing.T) {
linenum++ linenum++
} }
} }
if linenum != 4 { var expected = LevelError + 1
t.Fatal(linenum, "not line 4") if linenum != expected {
t.Fatal(linenum, "not "+strconv.Itoa(expected)+" lines")
} }
os.Remove("test2.log") os.Remove("test2.log")
} }
@ -79,17 +94,19 @@ func TestFile2(t *testing.T) {
func TestFileRotate(t *testing.T) { func TestFileRotate(t *testing.T) {
log := NewLogger(10000) log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`) log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`)
log.Trace("test")
log.Info("info")
log.Debug("debug") log.Debug("debug")
log.Warn("warning") log.Info("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error") log.Error("error")
log.Alert("alert")
log.Critical("critical") log.Critical("critical")
log.Emergency("emergency")
time.Sleep(time.Second * 4) time.Sleep(time.Second * 4)
rotatename := "test3.log" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) rotatename := "test3.log" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1)
b, err := exists(rotatename) b, err := exists(rotatename)
if !b || err != nil { if !b || err != nil {
t.Fatal("rotate not gen") t.Fatal("rotate not generated")
} }
os.Remove(rotatename) os.Remove(rotatename)
os.Remove("test3.log") os.Remove("test3.log")
@ -110,7 +127,7 @@ func BenchmarkFile(b *testing.B) {
log := NewLogger(100000) log := NewLogger(100000)
log.SetLogger("file", `{"filename":"test4.log"}`) log.SetLogger("file", `{"filename":"test4.log"}`)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
log.Trace("trace") log.Debug("debug")
} }
os.Remove("test4.log") os.Remove("test4.log")
} }

View File

@ -1,9 +1,35 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs
import ( import (
@ -13,14 +39,25 @@ import (
"sync" "sync"
) )
// RFC5424 log message levels.
const ( const (
// log message levels LevelEmergency = iota
LevelTrace = iota LevelAlert
LevelDebug
LevelInfo
LevelWarn
LevelError
LevelCritical 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 type loggerType func() LoggerInterface
@ -69,6 +106,7 @@ type logMsg struct {
// if the buffering chan is full, logger adapters write to file or other way. // if the buffering chan is full, logger adapters write to file or other way.
func NewLogger(channellen int64) *BeeLogger { func NewLogger(channellen int64) *BeeLogger {
bl := new(BeeLogger) bl := new(BeeLogger)
bl.level = LevelDebug
bl.loggerFuncCallDepth = 2 bl.loggerFuncCallDepth = 2
bl.msg = make(chan *logMsg, channellen) bl.msg = make(chan *logMsg, channellen)
bl.outputs = make(map[string]LoggerInterface) bl.outputs = make(map[string]LoggerInterface)
@ -110,13 +148,16 @@ func (bl *BeeLogger) DelLogger(adaptername string) error {
} }
func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
if bl.level > loglevel { if loglevel > bl.level {
return nil return nil
} }
lm := new(logMsg) lm := new(logMsg)
lm.level = loglevel lm.level = loglevel
if bl.enableFuncCallDepth { if bl.enableFuncCallDepth {
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) {
_, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1)
}
if ok { if ok {
_, filename := path.Split(file) _, filename := path.Split(file)
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
@ -130,8 +171,10 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
return nil return nil
} }
// set log message level. // Set log message level.
// if message level (such as LevelTrace) is less than logger level (such as LevelWarn), ignore message. //
// 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) { func (bl *BeeLogger) SetLevel(l int) {
bl.level = l bl.level = l
} }
@ -147,52 +190,88 @@ func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
} }
// start logger chan reading. // start logger chan reading.
// when chan is full, write logs. // when chan is not empty, write logs.
func (bl *BeeLogger) startLogger() { func (bl *BeeLogger) startLogger() {
for { for {
select { select {
case bm := <-bl.msg: case bm := <-bl.msg:
for _, l := range bl.outputs { for _, l := range bl.outputs {
l.WriteMsg(bm.msg, bm.level) err := l.WriteMsg(bm.msg, bm.level)
if err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
} }
} }
} }
} }
// log trace level message. // Log EMERGENCY level message.
func (bl *BeeLogger) Trace(format string, v ...interface{}) { func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
msg := fmt.Sprintf("[T] "+format, v...) msg := fmt.Sprintf("[M] "+format, v...)
bl.writerMsg(LevelTrace, msg) bl.writerMsg(LevelEmergency, msg)
} }
// log debug level message. // Log ALERT level message.
func (bl *BeeLogger) Debug(format string, v ...interface{}) { func (bl *BeeLogger) Alert(format string, v ...interface{}) {
msg := fmt.Sprintf("[D] "+format, v...) msg := fmt.Sprintf("[A] "+format, v...)
bl.writerMsg(LevelDebug, msg) bl.writerMsg(LevelAlert, msg)
} }
// log info level message. // Log CRITICAL level message.
func (bl *BeeLogger) Info(format string, v ...interface{}) { func (bl *BeeLogger) Critical(format string, v ...interface{}) {
msg := fmt.Sprintf("[I] "+format, v...) msg := fmt.Sprintf("[C] "+format, v...)
bl.writerMsg(LevelInfo, msg) bl.writerMsg(LevelCritical, msg)
} }
// log warn level message. // Log ERROR level message.
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
msg := fmt.Sprintf("[W] "+format, v...)
bl.writerMsg(LevelWarn, msg)
}
// log error level message.
func (bl *BeeLogger) Error(format string, v ...interface{}) { func (bl *BeeLogger) Error(format string, v ...interface{}) {
msg := fmt.Sprintf("[E] "+format, v...) msg := fmt.Sprintf("[E] "+format, v...)
bl.writerMsg(LevelError, msg) bl.writerMsg(LevelError, msg)
} }
// log critical level message. // Log WARNING level message.
func (bl *BeeLogger) Critical(format string, v ...interface{}) { func (bl *BeeLogger) Warning(format string, v ...interface{}) {
msg := fmt.Sprintf("[C] "+format, v...) msg := fmt.Sprintf("[W] "+format, v...)
bl.writerMsg(LevelCritical, msg) 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. // flush all chan data.
@ -208,11 +287,14 @@ func (bl *BeeLogger) Close() {
if len(bl.msg) > 0 { if len(bl.msg) > 0 {
bm := <-bl.msg bm := <-bl.msg
for _, l := range bl.outputs { for _, l := range bl.outputs {
l.WriteMsg(bm.msg, bm.level) err := l.WriteMsg(bm.msg, bm.level)
if err != nil {
fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
}
} }
} else { continue
break
} }
break
} }
for _, l := range bl.outputs { for _, l := range bl.outputs {
l.Flush() l.Flush()

View File

@ -1,21 +1,32 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs
import ( import (
"crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"net/smtp" "net/smtp"
"strings" "strings"
"time" "time"
) )
const ( const (
subjectPhrase = "Diagnostic message from server" // no usage
// subjectPhrase = "Diagnostic message from server"
) )
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server. // smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
@ -24,6 +35,7 @@ type SmtpWriter struct {
Password string `json:"password"` Password string `json:"password"`
Host string `json:"Host"` Host string `json:"Host"`
Subject string `json:"subject"` Subject string `json:"subject"`
FromAddress string `json:"fromAddress"`
RecipientAddresses []string `json:"sendTos"` RecipientAddresses []string `json:"sendTos"`
Level int `json:"level"` Level int `json:"level"`
} }
@ -40,6 +52,7 @@ func NewSmtpWriter() LoggerInterface {
// "password:"password", // "password:"password",
// "host":"smtp.gmail.com:465", // "host":"smtp.gmail.com:465",
// "subject":"email title", // "subject":"email title",
// "fromAddress":"from@example.com",
// "sendTos":["email1","email2"], // "sendTos":["email1","email2"],
// "level":LevelError // "level":LevelError
// } // }
@ -51,37 +64,90 @@ func (s *SmtpWriter) Init(jsonconfig string) error {
return nil 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. // write message in smtp writer.
// it will send an email with subject and only this message. // it will send an email with subject and only this message.
func (s *SmtpWriter) WriteMsg(msg string, level int) error { func (s *SmtpWriter) WriteMsg(msg string, level int) error {
if level < s.Level { if level > s.Level {
return nil return nil
} }
hp := strings.Split(s.Host, ":") hp := strings.Split(s.Host, ":")
// Set up authentication information. // Set up authentication information.
auth := smtp.PlainAuth( auth := s.GetSmtpAuth(hp[0])
"",
s.Username,
s.Password,
hp[0],
)
// Connect to the server, authenticate, set the sender and recipient, // Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step. // and send the email all in one step.
content_type := "Content-Type: text/plain" + "; charset=UTF-8" content_type := "Content-Type: text/plain" + "; charset=UTF-8"
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.Username + "<" + s.Username + 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) ">\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 := smtp.SendMail( return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
s.Host,
auth,
s.Username,
s.RecipientAddresses,
mailmsg,
)
return err
} }
// implementing method. empty. // implementing method. empty.

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package logs

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego

View File

@ -1,330 +0,0 @@
// 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 middleware
import (
"fmt"
"html/template"
"net/http"
"runtime"
"strconv"
)
var (
AppName string
VERSION string
)
var tpl = `
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>beego application error</title>
<style>
html, body, body * {padding: 0; margin: 0;}
#header {background:#ffd; border-bottom:solid 2px #A31515; padding: 20px 10px;}
#header h2{ }
#footer {border-top:solid 1px #aaa; padding: 5px 10px; font-size: 12px; color:green;}
#content {padding: 5px;}
#content .stack b{ font-size: 13px; color: red;}
#content .stack pre{padding-left: 10px;}
table {}
td.t {text-align: right; padding-right: 5px; color: #888;}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<div id="header">
<h2>{{.AppError}}</h2>
</div>
<div id="content">
<table>
<tr>
<td class="t">Request Method: </td><td>{{.RequestMethod}}</td>
</tr>
<tr>
<td class="t">Request URL: </td><td>{{.RequestURL}}</td>
</tr>
<tr>
<td class="t">RemoteAddr: </td><td>{{.RemoteAddr }}</td>
</tr>
</table>
<div class="stack">
<b>Stack</b>
<pre>{{.Stack}}</pre>
</div>
</div>
<div id="footer">
<p>beego {{ .BeegoVersion }} (beego framework)</p>
<p>golang version: {{.GoVersion}}</p>
</div>
</body>
</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)
data["AppError"] = AppName + ":" + fmt.Sprint(err)
data["RequestMethod"] = r.Method
data["RequestURL"] = r.RequestURI
data["RemoteAddr"] = r.RemoteAddr
data["Stack"] = Stack
data["BeegoVersion"] = VERSION
data["GoVersion"] = runtime.Version()
rw.WriteHeader(500)
t.Execute(rw, data)
}
var errtpl = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{.Title}}</title>
<style type="text/css">
* {
margin:0;
padding:0;
}
body {
background-color:#EFEFEF;
font: .9em "Lucida Sans Unicode", "Lucida Grande", sans-serif;
}
#wrapper{
width:600px;
margin:40px auto 0;
text-align:center;
-moz-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
-webkit-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
}
#wrapper h1{
color:#FFF;
text-align:center;
margin-bottom:20px;
}
#wrapper a{
display:block;
font-size:.9em;
padding-top:20px;
color:#FFF;
text-decoration:none;
text-align:center;
}
#container {
width:600px;
padding-bottom:15px;
background-color:#FFFFFF;
}
.navtop{
height:40px;
background-color:#24B2EB;
padding:13px;
}
.content {
padding:10px 10px 25px;
background: #FFFFFF;
margin:;
color:#333;
}
a.button{
color:white;
padding:15px 20px;
text-shadow:1px 1px 0 #00A5FF;
font-weight:bold;
text-align:center;
border:1px solid #24B2EB;
margin:0px 200px;
clear:both;
background-color: #24B2EB;
border-radius:100px;
-moz-border-radius:100px;
-webkit-border-radius:100px;
}
a.button:hover{
text-decoration:none;
background-color: #24B2EB;
}
</style>
</head>
<body>
<div id="wrapper">
<div id="container">
<div class="navtop">
<h1>{{.Title}}</h1>
</div>
<div id="content">
{{.Content}}
<a href="/" title="Home" class="button">Go Home</a><br />
<br>Powered by beego {{.BeegoVersion}}
</div>
</div>
</div>
</body>
</html>
`
// map of http handlers for each error string.
var ErrorMaps map[string]http.HandlerFunc
func init() {
ErrorMaps = make(map[string]http.HandlerFunc)
}
// 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 has flown the coop." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The page has moved" +
"<br>The page no longer exists" +
"<br>You were looking for your puppy and got lost" +
"<br>You like 404 pages" +
"</ul>")
data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusNotFound)
t.Execute(rw, data)
}
// 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 be authorized." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<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)
}
// 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 is forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Your address may be blocked" +
"<br>The site may be disabled" +
"<br>You need to log in" +
"</ul>")
data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusForbidden)
t.Execute(rw, data)
}
// 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 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)
}
// 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 is down right now." +
"<br><br><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)
}
// 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
}
if _, ok := ErrorMaps["401"]; !ok {
ErrorMaps["401"] = Unauthorized
}
if _, ok := ErrorMaps["403"]; !ok {
ErrorMaps["403"] = Forbidden
}
if _, ok := ErrorMaps["503"]; !ok {
ErrorMaps["503"] = ServiceUnavailable
}
if _, ok := ErrorMaps["500"]; !ok {
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.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
}
}

View File

@ -1,41 +0,0 @@
// 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 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"}
}

View File

@ -1,90 +1,71 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package middleware
//import ( import (
// "github.com/astaxie/beego/config" "encoding/json"
// "os" "io/ioutil"
// "path" "os"
//) )
//type Translation struct { type Translation struct {
// filetype string filepath string
// CurrentLocal string CurrentLocal string
// Locales map[string]map[string]string Locales map[string]map[string]string
//} }
//func NewLocale(filetype string) *Translation { func NewLocale(filepath string, defaultlocal string) *Translation {
// return &Translation{ file, err := os.Open(filepath)
// filetype: filetype, if err != nil {
// CurrentLocal: "zh", panic("open " + filepath + " err :" + err.Error())
// Locales: make(map[string]map[string]string), }
// } data, err := ioutil.ReadAll(file)
//} if err != nil {
panic("read " + filepath + " err :" + err.Error())
}
//func (t *Translation) loadTranslations(dirPath string) error { i18n := make(map[string]map[string]string)
// dir, err := os.Open(dirPath) if err = json.Unmarshal(data, &i18n); err != nil {
// if err != nil { panic("json.Unmarshal " + filepath + " err :" + err.Error())
// return err }
// } return &Translation{
// defer dir.Close() filepath: filepath,
CurrentLocal: defaultlocal,
Locales: i18n,
}
}
// names, err := dir.Readdirnames(-1) func (t *Translation) SetLocale(local string) {
// if err != nil { t.CurrentLocal = local
// return err }
// }
// for _, name := range names { func (t *Translation) Translate(key string, local string) string {
// fullPath := path.Join(dirPath, name) if local == "" {
local = t.CurrentLocal
// fi, err := os.Stat(fullPath) }
// if err != nil { if ct, ok := t.Locales[key]; ok {
// return err if v, o := ct[local]; o {
// } return v
}
// if fi.IsDir() { }
// continue return key
// } else { }
// if err := t.loadTranslation(fullPath, name); err != nil {
// return err
// }
// }
// }
// return nil
//}
//func (t *Translation) loadTranslation(fullPath, locale string) error {
// sourceKey2Trans, ok := t.Locales[locale]
// if !ok {
// sourceKey2Trans = make(map[string]string)
// t.Locales[locale] = sourceKey2Trans
// }
// for _, m := range trf.Messages {
// if m.Translation != "" {
// sourceKey2Trans[sourceKey(m.Source, m.Context)] = m.Translation
// }
// }
// return nil
//}
//func (t *Translation) SetLocale(local string) {
// t.CurrentLocal = local
//}
//func (t *Translation) Translate(key string) string {
// if ct, ok := t.Locales[t.CurrentLocal]; ok {
// if v, o := ct[key]; o {
// return v
// }
// }
// return key
//}

46
migration/ddl.go Normal file
View File

@ -0,0 +1,46 @@
// 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 migration
type Table struct {
TableName string
Columns []*Column
}
func (t *Table) Create() string {
return ""
}
func (t *Table) Drop() string {
return ""
}
type Column struct {
Name string
Type string
Default interface{}
}
func Create(tbname string, columns ...Column) string {
return ""
}
func Drop(tbname string, columns ...Column) string {
return ""
}
func TableDDL(tbname string, columns ...Column) string {
return ""
}

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
}

18
mime.go
View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego

View File

@ -1,15 +1,24 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego
import ( import (
"net/http" "net/http"
"strings"
beecontext "github.com/astaxie/beego/context" beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
) )
type namespaceCond func(*beecontext.Context) bool type namespaceCond func(*beecontext.Context) bool
@ -47,7 +56,7 @@ func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
func (n *Namespace) Cond(cond namespaceCond) *Namespace { func (n *Namespace) Cond(cond namespaceCond) *Namespace {
fn := func(ctx *beecontext.Context) { fn := func(ctx *beecontext.Context) {
if !cond(ctx) { if !cond(ctx) {
middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed") exception("405", ctx)
} }
} }
if v, ok := n.handlers.filters[BeforeRouter]; ok { if v, ok := n.handlers.filters[BeforeRouter]; ok {
@ -207,7 +216,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
n.handlers.routers[k] = t n.handlers.routers[k] = t
} }
} }
if n.handlers.enableFilter { if ni.handlers.enableFilter {
for pos, filterList := range ni.handlers.filters { for pos, filterList := range ni.handlers.filters {
for _, mr := range filterList { for _, mr := range filterList {
t := NewTree() t := NewTree()
@ -258,7 +267,9 @@ func addPrefix(t *Tree, prefix string) {
} }
for _, l := range t.leaves { for _, l := range t.leaves {
if c, ok := l.runObject.(*controllerInfo); ok { if c, ok := l.runObject.(*controllerInfo); ok {
c.pattern = prefix + c.pattern if !strings.HasPrefix(c.pattern, prefix) {
c.pattern = prefix + c.pattern
}
} }
} }

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors astaxie // 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 package beego

View File

@ -6,8 +6,6 @@ A powerful orm framework for go.
It is heavily influenced by Django ORM, SQLAlchemy. It is heavily influenced by Django ORM, SQLAlchemy.
now, beta, unstable, may be changing some api make your app build failed.
**Support Database:** **Support Database:**
* MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) * MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
@ -119,7 +117,7 @@ o.Begin()
... ...
user := User{Name: "slene"} user := User{Name: "slene"}
id, err := o.Insert(&user) id, err := o.Insert(&user)
if err != nil { if err == nil {
o.Commit() o.Commit()
} else { } else {
o.Rollback() o.Rollback()
@ -154,10 +152,5 @@ note: not recommend use this in product env.
more details and examples in docs and test more details and examples in docs and test
* [中文](http://beego.me/docs/Models_Overview?lang=zh) [documents](http://beego.me/docs/mvc/model/overview.md)
* [English](http://beego.me/docs/Models_Overview?lang=en)
## TODO
- some unrealized api
- examples
- docs

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm
@ -96,7 +104,11 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string {
typ += " " + "NOT NULL" typ += " " + "NOT NULL"
} }
return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s", Q, fi.mi.table, Q, Q, fi.column, Q, typ) return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s %s",
Q, fi.mi.table, Q,
Q, fi.column, Q,
typ, getColumnDefault(fi),
)
} }
// create database creation string. // create database creation string.
@ -144,6 +156,13 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
column += " " + "NOT NULL" column += " " + "NOT NULL"
} }
//if fi.initial.String() != "" {
// column += " DEFAULT " + fi.initial.String()
//}
// Append attribute DEFAULT
column += getColumnDefault(fi)
if fi.unique { if fi.unique {
column += " " + "UNIQUE" column += " " + "UNIQUE"
} }
@ -227,3 +246,44 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
return return
} }
// Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands
func getColumnDefault(fi *fieldInfo) string {
var (
v, t, d string
)
// Skip default attribute if field is in relations
if fi.rel || fi.reverse {
return v
}
t = " DEFAULT '%s' "
// These defaults will be useful if there no config value orm:"default" and NOT NULL is on
switch fi.fieldType {
case TypeDateField, TypeDateTimeField:
return v;
case TypeBooleanField, TypeBitField, TypeSmallIntegerField, TypeIntegerField,
TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField,
TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField,
TypeDecimalField:
d = "0"
}
if fi.colDefault {
if !fi.initial.Exist() {
v = fmt.Sprintf(t, "")
} else {
v = fmt.Sprintf(t, fi.initial.String())
}
} else {
if !fi.null {
v = fmt.Sprintf(t, d)
}
}
return v
}

122
orm/db.go
View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm
@ -114,6 +122,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
if nb.Valid { if nb.Valid {
value = nb.Bool value = nb.Bool
} }
} else if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().Bool()
}
} else { } else {
value = field.Bool() value = field.Bool()
} }
@ -123,6 +137,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
if ns.Valid { if ns.Valid {
value = ns.String value = ns.String
} }
} else if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().String()
}
} else { } else {
value = field.String() value = field.String()
} }
@ -132,6 +152,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
if nf.Valid { if nf.Valid {
value = nf.Float64 value = nf.Float64
} }
} else if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().Float()
}
} else { } else {
vu := field.Interface() vu := field.Interface()
if _, ok := vu.(float32); ok { if _, ok := vu.(float32); ok {
@ -153,13 +179,27 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
default: default:
switch { switch {
case fi.fieldType&IsPostiveIntegerField > 0: case fi.fieldType&IsPostiveIntegerField > 0:
value = field.Uint() if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().Uint()
}
} else {
value = field.Uint()
}
case fi.fieldType&IsIntegerField > 0: case fi.fieldType&IsIntegerField > 0:
if ni, ok := field.Interface().(sql.NullInt64); ok { if ni, ok := field.Interface().(sql.NullInt64); ok {
value = nil value = nil
if ni.Valid { if ni.Valid {
value = ni.Int64 value = ni.Int64
} }
} else if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().Int()
}
} else { } else {
value = field.Int() value = field.Int()
} }
@ -1169,6 +1209,11 @@ setValue:
nb.Valid = true nb.Valid = true
} }
field.Set(reflect.ValueOf(nb)) field.Set(reflect.ValueOf(nb))
} else if field.Kind() == reflect.Ptr {
if value != nil {
v := value.(bool)
field.Set(reflect.ValueOf(&v))
}
} else { } else {
if value == nil { if value == nil {
value = false value = false
@ -1186,6 +1231,11 @@ setValue:
ns.Valid = true ns.Valid = true
} }
field.Set(reflect.ValueOf(ns)) field.Set(reflect.ValueOf(ns))
} else if field.Kind() == reflect.Ptr {
if value != nil {
v := value.(string)
field.Set(reflect.ValueOf(&v))
}
} else { } else {
if value == nil { if value == nil {
value = "" value = ""
@ -1200,6 +1250,56 @@ setValue:
} }
field.Set(reflect.ValueOf(value)) field.Set(reflect.ValueOf(value))
} }
case fieldType == TypePositiveBitField && field.Kind() == reflect.Ptr:
if value != nil {
v := uint8(value.(uint64))
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypePositiveSmallIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
v := uint16(value.(uint64))
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypePositiveIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
if field.Type() == reflect.TypeOf(new(uint)) {
v := uint(value.(uint64))
field.Set(reflect.ValueOf(&v))
} else {
v := uint32(value.(uint64))
field.Set(reflect.ValueOf(&v))
}
}
case fieldType == TypePositiveBigIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
v := value.(uint64)
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypeBitField && field.Kind() == reflect.Ptr:
if value != nil {
v := int8(value.(int64))
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypeSmallIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
v := int16(value.(int64))
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypeIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
if field.Type() == reflect.TypeOf(new(int)) {
v := int(value.(int64))
field.Set(reflect.ValueOf(&v))
} else {
v := int32(value.(int64))
field.Set(reflect.ValueOf(&v))
}
}
case fieldType == TypeBigIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
v := value.(int64)
field.Set(reflect.ValueOf(&v))
}
case fieldType&IsIntegerField > 0: case fieldType&IsIntegerField > 0:
if fieldType&IsPostiveIntegerField > 0 { if fieldType&IsPostiveIntegerField > 0 {
if isNative { if isNative {
@ -1236,6 +1336,16 @@ setValue:
nf.Valid = true nf.Valid = true
} }
field.Set(reflect.ValueOf(nf)) field.Set(reflect.ValueOf(nf))
} else if field.Kind() == reflect.Ptr {
if value != nil {
if field.Type() == reflect.TypeOf(new(float32)) {
v := float32(value.(float64))
field.Set(reflect.ValueOf(&v))
} else {
v := value.(float64)
field.Set(reflect.ValueOf(&v))
}
}
} else { } else {
if value == nil { if value == nil {

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm
@ -136,7 +144,7 @@ func detectTZ(al *alias) {
if engine != "" { if engine != "" {
al.Engine = engine al.Engine = engine
} else { } else {
engine = "INNODB" al.Engine = "INNODB"
} }
case DR_Sqlite: case DR_Sqlite:

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,43 +0,0 @@
## 命令模式
注册模型与数据库以后,调用 RunCommand 执行 orm 命令
```go
func main() {
// orm.RegisterModel...
// orm.RegisterDataBase...
...
orm.RunCommand()
}
```
```bash
go build main.go
./main orm
# 直接执行可以显示帮助
# 如果你的程序可以支持的话,直接运行 go run main.go orm 也是一样的效果
```
## 自动建表
```bash
./main orm syncdb -h
Usage of orm command: syncdb:
-db="default": DataBase alias name
-force=false: drop tables before create
-v=false: verbose info
```
使用 `-force=1` 可以 drop table 后再建表
使用 `-v` 可以查看执行的 sql 语句
## 打印建表SQL
```bash
./main orm sqlall -h
Usage of orm command: syncdb:
-db="default": DataBase alias name
```
默认使用别名为 default 的数据库

View File

@ -1,38 +0,0 @@
## Custom Fields
TypeBooleanField = 1 << iota
// string
TypeCharField
// string
TypeTextField
// time.Time
TypeDateField
// time.Time
TypeDateTimeField
// int16
TypeSmallIntegerField
// int32
TypeIntegerField
// int64
TypeBigIntegerField
// uint16
TypePositiveSmallIntegerField
// uint32
TypePositiveIntegerField
// uint64
TypePositiveBigIntegerField
// float64
TypeFloatField
// float64
TypeDecimalField
RelForeignKey
RelOneToOne
RelManyToMany
RelReverseOne
RelReverseMany

View File

@ -1,288 +0,0 @@
## 模型定义
复杂的模型定义不是必须的,此功能用作数据库数据转换和[自动建表](Cmd.md#自动建表)
默认的表名使用驼峰转蛇形,比如 AuthUser -> auth_user
**自定义表名**
```go
type User struct {
Id int
Name string
}
func (u *User) TableName() string {
return "auth_user"
}
```
如果[前缀设置](Orm.md#registermodelwithprefix)为`prefix_`那么表名为prefix_auth_user
## Struct Tag 设置参数
```go
orm:"null;rel(fk)"
```
多个设置间使用 `;` 分隔,设置的值如果是多个,使用 `,` 分隔。
#### 忽略字段
设置 `-` 即可忽略 struct 中的字段
```go
type User struct {
...
AnyField string `orm:"-"`
...
```
#### auto
当 Field 类型为 int, int32, int64 时,可以设置字段为自增健
当模型定义里没有主键时,符合上述类型且名称为 `Id` 的 Field 将被视为自增健。
#### pk
设置为主键,适用于自定义其他类型为主键
#### null
数据库表默认为 `NOT NULL`,设置 null 代表 `ALLOW NULL`
#### blank
设置 string 类型的字段允许为空,否则 clean 会返回错误
#### index
为字段增加索引
#### unique
为字段增加 unique 键
#### column
为字段设置 db 字段的名称
```go
Name `orm:"column(user_name)"`
```
#### default
为字段设置默认值,类型必须符合
```go
type User struct {
...
Status int `orm:"default(1)"`
```
#### size
string 类型字段默认为 varchar(255)
设置 size 以后db type 将使用 varchar(size)
```go
Title string `orm:"size(60)"`
```
#### digits / decimals
设置 float32, float64 类型的浮点精度
```go
Money float64 `orm:"digits(12);decimals(4)"`
```
总长度 12 小数点后 4 位 eg: `99999999.9999`
#### auto_now / auto_now_add
```go
Created time.Time `auto_now_add`
Updated time.Time `auto_now`
```
* auto_now 每次 model 保存时都会对时间自动更新
* auto_now_add 第一次保存时才设置时间
对于批量的 update 此设置是不生效的
#### type
设置为 date 时time.Time 字段的对应 db 类型使用 date
```go
Created time.Time `orm:"auto_now_add;type(date)"`
```
设置为 text 时string 字段对应的 db 类型使用 text
```go
Content string `orm:"type(text)"`
```
## 表关系设置
#### rel / reverse
**RelOneToOne**:
```go
type User struct {
...
Profile *Profile `orm:"null;rel(one);on_delete(set_null)"`
```
对应的反向关系 **RelReverseOne**:
```go
type Profile struct {
...
User *User `orm:"reverse(one)" json:"-"`
```
**RelForeignKey**:
```go
type Post struct {
...
User*User `orm:"rel(fk)"` // RelForeignKey relation
```
对应的反向关系 **RelReverseMany**:
```go
type User struct {
...
Posts []*Post `orm:"reverse(many)" json:"-"` // fk 的反向关系
```
**RelManyToMany**:
```go
type Post struct {
...
Tags []*Tag `orm:"rel(m2m)"` // ManyToMany relation
```
对应的反向关系 **RelReverseMany**:
```go
type Tag struct {
...
Posts []*Post `orm:"reverse(many)" json:"-"`
```
#### rel_table / rel_through
此设置针对 `orm:"rel(m2m)"` 的关系字段
rel_table 设置自动生成的 m2m 关系表的名称
rel_through 如果要在 m2m 关系中使用自定义的 m2m 关系表
通过这个设置其名称,格式为 pkg.path.ModelName
eg: app.models.PostTagRel
PostTagRel 表需要有到 Post 和 Tag 的关系
当设置 rel_table 时会忽略 rel_through
#### on_delete
设置对应的 rel 关系删除时,如何处理关系字段。
cascade 级联删除(默认值)
set_null 设置为 NULL需要设置 null = true
set_default 设置为默认值,需要设置 default 值
do_nothing 什么也不做,忽略
```go
type User struct {
...
Profile *Profile `orm:"null;rel(one);on_delete(set_null)"`
...
type Profile struct {
...
User *User `orm:"reverse(one)" json:"-"`
// 删除 Profile 时将设置 User.Profile 的数据库字段为 NULL
```
## 模型字段与数据库类型的对应
在此列出 orm 推荐的对应数据库类型,自动建表功能也会以此为标准。
默认所有的字段都是 **NOT NULL**
#### MySQL
| go |mysql
| :--- | :---
| int, int32, int64 - 设置 auto 或者名称为 `Id` 时 | integer AUTO_INCREMENT
| bool | bool
| string - 默认为 size 255 | varchar(size)
| string - 设置 type(text) 时 | longtext
| time.Time - 设置 type 为 date 时 | date
| time.TIme | datetime
| byte | tinyint unsigned
| rune | integer
| int | integer
| int8 | tinyint
| int16 | smallint
| int32 | integer
| int64 | bigint
| uint | integer unsigned
| uint8 | tinyint unsigned
| uint16 | smallint unsigned
| uint32 | integer unsigned
| uint64 | bigint unsigned
| float32 | double precision
| float64 | double precision
| float64 - 设置 digits, decimals 时 | numeric(digits, decimals)
#### Sqlite3
| go | sqlite3
| :--- | :---
| int, int32, int64 - 设置 auto 或者名称为 `Id` 时 | integer AUTOINCREMENT
| bool | bool
| string - 默认为 size 255 | varchar(size)
| string - 设置 type(text) 时 | text
| time.Time - 设置 type 为 date 时 | date
| time.TIme | datetime
| byte | tinyint unsigned
| rune | integer
| int | integer
| int8 | tinyint
| int16 | smallint
| int32 | integer
| int64 | bigint
| uint | integer unsigned
| uint8 | tinyint unsigned
| uint16 | smallint unsigned
| uint32 | integer unsigned
| uint64 | bigint unsigned
| float32 | real
| float64 | real
| float64 - 设置 digits, decimals 时 | decimal
#### PostgreSQL
| go | postgres
| :--- | :---
| int, int32, int64 - 设置 auto 或者名称为 `Id` 时 | serial
| bool | bool
| string - 默认为 size 255 | varchar(size)
| string - 设置 type(text) 时 | text
| time.Time - 设置 type 为 date 时 | date
| time.TIme | timestamp with time zone
| byte | smallint CHECK("column" >= 0 AND "column" <= 255)
| rune | integer
| int | integer
| int8 | smallint CHECK("column" >= -127 AND "column" <= 128)
| int16 | smallint
| int32 | integer
| int64 | bigint
| uint | bigint CHECK("column" >= 0)
| uint8 | smallint CHECK("column" >= 0 AND "column" <= 255)
| uint16 | integer CHECK("column" >= 0)
| uint32 | bigint CHECK("column" >= 0)
| uint64 | bigint CHECK("column" >= 0)
| float32 | double precision
| float64 | double precision
| float64 - 设置 digits, decimals 时 | numeric(digits, decimals)
## 关系型字段
其字段类型取决于对应的主键。
* RelForeignKey
* RelOneToOne
* RelManyToMany
* RelReverseOne
* RelReverseMany

View File

@ -1,83 +0,0 @@
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for `comment`
-- ----------------------------
DROP TABLE IF EXISTS `comment`;
CREATE TABLE `comment` (
`id` int(11) NOT NULL,
`post_id` bigint(200) NOT NULL,
`content` longtext NOT NULL,
`parent_id` int(11) DEFAULT NULL,
`status` smallint(4) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `post`
-- ----------------------------
DROP TABLE IF EXISTS `post`;
CREATE TABLE `post` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`title` varchar(60) NOT NULL,
`content` longtext NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `post_tag_rel`
-- ----------------------------
DROP TABLE IF EXISTS `post_tag_rel`;
CREATE TABLE `post_tag_rel` (
`id` int(11) NOT NULL,
`post_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `tag`
-- ----------------------------
DROP TABLE IF EXISTS `tag`;
CREATE TABLE `tag` (
`id` int(11) NOT NULL,
`name` varchar(30) NOT NULL,
`status` smallint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(30) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(30) NOT NULL,
`status` smallint(4) NOT NULL,
`is_staff` tinyint(1) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`created` date NOT NULL,
`updated` datetime NOT NULL,
`profile_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `profile`
-- ----------------------------
DROP TABLE IF EXISTS `profile`;
CREATE TABLE `profile` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`age` smallint(4) NOT NULL,
`money` double NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -1,59 +0,0 @@
## 对象的CRUD操作
对 object 操作简单的三个方法 Read / Insert / Update / Delete
```go
o := orm.NewOrm()
user := new(User)
user.Name = "slene"
fmt.Println(o.Insert(user))
user.Name = "Your"
fmt.Println(o.Update(user))
fmt.Println(o.Read(user))
fmt.Println(o.Delete(user))
```
### Read
```go
o := orm.NewOrm()
user := User{Id: 1}
err = o.Read(&user)
if err == sql.ErrNoRows {
fmt.Println("查询不到")
} else if err == orm.ErrMissPK {
fmt.Println("找不到主键")
} else {
fmt.Println(user.Id, user.Name)
}
```
### Insert
```go
o := orm.NewOrm()
var user User
user.Name = "slene"
user.IsActive = true
fmt.Println(o.Insert(&user))
fmt.Println(user.Id)
```
创建后会自动对 auto 的 field 赋值
### Update
```go
o := orm.NewOrm()
user := User{Id: 1}
if o.Read(&user) == nil {
user.Name = "MyName"
o.Update(&user)
}
```
### Delete
```go
o := orm.NewOrm()
o.Delete(&User{Id: 1})
```
Delete 操作会对反向关系进行操作,此例中 Post 拥有一个到 User 的外键。删除 User 的时候。如果 on_delete 设置为默认的级联操作,将删除对应的 Post
删除以后会清除 auto field 的值

View File

@ -1,316 +0,0 @@
## Orm 使用方法
beego/orm 的使用例子
后文例子如无特殊说明都以这个为基础。
##### models.go:
```go
package main
import (
"github.com/astaxie/beego/orm"
)
type User struct {
Id int
Name string
Profile *Profile `orm:"rel(one)"` // OneToOne relation
}
type Profile struct {
Id int
Age int16
User *User `orm:"reverse(one)"` // 设置反向关系(可选)
}
func init() {
// 需要在init中注册定义的model
orm.RegisterModel(new(User), new(Profile))
}
```
##### main.go
```go
package main
import (
"fmt"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
func init() {
orm.RegisterDriver("mysql", orm.DR_MySQL)
orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8", 30)
}
func main() {
o := orm.NewOrm()
o.Using("default") // 默认使用 default你可以指定为其他数据库
profile := new(Profile)
profile.Age = 30
user := new(User)
user.Profile = profile
user.Name = "slene"
fmt.Println(o.Insert(profile))
fmt.Println(o.Insert(user))
}
```
## 数据库的设置
目前 orm 支持三种数据库,以下为测试过的 driver
将你需要使用的 driver 加入 import 中
```go
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
)
```
#### RegisterDriver
三种默认数据库类型
```go
orm.DR_MySQL
orm.DR_Sqlite
orm.DR_Postgres
```
```go
// 参数1 driverName
// 参数2 数据库类型
// 这个用来设置 driverName 对应的数据库类型
// mysql / sqlite3 / postgres 这三种是默认已经注册过的,所以可以无需设置
orm.RegisterDriver("mymysql", orm.DR_MySQL)
```
#### RegisterDataBase
orm 必须注册一个别名为 `default` 的数据库,作为默认使用。
```go
// 参数1 数据库的别名用来在orm中切换数据库使用
// 参数2 driverName
// 参数3 对应的链接字符串
// 参数4 设置最大的空闲连接数,使用 golang 自己的连接池
orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8", 30)
```
#### 时区设置
orm 默认使用 time.Local 本地时区
* 作用于 orm 自动创建的时间
* 从数据库中取回的时间转换成 orm 本地时间
如果需要的话,你也可以进行更改
```go
// 设置为 UTC 时间
orm.DefaultTimeLoc = time.UTC
```
orm 在进行 RegisterDataBase 的同时,会获取数据库使用的时区,然后在 time.Time 类型存取的时做相应转换,以匹配时间系统,从而保证时间不会出错。
**注意:** 鉴于 Sqlite3 的设计,存取默认都为 UTC 时间
## 注册模型
如果使用 orm.QuerySeter 进行高级查询的话,这个是必须的。
反之,如果只使用 Raw 查询和 map struct是无需这一步的。您可以去查看 [Raw SQL 查询](Raw.md)
#### RegisterModel
将你定义的 Model 进行注册,最佳设计是有单独的 models.go 文件,在他的 init 函数中进行注册。
迷你版 models.go
```go
package main
import "github.com/astaxie/beego/orm"
type User struct {
Id int
Name string
}
func init(){
orm.RegisterModel(new(User))
}
```
RegisterModel 也可以同时注册多个 model
```go
orm.RegisterModel(new(User), new(Profile), new(Post))
```
详细的 struct 定义请查看文档 [模型定义](Models.md)
#### RegisterModelWithPrefix
使用表名前缀
```go
orm.RegisterModelWithPrefix("prefix_", new(User))
```
创建后的表名为 prefix_user
## ORM 接口使用
使用 orm 必然接触的 Ormer 接口,我们来熟悉一下
```go
var o Ormer
o = orm.NewOrm() // 创建一个 Ormer
// NewOrm 的同时会执行 orm.BootStrap (整个 app 只执行一次),用以验证模型之间的定义并缓存。
```
* type Ormer interface {
* [Read(Modeler) error](Object.md#read)
* [Insert(Modeler) (int64, error)](Object.md#insert)
* [Update(Modeler) (int64, error)](Object.md#update)
* [Delete(Modeler) (int64, error)](Object.md#delete)
* [M2mAdd(Modeler, string, ...interface{}) (int64, error)](Object.md#m2madd)
* [M2mDel(Modeler, string, ...interface{}) (int64, error)](Object.md#m2mdel)
* [LoadRel(Modeler, string) (int64, error)](Object.md#loadRel)
* [QueryTable(interface{}) QuerySeter](#querytable)
* [Using(string) error](#using)
* [Begin() error](Transaction.md)
* [Commit() error](Transaction.md)
* [Rollback() error](Transaction.md)
* [Raw(string, ...interface{}) RawSeter](#raw)
* [Driver() Driver](#driver)
* }
#### QueryTable
传入表名,或者 Modeler 对象,返回一个 [QuerySeter](Query.md#queryseter)
```go
o := orm.NewOrm()
var qs QuerySeter
qs = o.QueryTable("user")
// 如果表没有定义过,会立刻 panic
```
#### Using
切换为其他数据库
```go
orm.RegisterDataBase("db1", "mysql", "root:root@/orm_db2?charset=utf8", 30)
orm.RegisterDataBase("db2", "sqlite3", "data.db", 30)
o1 := orm.NewOrm()
o1.Using("db1")
o2 := orm.NewOrm()
o2.Using("db2")
// 切换为其他数据库以后
// 这个 Ormer 对象的其下的 api 调用都将使用这个数据库
```
默认使用 `default` 数据库,无需调用 Using
#### Raw
使用 sql 语句直接进行操作
Raw 函数,返回一个 [RawSeter](Raw.md) 用以对设置的 sql 语句和参数进行操作
```go
o := NewOrm()
var r RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
```
#### Driver
返回当前 orm 使用的 db 信息
```go
type Driver interface {
Name() string
Type() DriverType
}
```
```go
orm.RegisterDataBase("db1", "mysql", "root:root@/orm_db2?charset=utf8", 30)
orm.RegisterDataBase("db2", "sqlite3", "data.db", 30)
o1 := orm.NewOrm()
o1.Using("db1")
dr := o1.Driver()
fmt.Println(dr.Name() == "db1") // true
fmt.Println(dr.Type() == orm.DR_MySQL) // true
o2 := orm.NewOrm()
o2.Using("db2")
dr = o2.Driver()
fmt.Println(dr.Name() == "db2") // true
fmt.Println(dr.Type() == orm.DR_Sqlite) // true
```
## 调试模式打印查询语句
简单的设置 Debug 为 true 打印查询的语句
可能存在性能问题,不建议使用在产品模式
```go
func main() {
orm.Debug = true
...
```
默认使用 os.Stderr 输出日志信息
改变输出到你自己的 io.Writer
```go
var w io.Writer
...
// 设置为你的 io.Writer
...
orm.DebugLog = orm.NewLog(w)
```
日志格式
```go
[ORM] - 时间 - [Queries/数据库名] - [执行操作/执行时间] - [SQL语句] - 使用标点 `,` 分隔的参数列表 - 打印遇到的错误
```
```go
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [ db.Exec / 0.4ms] - [INSERT INTO `user` (`name`) VALUES (?)] - `slene`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [ db.Exec / 0.5ms] - [UPDATE `user` SET `name` = ? WHERE `id` = ?] - `astaxie`, `14`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [db.QueryRow / 0.4ms] - [SELECT `id`, `name` FROM `user` WHERE `id` = ?] - `14`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [ db.Exec / 0.4ms] - [INSERT INTO `post` (`user_id`,`title`,`content`) VALUES (?, ?, ?)] - `14`, `beego orm`, `powerful amazing`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [ db.Query / 0.4ms] - [SELECT T1.`name` `User__Name`, T0.`user_id` `User`, T1.`id` `User__Id` FROM `post` T0 INNER JOIN `user` T1 ON T1.`id` = T0.`user_id` WHERE T0.`id` = ? LIMIT 1000] - `68`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [ db.Exec / 0.4ms] - [DELETE FROM `user` WHERE `id` = ?] - `14`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [ db.Query / 0.3ms] - [SELECT T0.`id` FROM `post` T0 WHERE T0.`user_id` IN (?) ] - `14`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [ db.Exec / 0.4ms] - [DELETE FROM `post` WHERE `id` IN (?)] - `68`
```
日志内容包括 **所有的数据库操作**事务Prepare

View File

@ -1,411 +0,0 @@
## 高级查询
orm 以 **QuerySeter** 来组织查询,每个返回 **QuerySeter** 的方法都会获得一个新的 **QuerySeter** 对象。
基本使用方法:
```go
o := orm.NewOrm()
// 获取 QuerySeter 对象user 为表名
qs := o.QueryTable("user")
// 也可以直接使用对象作为表名
user := new(User)
qs = o.QueryTable(user) // 返回 QuerySeter
```
## expr
QuerySeter 中用于描述字段和 sql 操作符,使用简单的 expr 查询方法
字段组合的前后顺序依照表的关系,比如 User 表拥有 Profile 的外键,那么对 User 表查询对应的 Profile.Age 为条件,则使用 `Profile__Age` 注意,字段的分隔符号使用双下划线 `__`,除了描述字段, expr 的尾部可以增加操作符以执行对应的 sql 操作。比如 `Profile__Age__gt` 代表 Profile.Age > 18 的条件查询。
注释后面将描述对应的 sql 语句,仅仅是描述 expr 的类似结果,并不代表实际生成的语句。
```go
qs.Filter("id", 1) // WHERE id = 1
qs.Filter("profile__age", 18) // WHERE profile.age = 18
qs.Filter("Profile__Age", 18) // 使用字段名和Field名都是允许的
qs.Filter("profile__age", 18) // WHERE profile.age = 18
qs.Filter("profile__age__gt", 18) // WHERE profile.age > 18
qs.Filter("profile__age__gte", 18) // WHERE profile.age >= 18
qs.Filter("profile__age__in", 18, 20) // WHERE profile.age IN (18, 20)
qs.Filter("profile__age__in", 18, 20).Exclude("profile__lt", 1000)
// WHERE profile.age IN (18, 20) AND NOT profile_id < 1000
```
## Operators
当前支持的操作符号:
* [exact](#exact) / [iexact](#iexact) 等于
* [contains](#contains) / [icontains](#icontains) 包含
* [gt / gte](#gt / gte) 大于 / 大于等于
* [lt / lte](#lt / lte) 小于 / 小于等于
* [startswith](#startswith) / [istartswith](#istartswith) 以...起始
* [endswith](#endswith) / [iendswith](#iendswith) 以...结束
* [in](#in)
* [isnull](#isnull)
后面以 `i` 开头的表示:大小写不敏感
#### exact
Filter / Exclude / Condition expr 的默认值
```go
qs.Filter("name", "slene") // WHERE name = 'slene'
qs.Filter("name__exact", "slene") // WHERE name = 'slene'
// 使用 = 匹配,大小写是否敏感取决于数据表使用的 collation
qs.Filter("profile", nil) // WHERE profile_id IS NULL
```
#### iexact
```go
qs.Filter("name__iexact", "slene")
// WHERE name LIKE 'slene'
// 大小写不敏感,匹配任意 'Slene' 'sLENE'
```
#### contains
```go
qs.Filter("name__contains", "slene")
// WHERE name LIKE BINARY '%slene%'
// 大小写敏感, 匹配包含 slene 的字符
```
#### icontains
```go
qs.Filter("name__icontains", "slene")
// WHERE name LIKE '%slene%'
// 大小写不敏感, 匹配任意 'im Slene', 'im sLENE'
```
#### in
```go
qs.Filter("profile__age__in", 17, 18, 19, 20)
// WHERE profile.age IN (17, 18, 19, 20)
```
#### gt / gte
```go
qs.Filter("profile__age__gt", 17)
// WHERE profile.age > 17
qs.Filter("profile__age__gte", 18)
// WHERE profile.age >= 18
```
#### lt / lte
```go
qs.Filter("profile__age__lt", 17)
// WHERE profile.age < 17
qs.Filter("profile__age__lte", 18)
// WHERE profile.age <= 18
```
#### startswith
```go
qs.Filter("name__startswith", "slene")
// WHERE name LIKE BINARY 'slene%'
// 大小写敏感, 匹配以 'slene' 起始的字符串
```
#### istartswith
```go
qs.Filter("name__istartswith", "slene")
// WHERE name LIKE 'slene%'
// 大小写不敏感, 匹配任意以 'slene', 'Slene' 起始的字符串
```
#### endswith
```go
qs.Filter("name__endswith", "slene")
// WHERE name LIKE BINARY '%slene'
// 大小写敏感, 匹配以 'slene' 结束的字符串
```
#### iendswith
```go
qs.Filter("name__startswith", "slene")
// WHERE name LIKE '%slene'
// 大小写不敏感, 匹配任意以 'slene', 'Slene' 结束的字符串
```
#### isnull
```go
qs.Filter("profile__isnull", true)
qs.Filter("profile_id__isnull", true)
// WHERE profile_id IS NULL
qs.Filter("profile__isnull", false)
// WHERE profile_id IS NOT NULL
```
## 高级查询接口使用
QuerySeter 是高级查询使用的接口,我们来熟悉下他的接口方法
* type QuerySeter interface {
* [Filter(string, ...interface{}) QuerySeter](#filter)
* [Exclude(string, ...interface{}) QuerySeter](#exclude)
* [SetCond(*Condition) QuerySeter](#setcond)
* [Limit(int, ...int64) QuerySeter](#limit)
* [Offset(int64) QuerySeter](#offset)
* [OrderBy(...string) QuerySeter](#orderby)
* [RelatedSel(...interface{}) QuerySeter](#relatedsel)
* [Count() (int64, error)](#count)
* [Update(Params) (int64, error)](#update)
* [Delete() (int64, error)](#delete)
* [PrepareInsert() (Inserter, error)](#prepareinsert)
* [All(interface{}) (int64, error)](#all)
* [One(Modeler) error](#one)
* [Values(*[]Params, ...string) (int64, error)](#values)
* [ValuesList(*[]ParamsList, ...string) (int64, error)](#valueslist)
* [ValuesFlat(*ParamsList, string) (int64, error)](#valuesflat)
* }
* 每个返回 QuerySeter 的 api 调用时都会新建一个 QuerySeter不影响之前创建的。
* 高级查询使用 Filter 和 Exclude 来做常用的条件查询。囊括两种清晰的过滤规则:包含, 排除
#### Filter
用来过滤查询结果,起到 **包含条件** 的作用
多个 Filter 之间使用 `AND` 连接
```go
qs.Filter("profile__isnull", true).Filter("name", "slene")
// WHERE profile_id IS NULL AND name = 'slene'
```
#### Exclude
用来过滤查询结果,起到 **排除条件** 的作用
使用 `NOT` 排除条件
多个 Exclude 之间使用 `AND` 连接
```go
qs.Exclude("profile__isnull", true).Filter("name", "slene")
// WHERE NOT profile_id IS NULL AND name = 'slene'
```
#### SetCond
自定义条件表达式
```go
cond := NewCondition()
cond1 := cond.And("profile__isnull", false).AndNot("status__in", 1).Or("profile__age__gt", 2000)
qs := orm.QueryTable("user")
qs = qs.SetCond(cond1)
// WHERE ... AND ... AND NOT ... OR ...
cond2 := cond.AndCond(cond1).OrCond(cond.And("name", "slene"))
qs = qs.SetCond(cond2).Count()
// WHERE (... AND ... AND NOT ... OR ...) OR ( ... )
```
#### Limit
限制最大返回数据行数,第二个参数可以设置 `Offset`
```go
var DefaultRowsLimit = 1000 // orm 默认的 limit 值为 1000
// 默认情况下 select 查询的最大行数为 1000
// LIMIT 1000
qs.Limit(10)
// LIMIT 10
qs.Limit(10, 20)
// LIMIT 10 OFFSET 20
qs.Limit(-1)
// no limit
qs.Limit(-1, 100)
// LIMIT 18446744073709551615 OFFSET 100
// 18446744073709551615 是 1<<64 - 1 用来指定无 limit 限制 但有 offset 偏移的情况
```
#### Offset
设置 偏移行数
```go
qs.Offset(20)
// LIMIT 1000 OFFSET 20
```
#### OrderBy
参数使用 **expr**
在 expr 前使用减号 `-` 表示 `DESC` 的排列
```go
qs.OrderBy("id", "-profile__age")
// ORDER BY id ASC, profile.age DESC
qs.OrderBy("-profile__age", "profile")
// ORDER BY profile.age DESC, profile_id ASC
```
#### RelatedSel
关系查询,参数使用 **expr**
```go
var DefaultRelsDepth = 5 // 默认情况下直接调用 RelatedSel 将进行最大 5 层的关系查询
qs := o.QueryTable("post")
qs.RelateSel()
// INNER JOIN user ... LEFT OUTER JOIN profile ...
qs.RelateSel("user")
// INNER JOIN user ...
// 设置 expr 只对设置的字段进行关系查询
// 对设置 null 属性的 Field 将使用 LEFT OUTER JOIN
```
#### Count
依据当前的查询条件,返回结果行数
```go
cnt, err := o.QueryTable("user").Count() // SELECT COUNT(*) FROM USER
fmt.Printf("Count Num: %s, %s", cnt, err)
```
#### Update
依据当前查询条件,进行批量更新操作
```go
num, err := o.QueryTable("user").Filter("name", "slene").Update(orm.Params{
"name": "astaxie",
})
fmt.Printf("Affected Num: %s, %s", num, err)
// SET name = "astaixe" WHERE name = "slene"
```
#### Delete
依据当前查询条件,进行批量删除操作
```go
num, err := o.QueryTable("user").Filter("name", "slene").Delete()
fmt.Printf("Affected Num: %s, %s", num, err)
// DELETE FROM user WHERE name = "slene"
```
#### PrepareInsert
用于一次 prepare 多次 insert 插入,以提高批量插入的速度。
```go
var users []*User
...
qs := o.QueryTable("user")
i, _ := qs.PrepareInsert()
for _, user := range users {
id, err := i.Insert(user)
if err != nil {
...
}
}
// PREPARE INSERT INTO user (`name`, ...) VALUES (?, ...)
// EXECUTE INSERT INTO user (`name`, ...) VALUES ("slene", ...)
// EXECUTE ...
// ...
i.Close() // 别忘记关闭 statement
```
#### All
返回对应的结果集对象
```go
var users []*User
num, err := o.QueryTable("user").Filter("name", "slene").All(&users)
fmt.Printf("Returned Rows Num: %s, %s", num, err)
```
#### One
尝试返回单条记录
```go
var user *User
err := o.QueryTable("user").Filter("name", "slene").One(&user)
if err == orm.ErrMultiRows {
// 多条的时候报错
fmt.Printf("Returned Multi Rows Not One")
}
if err == orm.ErrNoRows {
// 没有找到记录
fmt.Printf("Not row found")
}
```
#### Values
返回结果集的 key => value 值
key 为 Model 里的 Field namevalue 的值 以 string 保存
```go
var maps []orm.Params
num, err := o.QueryTable("user").Values(&maps)
if err != nil {
fmt.Printf("Result Nums: %d\n", num)
for _, m := range maps {
fmt.Println(m["Id"], m["Name"])
}
}
```
返回指定的 Field 数据
**TODO**: 暂不支持级联查询 **RelatedSel** 直接返回 Values
但可以直接指定 expr 级联返回需要的数据
```go
var maps []orm.Params
num, err := o.QueryTable("user").Values(&maps, "id", "name", "profile", "profile__age")
if err != nil {
fmt.Printf("Result Nums: %d\n", num)
for _, m := range maps {
fmt.Println(m["Id"], m["Name"], m["Profile"], m["Profile__Age"])
// map 中的数据都是展开的,没有复杂的嵌套
}
}
```
#### ValuesList
顾名思义返回的结果集以slice存储
结果的排列与 Model 中定义的 Field 顺序一致
返回的每个元素值以 string 保存
```go
var lists []orm.ParamsList
num, err := o.QueryTable("user").ValuesList(&lists)
if err != nil {
fmt.Printf("Result Nums: %d\n", num)
for _, row := range lists {
fmt.Println(row)
}
}
```
当然也可以指定 expr 返回指定的 Field
```go
var lists []orm.ParamsList
num, err := o.QueryTable("user").ValuesList(&lists, "name", "profile__age")
if err != nil {
fmt.Printf("Result Nums: %d\n", num)
for _, row := range lists {
fmt.Printf("Name: %s, Age: %s\m", row[0], row[1])
}
}
```
#### ValuesFlat
只返回特定的 Field 值,讲结果集展开到单个 slice 里
```go
var list orm.ParamsList
num, err := o.QueryTable("user").ValuesFlat(&list, "name")
if err != nil {
fmt.Printf("Result Nums: %d\n", num)
fmt.Printf("All User Names: %s", strings.Join(list, ", ")
}
```

View File

@ -1,40 +0,0 @@
最新文档请查看 beedoc
* [中文](http://beego.me/docs/Models_Overview?lang=zh)
* [English](http://beego.me/docs/Models_Overview?lang=en)
## 文档目录
1. [Orm 使用方法](Orm.md)
- [数据库的设置](Orm.md#数据库的设置)
* [驱动类型设置](Orm.md#registerdriver)
* [参数设置](Orm.md#registerdatabase)
* [时区设置](Orm.md#时区设置)
- [注册模型](Orm.md#注册模型)
- [ORM 接口使用](Orm.md#orm-接口使用)
- [调试模式打印查询语句](Orm.md#调试模式打印查询语句)
2. [对象的CRUD操作](Object.md)
3. [高级查询](Query.md)
- [使用的表达式语法](Query.md#expr)
- [支持的操作符号](Query.md#operators)
- [高级查询接口使用](Query.md#高级查询接口使用)
4. [使用SQL语句进行查询](Raw.md)
5. [事务处理](Transaction.md)
6. [模型定义](Models.md)
- [Struct Tag 设置参数](Models.md#struct-tag-设置参数)
- [表关系设置](Models.md#表关系设置)
- [模型字段与数据库类型的对应](Models.md#模型字段与数据库类型的对应)
7. [命令模式](Cmd.md)
- [自动建表](Cmd.md#自动建表)
- [打印建表SQL](Cmd.md#打印建表sql)
8. [Test ORM](Test.md)
9. Custom Fields
10. Faq
### 文档更新记录
* 2013-08-20: 这里不再更新,最新文档在 beedoc, [中文](http://beego.me/docs/Models_Overview?lang=zh) / [English](http://beego.me/docs/Models_Overview?lang=en)
* 2013-08-19: 增加[自动建表](Cmd.md#自动建表)功能
* 2013-08-13: ORM 的 [时区设置](Orm.md#时区设置)
* 2013-08-13: [模型字段与数据库类型的对应](Models.md#模型字段与数据库类型的对应) 推荐的数据库对应使用的类型

View File

@ -1,116 +0,0 @@
## 使用SQL语句进行查询
* 使用 Raw SQL 查询,无需使用 ORM 表定义
* 多数据库,都可直接使用占位符号 `?`,自动转换
* 查询时的参数,支持使用 Model Struct 和 Slice, Array
```go
ids := []int{1, 2, 3}
p.Raw("SELECT name FROM user WHERE id IN (?, ?, ?)", ids)
```
创建一个 **RawSeter**
```go
o := NewOrm()
var r RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
```
* type RawSeter interface {
* [Exec() (int64, error)](#exec)
* [QueryRow(...interface{}) error](#queryrow)
* [QueryRows(...interface{}) (int64, error)](#queryrows)
* [SetArgs(...interface{}) RawSeter](#setargs)
* [Values(*[]Params) (int64, error)](#values)
* [ValuesList(*[]ParamsList) (int64, error)](#valueslist)
* [ValuesFlat(*ParamsList) (int64, error)](#valuesflat)
* [Prepare() (RawPreparer, error)](#prepare)
* }
#### Exec
执行sql语句
```go
num, err := r.Exec()
```
#### QueryRow
TODO
#### QueryRows
TODO
#### SetArgs
改变 Raw(sql, args...) 中的 args 参数,返回一个新的 RawSeter
用于单条 sql 语句,重复利用,替换参数然后执行。
```go
num, err := r.SetArgs("arg1", "arg2").Exec()
num, err := r.SetArgs("arg1", "arg2").Exec()
...
```
#### Values / ValuesList / ValuesFlat
Raw SQL 查询获得的结果集 Value 为 `string` 类型NULL 字段的值为空 ``
#### Values
返回结果集的 key => value 值
```go
var maps []orm.Params
num, err = o.Raw("SELECT user_name FROM user WHERE status = ?", 1).Values(&maps)
if err == nil && num > 0 {
fmt.Println(maps[0]["user_name"]) // slene
}
```
#### ValuesList
返回结果集 slice
```go
var lists []orm.ParamsList
num, err = o.Raw("SELECT user_name FROM user WHERE status = ?", 1).ValuesList(&lists)
if err == nil && num > 0 {
fmt.Println(lists[0][0]) // slene
}
```
#### ValuesFlat
返回单一字段的平铺 slice 数据
```go
var list orm.ParamsList
num, err = o.Raw("SELECT id FROM user WHERE id < ?", 10).ValuesList(&list)
if err == nil && num > 0 {
fmt.Println(list) // []{"1","2","3",...}
}
```
#### Prepare
用于一次 prepare 多次 exec以提高批量执行的速度。
```go
p, err := o.Raw("UPDATE user SET name = ? WHERE name = ?").Prepare()
num, err := p.Exec("testing", "slene")
num, err = p.Exec("testing", "astaxie")
...
...
p.Close() // 别忘记关闭 statement
```
## FAQ
1. 我的 app 需要支持多类型数据库,如何在使用 Raw SQL 的时候判断当前使用的数据库类型。
使用 Ormer 的 [Driver方法](Orm.md#driver) 可以进行判断

View File

@ -1,34 +0,0 @@
## Test ORM
测试代码参见
```bash
models_test.go // 表定义
orm_test.go // 测试用例
```
#### MySQL
```bash
mysql -u root -e 'create database orm_test;'
export ORM_DRIVER=mysql
export ORM_SOURCE="root:@/orm_test?charset=utf8"
go test -v github.com/astaxie/beego/orm
```
#### Sqlite3
```bash
touch /path/to/orm_test.db
export ORM_DRIVER=sqlite3
export ORM_SOURCE=/path/to/orm_test.db
go test -v github.com/astaxie/beego/orm
```
#### PostgreSQL
```bash
psql -c 'create database orm_test;' -U postgres
export ORM_DRIVER=postgres
export ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
go test -v github.com/astaxie/beego/orm
```

View File

@ -1,17 +0,0 @@
## 事务处理
orm 可以简单的进行事务操作
```go
o := NewOrm()
err := o.Begin()
// 事务处理过程
...
...
// 此过程中的所有使用 o Ormer 对象的查询都在事务处理范围内
if SomeError {
err = o.Rollback()
} else {
err = o.Commit()
}
```

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm
@ -108,6 +116,7 @@ type fieldInfo struct {
null bool null bool
index bool index bool
unique bool unique bool
colDefault bool
initial StrTo initial StrTo
size int size int
auto_now bool auto_now bool
@ -272,6 +281,11 @@ checkType:
fi.pk = attrs["pk"] fi.pk = attrs["pk"]
fi.unique = attrs["unique"] fi.unique = attrs["unique"]
// Mark object property if there is attribute "default" in the orm configuration
if _, ok := tags["default"]; ok {
fi.colDefault = true
}
switch fieldType { switch fieldType {
case RelManyToMany, RelReverseMany, RelReverseOne: case RelManyToMany, RelReverseMany, RelReverseOne:
fi.null = false fi.null = false

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm
@ -147,6 +155,24 @@ type DataNull struct {
NullBool sql.NullBool `orm:"null"` NullBool sql.NullBool `orm:"null"`
NullFloat64 sql.NullFloat64 `orm:"null"` NullFloat64 sql.NullFloat64 `orm:"null"`
NullInt64 sql.NullInt64 `orm:"null"` NullInt64 sql.NullInt64 `orm:"null"`
BooleanPtr *bool `orm:"null"`
CharPtr *string `orm:"null;size(50)"`
TextPtr *string `orm:"null;type(text)"`
BytePtr *byte `orm:"null"`
RunePtr *rune `orm:"null"`
IntPtr *int `orm:"null"`
Int8Ptr *int8 `orm:"null"`
Int16Ptr *int16 `orm:"null"`
Int32Ptr *int32 `orm:"null"`
Int64Ptr *int64 `orm:"null"`
UintPtr *uint `orm:"null"`
Uint8Ptr *uint8 `orm:"null"`
Uint16Ptr *uint16 `orm:"null"`
Uint32Ptr *uint32 `orm:"null"`
Uint64Ptr *uint64 `orm:"null"`
Float32Ptr *float32 `orm:"null"`
Float64Ptr *float64 `orm:"null"`
DecimalPtr *float64 `orm:"digits(8);decimals(4);null"`
} }
type String string type String string

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm
@ -103,42 +111,73 @@ func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col
// return field type as type constant from reflect.Value // return field type as type constant from reflect.Value
func getFieldType(val reflect.Value) (ft int, err error) { func getFieldType(val reflect.Value) (ft int, err error) {
elm := reflect.Indirect(val) switch val.Type() {
switch elm.Kind() { case reflect.TypeOf(new(int8)):
case reflect.Int8:
ft = TypeBitField ft = TypeBitField
case reflect.Int16: case reflect.TypeOf(new(int16)):
ft = TypeSmallIntegerField ft = TypeSmallIntegerField
case reflect.Int32, reflect.Int: case reflect.TypeOf(new(int32)),
reflect.TypeOf(new(int)):
ft = TypeIntegerField ft = TypeIntegerField
case reflect.Int64: case reflect.TypeOf(new(int64)):
ft = TypeBigIntegerField ft = TypeBigIntegerField
case reflect.Uint8: case reflect.TypeOf(new(uint8)):
ft = TypePositiveBitField ft = TypePositiveBitField
case reflect.Uint16: case reflect.TypeOf(new(uint16)):
ft = TypePositiveSmallIntegerField ft = TypePositiveSmallIntegerField
case reflect.Uint32, reflect.Uint: case reflect.TypeOf(new(uint32)),
reflect.TypeOf(new(uint)):
ft = TypePositiveIntegerField ft = TypePositiveIntegerField
case reflect.Uint64: case reflect.TypeOf(new(uint64)):
ft = TypePositiveBigIntegerField ft = TypePositiveBigIntegerField
case reflect.Float32, reflect.Float64: case reflect.TypeOf(new(float32)),
reflect.TypeOf(new(float64)):
ft = TypeFloatField ft = TypeFloatField
case reflect.Bool: case reflect.TypeOf(new(bool)):
ft = TypeBooleanField ft = TypeBooleanField
case reflect.String: case reflect.TypeOf(new(string)):
ft = TypeCharField ft = TypeCharField
default: default:
switch elm.Interface().(type) { elm := reflect.Indirect(val)
case sql.NullInt64: switch elm.Kind() {
case reflect.Int8:
ft = TypeBitField
case reflect.Int16:
ft = TypeSmallIntegerField
case reflect.Int32, reflect.Int:
ft = TypeIntegerField
case reflect.Int64:
ft = TypeBigIntegerField ft = TypeBigIntegerField
case sql.NullFloat64: case reflect.Uint8:
ft = TypePositiveBitField
case reflect.Uint16:
ft = TypePositiveSmallIntegerField
case reflect.Uint32, reflect.Uint:
ft = TypePositiveIntegerField
case reflect.Uint64:
ft = TypePositiveBigIntegerField
case reflect.Float32, reflect.Float64:
ft = TypeFloatField ft = TypeFloatField
case sql.NullBool: case reflect.Bool:
ft = TypeBooleanField ft = TypeBooleanField
case sql.NullString: case reflect.String:
ft = TypeCharField ft = TypeCharField
case time.Time: default:
ft = TypeDateTimeField if elm.Interface() == nil {
panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val))
}
switch elm.Interface().(type) {
case sql.NullInt64:
ft = TypeBigIntegerField
case sql.NullFloat64:
ft = TypeFloatField
case sql.NullBool:
ft = TypeBooleanField
case sql.NullString:
ft = TypeCharField
case time.Time:
ft = TypeDateTimeField
}
} }
} }
if ft&IsFieldType == 0 { if ft&IsFieldType == 0 {

View File

@ -1,9 +1,53 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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.
// Simple Usage
//
// 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() {
// 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)
// }
//
// more docs: http://beego.me/docs/mvc/model/overview.md
package orm package orm
import ( import (
@ -445,10 +489,6 @@ func (o *orm) Driver() Driver {
return driver(o.alias.Name) return driver(o.alias.Name)
} }
func (o *orm) GetDB() dbQuerier {
panic(ErrNotImplement)
}
// create new orm // create new orm
func NewOrm() Ormer { func NewOrm() Ormer {
BootStrap() // execute only once BootStrap() // execute only once

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

View File

@ -1,8 +1,16 @@
// Beego (http://beego.me/) // Copyright 2014 beego Author. All Rights Reserved.
// @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 // Licensed under the Apache License, Version 2.0 (the "License");
// @license http://github.com/astaxie/beego/blob/master/LICENSE // you may not use this file except in compliance with the License.
// @authors slene // 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 package orm

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