mirror of
https://github.com/astaxie/beego.git
synced 2025-07-11 23:11:01 +00:00
Compare commits
600 Commits
Author | SHA1 | Date | |
---|---|---|---|
f7f390dfec | |||
8f7246e17b | |||
c8f6e0f156 | |||
0207caab6f | |||
d629c1d3d0 | |||
817650aa33 | |||
ba1232dfaf | |||
64d4f6518b | |||
9f05db8475 | |||
b275d7c6f5 | |||
73770fbe22 | |||
fc11169ee3 | |||
b54589fa9d | |||
2af0c569a5 | |||
27b7a8f743 | |||
c143a6ec19 | |||
e619d83990 | |||
27b452cd95 | |||
b776e43962 | |||
cb89cd577d | |||
6b777f0c5e | |||
491238ce7d | |||
d9bb1a3592 | |||
9c6775c22c | |||
4d70b22f96 | |||
d943d16d52 | |||
bbb6f31f16 | |||
364cacf659 | |||
21586586ba | |||
e1d7bc8826 | |||
499ee09d4b | |||
970f0b460c | |||
9280683935 | |||
a58c8180e8 | |||
b9852df51c | |||
8e71d31dbe | |||
db06e954b5 | |||
3abd01799d | |||
ae37689314 | |||
40974365e6 | |||
edbaa080f1 | |||
d56491ab3a | |||
9fd7acf663 | |||
2dca48f26e | |||
4138fe0217 | |||
245762f7d9 | |||
18bdec951d | |||
9252301fa0 | |||
1053b63bbc | |||
3bd6caae0a | |||
d8734cf58d | |||
51161361db | |||
6da0cdb9e2 | |||
9f070c622b | |||
738e22e389 | |||
69fc22f0df | |||
4cd7177ece | |||
8e618192c2 | |||
3415a5b091 | |||
dfe055c47c | |||
74b22649c8 | |||
4255630564 | |||
7e3b5e5307 | |||
0222b8d693 | |||
23268b788a | |||
ee4fd60e4d | |||
4414659df4 | |||
68ccd8e5a4 | |||
6f802b0a05 | |||
743628a946 | |||
d90ce15707 | |||
23457ed2a0 | |||
d7791ba837 | |||
676595213f | |||
d446b5b011 | |||
2ba12ad1e1 | |||
8cc57e2fc8 | |||
322b208566 | |||
fd610d6777 | |||
1148359570 | |||
55b1d6a897 | |||
c4c9a50c42 | |||
a311d712a5 | |||
bb5351bb9f | |||
26130a5df6 | |||
2c9363d29b | |||
91886a4547 | |||
a7e60c93dc | |||
5b1705b2d6 | |||
34940d00c0 | |||
1a6ea693b5 | |||
2e51c124f1 | |||
1d8afdc9c9 | |||
1592e9c04d | |||
bf6b0d3e1f | |||
af71289c25 | |||
b1efae6ff8 | |||
92f3de4027 | |||
185089299c | |||
59c1e74e13 | |||
9bb9855153 | |||
519602a553 | |||
740a526105 | |||
98289cd8de | |||
56e2143630 | |||
c0cfb5277c | |||
b29700c3c3 | |||
47be2fadb5 | |||
1d72629334 | |||
b4880c5e1d | |||
c0bb5b9237 | |||
1e1068e81c | |||
6c3e274b6e | |||
f56bdb6284 | |||
d8fa118727 | |||
0afd04ec6f | |||
973306b28d | |||
da8c3c3910 | |||
0c1bb6409a | |||
cddb4fdb60 | |||
e1d20aea5d | |||
4498a02c15 | |||
5534258e22 | |||
73650e1f2b | |||
d0e7dd686b | |||
dc58ec8316 | |||
6d72fc63ab | |||
dd4afac5dc | |||
e229a4762f | |||
ecb27f34e6 | |||
d55997e520 | |||
44c8534477 | |||
44a39a6b3e | |||
cc5ca458ab | |||
6f6b412709 | |||
642a69de02 | |||
466f3c49c1 | |||
d1c9cb2281 | |||
9c9ffa202a | |||
56dfe418dd | |||
7caeb91f9b | |||
47848fa77b | |||
e0e8b98622 | |||
5c84ada389 | |||
ac6203b81b | |||
5e1e618d0f | |||
a5124a1d45 | |||
e675594e46 | |||
d02e32fa51 | |||
8a82e25e85 | |||
49c0f8906f | |||
9261c80509 | |||
217e24815b | |||
840fd3b64f | |||
eedaea2fea | |||
a002f78443 | |||
cdf9ff401f | |||
caa260f053 | |||
5fa55ca2d3 | |||
260b5b1951 | |||
54ae4bc25b | |||
162bee1b43 | |||
533b00ae56 | |||
d3cdebbee2 | |||
06b5c7f644 | |||
185ee872c4 | |||
74e0af4a9a | |||
8aa9455900 | |||
2d26f7df2f | |||
3d6408cfc2 | |||
223f57bb4c | |||
c4aa33fb1b | |||
20cc5b261e | |||
7ec2a077d9 | |||
9f70561f21 | |||
1c9898dee5 | |||
2cf7c6a58a | |||
2cee46ab2b | |||
020bfbcc9c | |||
3f8252bffd | |||
6d313aa15f | |||
2a4e2d4a71 | |||
f96a6285bf | |||
e938876c4a | |||
6e9d2dc965 | |||
3aceaf8838 | |||
71b9854f48 | |||
f59ccd3a35 | |||
181a7c35fe | |||
2ed272aeb2 | |||
77c1109134 | |||
29d4823866 | |||
24cf06d288 | |||
0c31c2d689 | |||
f988f035e5 | |||
1b4158c15b | |||
433e8f2ce3 | |||
22ba7fdce4 | |||
2a0f87e810 | |||
19db4b67f6 | |||
b1baf4503d | |||
d536f5b8dc | |||
1cc1d57f55 | |||
73370ade90 | |||
0d3b7dcd07 | |||
d7fe5ef435 | |||
8bd902814f | |||
378356a65e | |||
30871e2617 | |||
3731088b4a | |||
d46833c6d8 | |||
18659e16ba | |||
5d8187d005 | |||
d961ae4cd8 | |||
0e1a0049d1 | |||
0c933643e2 | |||
d2c5daa5ee | |||
d3ab157915 | |||
75d28d49c5 | |||
76bb4827d0 | |||
3caba06189 | |||
572508ddd8 | |||
e34f8479bb | |||
daf85f06f8 | |||
22671c524e | |||
ab99d5f1e2 | |||
c52f634d9c | |||
9c665afc04 | |||
77ed151243 | |||
29d98731c6 | |||
934dd2e8d2 | |||
e65d87974a | |||
db04c3cbb4 | |||
802aa16136 | |||
f2df07f630 | |||
dc89f844f3 | |||
b80cdef20f | |||
98dcee0643 | |||
0ad75cb5fa | |||
6a9d04c269 | |||
93ca11f83d | |||
d0b43ef4f5 | |||
c9bb9d6a09 | |||
f96245786a | |||
1a1b0c14b9 | |||
07c628c7e9 | |||
1e92d17605 | |||
bb795847da | |||
54ba307f7f | |||
950ff91d87 | |||
b43401b9f6 | |||
fe50269b3f | |||
000033e2a7 | |||
15242d89ce | |||
76522d43af | |||
52df1234bd | |||
fc6b9ce009 | |||
b9fdd67519 | |||
7743eecfd4 | |||
c4d8e4a244 | |||
9d4ec508bb | |||
8b747f54bc | |||
a2428af8a7 | |||
90a7ce5c6a | |||
ef3c7c127b | |||
88caf1ed70 | |||
304beaf89f | |||
2288ac868c | |||
8d797a4a5e | |||
10db97b193 | |||
716962672f | |||
90cff5f042 | |||
da127bbc22 | |||
945b1da3a8 | |||
94c84b846f | |||
db43892fe6 | |||
71149218d1 | |||
1636a7271c | |||
68c3bdfdd4 | |||
ecd0a5487e | |||
fda841208d | |||
57e62e5e57 | |||
824e3f8f5b | |||
1822dd95ac | |||
ddbfc25e56 | |||
1f26852610 | |||
0188fb3711 | |||
0bcd828d73 | |||
e11a27f1d1 | |||
90caeb4cf7 | |||
6c9249034d | |||
14114018ea | |||
6f5162461e | |||
c34c514bba | |||
8ac2b9bf66 | |||
2a85c79ce5 | |||
767083bd56 | |||
1a79513293 | |||
c6cb1f92e8 | |||
9c0aad06c5 | |||
180c6aafac | |||
710f5b6234 | |||
dbf944adce | |||
6c9ff81fc1 | |||
ec6383c07d | |||
76db5cded4 | |||
1b3e7de463 | |||
ab28edaf25 | |||
04431a7a15 | |||
b00c42b3df | |||
4cae7af3f9 | |||
e4988b714e | |||
24489df63d | |||
fb8e9ae1a3 | |||
1eb9aef687 | |||
efc14a1e8d | |||
fa1281002e | |||
812950b60d | |||
f9e991b538 | |||
fc07419938 | |||
d69eee23f0 | |||
41de7c7db6 | |||
6a33647f30 | |||
e5134873be | |||
8d20ea04b0 | |||
5c1e8e42b9 | |||
ca3e7568a1 | |||
2823167848 | |||
a27f5c0dc0 | |||
8af0475251 | |||
9c07332cfc | |||
a06e0f27ad | |||
a760e46f98 | |||
262665f4e5 | |||
0b3763cc67 | |||
c147f26cd1 | |||
1ba7847913 | |||
52df979aca | |||
b6f789c497 | |||
fa6cbc08d9 | |||
c4f8f45da4 | |||
6fca4a8218 | |||
aae89576c6 | |||
a907a86476 | |||
8716185de8 | |||
31e6133413 | |||
3a5de83ec2 | |||
f5f3395560 | |||
8164367762 | |||
e1475b72b9 | |||
f267ee8a12 | |||
7e060e6e5c | |||
727d2f9ea1 | |||
67c0c232a1 | |||
6c62198b59 | |||
e48e1ddaa9 | |||
1f9281c830 | |||
ccab9a7044 | |||
9013f5c6c7 | |||
29b7ff84e1 | |||
fb0cc55822 | |||
0820e21738 | |||
38eb29fa7b | |||
cca0a3f76d | |||
f9a9b5a905 | |||
c667895ce5 | |||
b2cdabb8a0 | |||
647a47517d | |||
f7cd1479ba | |||
fcc359af11 | |||
08b3e4191e | |||
4f4f7ce257 | |||
d06e02474f | |||
4d65330ca1 | |||
2dfe1fc61c | |||
29b60d6058 | |||
6eee223352 | |||
0692f92890 | |||
41adcf9966 | |||
e6b42a4070 | |||
bc4780091b | |||
d8614e80e7 | |||
c83a2a0925 | |||
50a21d60c1 | |||
5a087b28d2 | |||
9b40271878 | |||
770dc702f0 | |||
e146100a23 | |||
2d94f7797b | |||
61ce608847 | |||
2fe559701c | |||
37fe175c26 | |||
fcc9d8c45f | |||
e51a9d6481 | |||
e9a1daa3ee | |||
44ea260db1 | |||
1d1ad69954 | |||
59773dfabe | |||
ccb61f0416 | |||
14629c214b | |||
a3888cef7f | |||
f6a1a6c9bf | |||
38ee43701d | |||
421b796f1a | |||
db51ddab96 | |||
baf2c63d5c | |||
771179a3c6 | |||
c07b1d881b | |||
d8f2b05e08 | |||
7b2fe824d5 | |||
02301caac1 | |||
98c2307763 | |||
485c2e865c | |||
b390667374 | |||
f684de2385 | |||
35b4022ee0 | |||
77294a5881 | |||
7b39bd7042 | |||
01e4084587 | |||
bf429a3a20 | |||
f8ff79d77d | |||
e9487d3571 | |||
d7c3727f96 | |||
03eb1fc104 | |||
0a967875da | |||
6ae8bc1a16 | |||
e70537f8b3 | |||
9a583323a8 | |||
ff9c8d94e6 | |||
7f977a0c8c | |||
aaabeff44f | |||
f85ac088c3 | |||
26da23266a | |||
80274684e0 | |||
be005f9774 | |||
c16b7be9ac | |||
de87529387 | |||
c4fa17921e | |||
724137e605 | |||
a144769515 | |||
05089be427 | |||
7668c54d05 | |||
86752a55b6 | |||
e07d780dcf | |||
e566322643 | |||
51ee1e77c2 | |||
986e91b7d6 | |||
8b8638c507 | |||
d27c5c8daf | |||
86121deac4 | |||
6b02e1e9d4 | |||
83696d95c3 | |||
7be8114616 | |||
1dbbb89ad1 | |||
75904effd9 | |||
cd9e614a71 | |||
c1234e7c6d | |||
b611b9dab6 | |||
cad3da337a | |||
3969cd3b40 | |||
f4867aad5a | |||
17006cfb26 | |||
7d1b03ee5d | |||
84a4379f0d | |||
ab71201c96 | |||
c347dd9e7b | |||
75e2611cc4 | |||
d314d12c77 | |||
564c3bbeb5 | |||
31f0ac4ce3 | |||
57a9670b0a | |||
14cd9e51ac | |||
886bb782a5 | |||
d2119f715c | |||
f98b1810ab | |||
55a7711017 | |||
436edda926 | |||
1f1190fbc3 | |||
20463fa725 | |||
ae37f95239 | |||
da6726f3da | |||
32469cd69d | |||
e572f45296 | |||
efcaa3d934 | |||
5ecfe0c335 | |||
d325a66fee | |||
fe9017c819 | |||
b97279a74f | |||
6a78898bb1 | |||
f201859fa7 | |||
52fdfc5665 | |||
8ed6d06572 | |||
b5a2347e1d | |||
118e07158e | |||
7a376c32be | |||
3d74a1a436 | |||
00eac0e4cb | |||
58ac0d5ea4 | |||
2773fda883 | |||
99c03a2b9c | |||
b1b4dbb0e4 | |||
77e1f26dd4 | |||
2820f630c8 | |||
1f6c5599aa | |||
df8c73b23a | |||
ea6982fcea | |||
a3f40234ca | |||
2c420573d4 | |||
c0d3cc6fc5 | |||
3f7ecea089 | |||
f4147058fc | |||
1fb24aca34 | |||
885c0678ff | |||
d7a5281bda | |||
474a16a7a0 | |||
1d36b19cab | |||
deed251794 | |||
22ba252c8f | |||
289f050c04 | |||
bbd31131a4 | |||
008ae39ff6 | |||
0836b9e13f | |||
7a39a3c52f | |||
509af636b1 | |||
daf8b1101f | |||
428aec1c24 | |||
a804d242d9 | |||
7ddd20340b | |||
e7fcb824c1 | |||
ee9749d640 | |||
d7090689e8 | |||
52d153da87 | |||
6ce55e8884 | |||
91ee42ceeb | |||
d17f107fc4 | |||
ae8bb8ce82 | |||
38188098c5 | |||
c372328f88 | |||
a6ced64441 | |||
69094b2786 | |||
08397fef45 | |||
84da1c924a | |||
f733b5707a | |||
9d0798edc6 | |||
1858f3073b | |||
e6d6419a65 | |||
e52386b52d | |||
024817aacb | |||
9d0ad3f974 | |||
6357c88d97 | |||
9457e61a0c | |||
f657509a42 | |||
20e05a3908 | |||
77c40e6f7b | |||
ea2ed90ab0 | |||
15759f60ed | |||
0e3e22efbe | |||
a906bf1174 | |||
a6379481cf | |||
7d09ac252a | |||
0ff058bd25 | |||
dbe75f90d5 | |||
ca1fd96ea5 | |||
0b1b121db8 | |||
8683ee7c67 | |||
007db805be | |||
19c3a5b41c | |||
e635e274d4 | |||
46cde6e579 | |||
3bb4d6f013 | |||
cec151fda7 | |||
b6c4e27a75 | |||
2933d1fedd | |||
cbffcaa7a8 | |||
707c951302 | |||
fefd8ddb5b | |||
14dee37a21 | |||
17a9c3c3a9 | |||
8946f816f9 | |||
36ba1e49b1 | |||
34936dde35 | |||
aa004ed973 | |||
53353fce56 | |||
f92ce9af96 | |||
8b021c8ea1 | |||
34572193c6 | |||
4dde2c59ff | |||
c83c17d917 | |||
4994d36b66 | |||
a991b9dcde | |||
3fe9e6a28a | |||
62e9c89010 | |||
ac96c2b15e | |||
0f170a80da | |||
9c5348f690 | |||
90f91b10c5 | |||
a5a6a30744 | |||
1f6e689e5d | |||
7b7a95677a | |||
af4f153830 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.idea
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.swo
|
||||
|
36
README.md
36
README.md
@ -1,39 +1,37 @@
|
||||
## beego
|
||||
## Beego
|
||||
|
||||
[](https://drone.io/github.com/astaxie/beego/latest)
|
||||
[](http://godoc.org/github.com/astaxie/beego)
|
||||
|
||||
beego is a Go Framework inspired by tornado and sinatra.
|
||||
|
||||
It is a simple & powerful web framework.
|
||||
beego is an open-source, high-performance, modular, full-stack web framework.
|
||||
|
||||
More info [beego.me](http://beego.me)
|
||||
|
||||
## Installation
|
||||
|
||||
go get github.com/astaxie/beego
|
||||
|
||||
## Features
|
||||
|
||||
* RESTful support
|
||||
* MVC architecture
|
||||
* Session support (store in memory, file, Redis or MySQL)
|
||||
* Cache support (store in memory, Redis or Memcache)
|
||||
* Global Config
|
||||
* Intelligent routing
|
||||
* Thread-safe map
|
||||
* Friendly displaying of errors
|
||||
* Useful template functions
|
||||
|
||||
* Modularity
|
||||
* Auto API documents
|
||||
* Annotation router
|
||||
* Namespace
|
||||
* Powerful development tools
|
||||
* Full stack for Web & API
|
||||
|
||||
## 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)
|
||||
|
||||
[中文文档](http://beego.me/docs/intro/)
|
||||
## Community
|
||||
|
||||
* [http://beego.me/community](http://beego.me/community)
|
||||
|
||||
## LICENSE
|
||||
|
||||
beego is licensed under the Apache Licence, Version 2.0
|
||||
(http://www.apache.org/licenses/LICENSE-2.0.html).
|
||||
|
||||
[][koding]
|
||||
[koding]: https://koding.com/Teamwork?import=https://github.com/astaxie/beego/archive/master.zip&c=git1
|
418
admin.go
418
admin.go
@ -1,17 +1,29 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package beego
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/grace"
|
||||
"github.com/astaxie/beego/toolbox"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
@ -46,7 +58,6 @@ func init() {
|
||||
beeAdminApp.Route("/prof", profIndex)
|
||||
beeAdminApp.Route("/healthcheck", healthcheck)
|
||||
beeAdminApp.Route("/task", taskStatus)
|
||||
beeAdminApp.Route("/runtask", runTask)
|
||||
beeAdminApp.Route("/listconf", listConf)
|
||||
FilterMonitorFunc = func(string, string, time.Duration) bool { return true }
|
||||
}
|
||||
@ -54,22 +65,24 @@ func init() {
|
||||
// AdminIndex is the default http.Handler for admin module.
|
||||
// it matches url pattern "/".
|
||||
func adminIndex(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Write([]byte("<html><head><title>beego admin dashboard</title></head><body>"))
|
||||
rw.Write([]byte("Welcome to Admin Dashboard<br>\n"))
|
||||
rw.Write([]byte("There are servral functions:<br>\n"))
|
||||
rw.Write([]byte("1. Record all request and request time, <a href='/qps'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/qps</a><br>\n"))
|
||||
rw.Write([]byte("2. Get runtime profiling data by the pprof, <a href='/prof'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/prof</a><br>\n"))
|
||||
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>"))
|
||||
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
|
||||
tmpl = template.Must(tmpl.Parse(indexTpl))
|
||||
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
|
||||
data := make(map[interface{}]interface{})
|
||||
tmpl.Execute(rw, data)
|
||||
}
|
||||
|
||||
// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter.
|
||||
// it's registered with url pattern "/qbs" in admin module.
|
||||
func qpsIndex(rw http.ResponseWriter, r *http.Request) {
|
||||
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.
|
||||
@ -78,112 +91,210 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
command := r.Form.Get("command")
|
||||
if command != "" {
|
||||
data := make(map[interface{}]interface{})
|
||||
switch command {
|
||||
case "conf":
|
||||
fmt.Fprintln(rw, "list all beego's conf:")
|
||||
fmt.Fprintln(rw, "AppName:", AppName)
|
||||
fmt.Fprintln(rw, "AppPath:", AppPath)
|
||||
fmt.Fprintln(rw, "AppConfigPath:", AppConfigPath)
|
||||
fmt.Fprintln(rw, "StaticDir:", StaticDir)
|
||||
fmt.Fprintln(rw, "StaticExtensionsToGzip:", StaticExtensionsToGzip)
|
||||
fmt.Fprintln(rw, "HttpAddr:", HttpAddr)
|
||||
fmt.Fprintln(rw, "HttpPort:", HttpPort)
|
||||
fmt.Fprintln(rw, "HttpTLS:", EnableHttpTLS)
|
||||
fmt.Fprintln(rw, "HttpCertFile:", HttpCertFile)
|
||||
fmt.Fprintln(rw, "HttpKeyFile:", HttpKeyFile)
|
||||
fmt.Fprintln(rw, "RecoverPanic:", RecoverPanic)
|
||||
fmt.Fprintln(rw, "AutoRender:", AutoRender)
|
||||
fmt.Fprintln(rw, "ViewsPath:", ViewsPath)
|
||||
fmt.Fprintln(rw, "RunMode:", RunMode)
|
||||
fmt.Fprintln(rw, "SessionOn:", SessionOn)
|
||||
fmt.Fprintln(rw, "SessionProvider:", SessionProvider)
|
||||
fmt.Fprintln(rw, "SessionName:", SessionName)
|
||||
fmt.Fprintln(rw, "SessionGCMaxLifetime:", SessionGCMaxLifetime)
|
||||
fmt.Fprintln(rw, "SessionSavePath:", SessionSavePath)
|
||||
fmt.Fprintln(rw, "SessionHashFunc:", SessionHashFunc)
|
||||
fmt.Fprintln(rw, "SessionHashKey:", SessionHashKey)
|
||||
fmt.Fprintln(rw, "SessionCookieLifeTime:", SessionCookieLifeTime)
|
||||
fmt.Fprintln(rw, "UseFcgi:", UseFcgi)
|
||||
fmt.Fprintln(rw, "MaxMemory:", MaxMemory)
|
||||
fmt.Fprintln(rw, "EnableGzip:", EnableGzip)
|
||||
fmt.Fprintln(rw, "DirectoryIndex:", DirectoryIndex)
|
||||
fmt.Fprintln(rw, "HttpServerTimeOut:", HttpServerTimeOut)
|
||||
fmt.Fprintln(rw, "ErrorsShow:", ErrorsShow)
|
||||
fmt.Fprintln(rw, "XSRFKEY:", XSRFKEY)
|
||||
fmt.Fprintln(rw, "EnableXSRF:", EnableXSRF)
|
||||
fmt.Fprintln(rw, "XSRFExpire:", XSRFExpire)
|
||||
fmt.Fprintln(rw, "CopyRequestBody:", CopyRequestBody)
|
||||
fmt.Fprintln(rw, "TemplateLeft:", TemplateLeft)
|
||||
fmt.Fprintln(rw, "TemplateRight:", TemplateRight)
|
||||
fmt.Fprintln(rw, "BeegoServerName:", BeegoServerName)
|
||||
fmt.Fprintln(rw, "EnableAdmin:", EnableAdmin)
|
||||
fmt.Fprintln(rw, "AdminHttpAddr:", AdminHttpAddr)
|
||||
fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort)
|
||||
m := make(map[string]interface{})
|
||||
|
||||
m["AppName"] = AppName
|
||||
m["AppPath"] = AppPath
|
||||
m["AppConfigPath"] = AppConfigPath
|
||||
m["StaticDir"] = StaticDir
|
||||
m["StaticExtensionsToGzip"] = StaticExtensionsToGzip
|
||||
m["HttpAddr"] = HttpAddr
|
||||
m["HttpPort"] = HttpPort
|
||||
m["HttpTLS"] = EnableHttpTLS
|
||||
m["HttpCertFile"] = HttpCertFile
|
||||
m["HttpKeyFile"] = HttpKeyFile
|
||||
m["RecoverPanic"] = RecoverPanic
|
||||
m["AutoRender"] = AutoRender
|
||||
m["ViewsPath"] = ViewsPath
|
||||
m["RunMode"] = RunMode
|
||||
m["SessionOn"] = SessionOn
|
||||
m["SessionProvider"] = SessionProvider
|
||||
m["SessionName"] = SessionName
|
||||
m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
|
||||
m["SessionSavePath"] = SessionSavePath
|
||||
m["SessionCookieLifeTime"] = SessionCookieLifeTime
|
||||
m["UseFcgi"] = UseFcgi
|
||||
m["MaxMemory"] = MaxMemory
|
||||
m["EnableGzip"] = EnableGzip
|
||||
m["DirectoryIndex"] = DirectoryIndex
|
||||
m["HttpServerTimeOut"] = HttpServerTimeOut
|
||||
m["ErrorsShow"] = ErrorsShow
|
||||
m["XSRFKEY"] = XSRFKEY
|
||||
m["EnableXSRF"] = EnableXSRF
|
||||
m["XSRFExpire"] = XSRFExpire
|
||||
m["CopyRequestBody"] = CopyRequestBody
|
||||
m["TemplateLeft"] = TemplateLeft
|
||||
m["TemplateRight"] = TemplateRight
|
||||
m["BeegoServerName"] = BeegoServerName
|
||||
m["EnableAdmin"] = EnableAdmin
|
||||
m["AdminHttpAddr"] = AdminHttpAddr
|
||||
m["AdminHttpPort"] = AdminHttpPort
|
||||
|
||||
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
|
||||
tmpl = template.Must(tmpl.Parse(configTpl))
|
||||
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
|
||||
|
||||
data["Content"] = m
|
||||
|
||||
tmpl.Execute(rw, data)
|
||||
|
||||
case "router":
|
||||
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 {
|
||||
fmt.Fprintln(rw)
|
||||
fmt.Fprintln(rw)
|
||||
fmt.Fprintln(rw, " Method:", method)
|
||||
printTree(rw, t)
|
||||
|
||||
resultList := new([][]string)
|
||||
|
||||
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":
|
||||
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 {
|
||||
fmt.Fprintln(rw, "BeforeRouter:")
|
||||
var filterType string
|
||||
|
||||
if bf, ok := BeeApp.Handlers.filters[BeforeRouter]; ok {
|
||||
filterType = "Before Router"
|
||||
filterTypes = append(filterTypes, filterType)
|
||||
resultList := new([][]string)
|
||||
for _, f := range bf {
|
||||
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)
|
||||
}
|
||||
fmt.Fprintln(rw, "BeforeExec:")
|
||||
filterTypeData[filterType] = resultList
|
||||
}
|
||||
|
||||
if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
|
||||
filterType = "Before Exec"
|
||||
filterTypes = append(filterTypes, filterType)
|
||||
resultList := new([][]string)
|
||||
for _, f := range bf {
|
||||
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)
|
||||
}
|
||||
fmt.Fprintln(rw, "AfterExec:")
|
||||
filterTypeData[filterType] = resultList
|
||||
}
|
||||
|
||||
if bf, ok := BeeApp.Handlers.filters[AfterExec]; ok {
|
||||
filterType = "After Exec"
|
||||
filterTypes = append(filterTypes, filterType)
|
||||
resultList := new([][]string)
|
||||
for _, f := range bf {
|
||||
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)
|
||||
}
|
||||
fmt.Fprintln(rw, "FinishRouter:")
|
||||
filterTypeData[filterType] = resultList
|
||||
}
|
||||
|
||||
if bf, ok := BeeApp.Handlers.filters[FinishRouter]; ok {
|
||||
filterType = "Finish Router"
|
||||
filterTypes = append(filterTypes, filterType)
|
||||
resultList := new([][]string)
|
||||
for _, f := range bf {
|
||||
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:
|
||||
rw.Write([]byte("command not support"))
|
||||
}
|
||||
} 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 {
|
||||
printTree(rw, tr)
|
||||
printTree(resultList, tr)
|
||||
}
|
||||
if t.wildcard != nil {
|
||||
printTree(rw, t.wildcard)
|
||||
printTree(resultList, t.wildcard)
|
||||
}
|
||||
for _, l := range t.leaves {
|
||||
if v, ok := l.runObject.(*controllerInfo); ok {
|
||||
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 {
|
||||
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 {
|
||||
fmt.Fprintln(rw, v.pattern, "handler")
|
||||
var result = []string{
|
||||
fmt.Sprintf("%s", v.pattern),
|
||||
fmt.Sprintf(""),
|
||||
fmt.Sprintf(""),
|
||||
}
|
||||
*resultList = append(*resultList, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -194,58 +305,134 @@ func printTree(rw http.ResponseWriter, t *Tree) {
|
||||
func profIndex(rw http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
command := r.Form.Get("command")
|
||||
format := r.Form.Get("format")
|
||||
data := make(map[string]interface{})
|
||||
|
||||
var result bytes.Buffer
|
||||
if command != "" {
|
||||
toolbox.ProcessInput(command, 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 {
|
||||
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>"))
|
||||
|
||||
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
|
||||
}
|
||||
tmpl.Execute(rw, data)
|
||||
}
|
||||
}
|
||||
|
||||
// Healthcheck is a http.Handler calling health checking and showing the result.
|
||||
// it's in "/healthcheck" pattern in admin module.
|
||||
func healthcheck(rw http.ResponseWriter, req *http.Request) {
|
||||
data := make(map[interface{}]interface{})
|
||||
|
||||
var result = []string{}
|
||||
fields := []string{
|
||||
fmt.Sprintf("Name"),
|
||||
fmt.Sprintf("Message"),
|
||||
fmt.Sprintf("Status"),
|
||||
}
|
||||
resultList := new([][]string)
|
||||
|
||||
content := make(map[string]interface{})
|
||||
|
||||
for name, h := range toolbox.AdminCheckList {
|
||||
if err := h.Check(); err != nil {
|
||||
fmt.Fprintf(rw, "%s : %s\n", name, err.Error())
|
||||
result = []string{
|
||||
fmt.Sprintf("error"),
|
||||
fmt.Sprintf("%s", name),
|
||||
fmt.Sprintf("%s", err.Error()),
|
||||
}
|
||||
|
||||
} 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).
|
||||
// it's in "/task" pattern in admin module.
|
||||
func taskStatus(rw http.ResponseWriter, req *http.Request) {
|
||||
for tname, tk := range toolbox.AdminTaskList {
|
||||
fmt.Fprintf(rw, "%s:%s:%s", tname, tk.GetStatus(), tk.GetPrev().String())
|
||||
}
|
||||
}
|
||||
data := make(map[interface{}]interface{})
|
||||
|
||||
// RunTask is a http.Handler to run a Task from the "query string.
|
||||
// the request url likes /runtask?taskname=sendmail.
|
||||
func runTask(rw http.ResponseWriter, req *http.Request) {
|
||||
// Run Task
|
||||
req.ParseForm()
|
||||
taskname := req.Form.Get("taskname")
|
||||
if taskname != "" {
|
||||
|
||||
if t, ok := toolbox.AdminTaskList[taskname]; ok {
|
||||
err := t.Run()
|
||||
if err != nil {
|
||||
fmt.Fprintf(rw, "%v", err)
|
||||
data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
|
||||
}
|
||||
fmt.Fprintf(rw, "%s run success,Now the Status is %s", taskname, t.GetStatus())
|
||||
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus())}
|
||||
} else {
|
||||
fmt.Fprintf(rw, "there's no task which named:%s", taskname)
|
||||
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
|
||||
}
|
||||
}
|
||||
|
||||
// List Tasks
|
||||
content := make(map[string]interface{})
|
||||
resultList := new([][]string)
|
||||
var result = []string{}
|
||||
var fields = []string{
|
||||
fmt.Sprintf("Task Name"),
|
||||
fmt.Sprintf("Task Spec"),
|
||||
fmt.Sprintf("Task 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.
|
||||
@ -272,8 +459,15 @@ func (admin *adminApp) Run() {
|
||||
for p, f := range admin.routers {
|
||||
http.Handle(p, f)
|
||||
}
|
||||
err := http.ListenAndServe(addr, nil)
|
||||
BeeLogger.Info("Admin server Running on %s", addr)
|
||||
|
||||
var err error
|
||||
if Graceful {
|
||||
err = grace.ListenAndServe(addr, nil)
|
||||
} else {
|
||||
err = http.ListenAndServe(addr, nil)
|
||||
}
|
||||
if err != nil {
|
||||
BeeLogger.Critical("Admin ListenAndServe: ", err)
|
||||
BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||
}
|
||||
}
|
||||
|
355
adminui.go
Normal file
355
adminui.go
Normal file
File diff suppressed because one or more lines are too long
100
app.go
100
app.go
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@ -11,14 +19,13 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/grace"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
// FilterFunc defines filter function type.
|
||||
type FilterFunc func(*context.Context)
|
||||
|
||||
// App defines beego application with a new PatternServeMux.
|
||||
type App struct {
|
||||
Handlers *ControllerRegistor
|
||||
@ -40,8 +47,6 @@ func (app *App) Run() {
|
||||
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
|
||||
}
|
||||
|
||||
BeeLogger.Info("Running on %s", addr)
|
||||
|
||||
var (
|
||||
err error
|
||||
l net.Listener
|
||||
@ -49,7 +54,19 @@ func (app *App) Run() {
|
||||
endRunning := make(chan bool, 1)
|
||||
|
||||
if UseFcgi {
|
||||
if UseStdIo {
|
||||
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 {
|
||||
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)
|
||||
@ -58,6 +75,45 @@ func (app *App) Run() {
|
||||
BeeLogger.Critical("Listen: ", err)
|
||||
}
|
||||
err = fcgi.Serve(l, app.Handlers)
|
||||
}
|
||||
} else {
|
||||
if Graceful {
|
||||
app.Server.Addr = addr
|
||||
app.Server.Handler = app.Handlers
|
||||
app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second
|
||||
app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second
|
||||
if EnableHttpTLS {
|
||||
go func() {
|
||||
time.Sleep(20 * time.Microsecond)
|
||||
if HttpsPort != 0 {
|
||||
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
|
||||
app.Server.Addr = addr
|
||||
}
|
||||
server := grace.NewServer(addr, app.Handlers)
|
||||
server.Server = app.Server
|
||||
err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
|
||||
if err != nil {
|
||||
BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
}
|
||||
}()
|
||||
}
|
||||
if EnableHttpListen {
|
||||
go func() {
|
||||
server := grace.NewServer(addr, app.Handlers)
|
||||
server.Server = app.Server
|
||||
if ListenTCP4 && HttpAddr == "" {
|
||||
server.Network = "tcp4"
|
||||
}
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
}
|
||||
}()
|
||||
}
|
||||
} else {
|
||||
app.Server.Addr = addr
|
||||
app.Server.Handler = app.Handlers
|
||||
@ -66,9 +122,11 @@ func (app *App) Run() {
|
||||
|
||||
if EnableHttpTLS {
|
||||
go func() {
|
||||
time.Sleep(20 * time.Microsecond)
|
||||
if HttpsPort != 0 {
|
||||
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)
|
||||
if err != nil {
|
||||
BeeLogger.Critical("ListenAndServeTLS: ", err)
|
||||
@ -80,15 +138,35 @@ func (app *App) Run() {
|
||||
|
||||
if EnableHttpListen {
|
||||
go func() {
|
||||
app.Server.Addr = addr
|
||||
BeeLogger.Info("http server Running on %s", app.Server.Addr)
|
||||
if ListenTCP4 && HttpAddr == "" {
|
||||
ln, err := net.Listen("tcp4", app.Server.Addr)
|
||||
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
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
<-endRunning
|
||||
}
|
||||
|
139
beego.go
139
beego.go
@ -1,9 +1,28 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// beego is an open-source, high-performance, modularity, full-stack web framework
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import "github.com/astaxie/beego"
|
||||
//
|
||||
// func main() {
|
||||
// beego.Run()
|
||||
// }
|
||||
//
|
||||
// more infomation: http://beego.me
|
||||
package beego
|
||||
|
||||
import (
|
||||
@ -14,83 +33,15 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/middleware"
|
||||
"github.com/astaxie/beego/session"
|
||||
)
|
||||
|
||||
// beego web framework version.
|
||||
const VERSION = "1.3.0"
|
||||
const VERSION = "1.5.0"
|
||||
|
||||
type hookfunc func() error //hook function to run
|
||||
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.
|
||||
// it's an alias method of App.Router.
|
||||
// usage:
|
||||
@ -261,15 +212,6 @@ func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// ErrorHandler registers http.HandlerFunc to each http err code string.
|
||||
// usage:
|
||||
// beego.ErrorHandler("404",NotFound)
|
||||
// beego.ErrorHandler("500",InternalServerError)
|
||||
func Errorhandler(err string, h http.HandlerFunc) *App {
|
||||
middleware.Errorhandler(err, h)
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// SetViewsPath sets view directory path in beego application.
|
||||
func SetViewsPath(path string) *App {
|
||||
ViewsPath = path
|
||||
@ -289,15 +231,20 @@ func SetStaticPath(url string, path string) *App {
|
||||
|
||||
// DelStaticPath removes the static folder setting in this url pattern in beego application.
|
||||
func DelStaticPath(url string) *App {
|
||||
if !strings.HasPrefix(url, "/") {
|
||||
url = "/" + url
|
||||
}
|
||||
url = strings.TrimRight(url, "/")
|
||||
delete(StaticDir, url)
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// InsertFilter adds a FilterFunc with pattern condition and action constant.
|
||||
// The pos means action constant including
|
||||
// beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
|
||||
func InsertFilter(pattern string, pos int, filter FilterFunc) *App {
|
||||
BeeApp.Handlers.InsertFilter(pattern, pos, filter)
|
||||
// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
|
||||
// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
|
||||
func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App {
|
||||
BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...)
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
@ -340,6 +287,9 @@ func initBeforeHttpRun() {
|
||||
}
|
||||
}
|
||||
|
||||
//init mime
|
||||
AddAPPStartHook(initMime)
|
||||
|
||||
// do hooks function
|
||||
for _, hk := range hooks {
|
||||
err := hk()
|
||||
@ -354,11 +304,10 @@ func initBeforeHttpRun() {
|
||||
if sessionConfig == "" {
|
||||
sessionConfig = `{"cookieName":"` + SessionName + `",` +
|
||||
`"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
|
||||
`"providerConfig":"` + SessionSavePath + `",` +
|
||||
`"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` +
|
||||
`"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
|
||||
`"sessionIDHashFunc":"` + SessionHashFunc + `",` +
|
||||
`"sessionIDHashKey":"` + SessionHashKey + `",` +
|
||||
`"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
|
||||
`"domain":"` + SessionDomain + `",` +
|
||||
`"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
|
||||
}
|
||||
GlobalSessions, err = session.NewManager(SessionProvider,
|
||||
@ -376,14 +325,10 @@ func initBeforeHttpRun() {
|
||||
}
|
||||
}
|
||||
|
||||
middleware.VERSION = VERSION
|
||||
middleware.AppName = AppName
|
||||
middleware.RegisterErrorHandler()
|
||||
registerDefaultErrorHandler()
|
||||
|
||||
for u, _ := range StaticDir {
|
||||
Get(u+"/*", serverStaticRouter)
|
||||
}
|
||||
if EnableDocs {
|
||||
Get("/docs", serverDocs)
|
||||
Get("/docs/*", serverDocs)
|
||||
}
|
||||
}
|
||||
@ -391,7 +336,7 @@ func initBeforeHttpRun() {
|
||||
// this function is for test package init
|
||||
func TestBeegoInit(apppath string) {
|
||||
AppPath = apppath
|
||||
RunMode = "test"
|
||||
os.Setenv("BEEGO_RUNMODE", "test")
|
||||
AppConfigPath = filepath.Join(AppPath, "conf", "app.conf")
|
||||
err := ParseConfig()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
@ -404,6 +349,4 @@ func TestBeegoInit(apppath string) {
|
||||
|
||||
func init() {
|
||||
hooks = make([]hookfunc, 0)
|
||||
//init mime
|
||||
AddAPPStartHook(initMime)
|
||||
}
|
||||
|
2
cache/README.md
vendored
2
cache/README.md
vendored
@ -22,7 +22,7 @@ First you must import it
|
||||
|
||||
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:
|
||||
|
||||
|
57
cache/cache.go
vendored
57
cache/cache.go
vendored
@ -1,9 +1,33 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// import(
|
||||
// "github.com/astaxie/beego/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("memory", `{"interval":60}`)
|
||||
//
|
||||
// Use it like this:
|
||||
//
|
||||
// bm.Put("astaxie", 1, 10)
|
||||
// bm.Get("astaxie")
|
||||
// bm.IsExist("astaxie")
|
||||
// bm.Delete("astaxie")
|
||||
//
|
||||
// more docs http://beego.me/docs/module/cache.md
|
||||
package cache
|
||||
|
||||
import (
|
||||
@ -13,7 +37,7 @@ import (
|
||||
// Cache interface contains all behaviors for cache adapter.
|
||||
// usage:
|
||||
// cache.Register("file",cache.NewFileCache()) // this operation is run in init method of file.go.
|
||||
// c := cache.NewCache("file","{....}")
|
||||
// c,err := cache.NewCache("file","{....}")
|
||||
// c.Put("key",value,3600)
|
||||
// v := c.Get("key")
|
||||
//
|
||||
@ -23,6 +47,8 @@ import (
|
||||
type Cache interface {
|
||||
// get cached value by key.
|
||||
Get(key string) interface{}
|
||||
// GetMulti is a batch version of Get.
|
||||
GetMulti(keys []string) []interface{}
|
||||
// set cached value with key and expire time.
|
||||
Put(key string, val interface{}, timeout int64) error
|
||||
// delete cached value by key.
|
||||
@ -31,11 +57,11 @@ type Cache interface {
|
||||
Incr(key string) error
|
||||
// decrease cached int value by key, as a counter.
|
||||
Decr(key string) error
|
||||
// check cached value is existed or not.
|
||||
// check if cached value exists or not.
|
||||
IsExist(key string) bool
|
||||
// clear all cache.
|
||||
ClearAll() error
|
||||
// start gc routine via config string setting.
|
||||
// start gc routine based on config string settings.
|
||||
StartAndGC(config string) error
|
||||
}
|
||||
|
||||
@ -48,23 +74,24 @@ func Register(name string, adapter Cache) {
|
||||
if adapter == nil {
|
||||
panic("cache: Register adapter is nil")
|
||||
}
|
||||
if _, dup := adapters[name]; dup {
|
||||
if _, ok := adapters[name]; ok {
|
||||
panic("cache: Register called twice for adapter " + name)
|
||||
}
|
||||
adapters[name] = adapter
|
||||
}
|
||||
|
||||
// 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}.
|
||||
// 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]
|
||||
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 {
|
||||
return nil, err
|
||||
adapter = nil
|
||||
}
|
||||
return adapter, nil
|
||||
return
|
||||
}
|
||||
|
73
cache/cache_test.go
vendored
73
cache/cache_test.go
vendored
@ -1,12 +1,21 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -56,10 +65,39 @@ func TestCache(t *testing.T) {
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("delete err")
|
||||
}
|
||||
|
||||
//test GetMulti
|
||||
if err = bm.Put("astaxie", "author", 10); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
if v := bm.Get("astaxie"); v.(string) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
if err = bm.Put("astaxie1", "author1", 10); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0].(string) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Error("init err")
|
||||
}
|
||||
@ -93,6 +131,7 @@ func TestFileCache(t *testing.T) {
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("delete err")
|
||||
}
|
||||
|
||||
//test string
|
||||
if err = bm.Put("astaxie", "author", 10); err != nil {
|
||||
t.Error("set Error", err)
|
||||
@ -100,8 +139,28 @@ func TestFileCache(t *testing.T) {
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
if v := bm.Get("astaxie"); v.(string) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
//test GetMulti
|
||||
if err = bm.Put("astaxie1", "author1", 10); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0].(string) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
|
||||
os.RemoveAll("cache")
|
||||
}
|
||||
|
56
cache/conv.go
vendored
56
cache/conv.go
vendored
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
|
||||
@ -19,12 +27,11 @@ func GetString(v interface{}) string {
|
||||
case []byte:
|
||||
return string(result)
|
||||
default:
|
||||
if v == nil {
|
||||
return ""
|
||||
} else {
|
||||
if v != nil {
|
||||
return fmt.Sprintf("%v", result)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// convert interface to int.
|
||||
@ -37,14 +44,11 @@ func GetInt(v interface{}) int {
|
||||
case int64:
|
||||
return int(result)
|
||||
default:
|
||||
d := GetString(v)
|
||||
if d != "" {
|
||||
value, err := strconv.Atoi(d)
|
||||
if err == nil {
|
||||
if d := GetString(v); d != "" {
|
||||
value, _ := strconv.Atoi(d)
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -58,12 +62,10 @@ func GetInt64(v interface{}) int64 {
|
||||
case int64:
|
||||
return result
|
||||
default:
|
||||
d := GetString(v)
|
||||
if d != "" {
|
||||
result, err := strconv.ParseInt(d, 10, 64)
|
||||
if err == nil {
|
||||
return result
|
||||
}
|
||||
|
||||
if d := GetString(v); d != "" {
|
||||
value, _ := strconv.ParseInt(d, 10, 64)
|
||||
return value
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@ -75,14 +77,11 @@ func GetFloat64(v interface{}) float64 {
|
||||
case float64:
|
||||
return result
|
||||
default:
|
||||
d := GetString(v)
|
||||
if d != "" {
|
||||
value, err := strconv.ParseFloat(d, 64)
|
||||
if err == nil {
|
||||
if d := GetString(v); d != "" {
|
||||
value, _ := strconv.ParseFloat(d, 64)
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -92,12 +91,9 @@ func GetBool(v interface{}) bool {
|
||||
case bool:
|
||||
return result
|
||||
default:
|
||||
d := GetString(v)
|
||||
if d != "" {
|
||||
result, err := strconv.ParseBool(d)
|
||||
if err == nil {
|
||||
return result
|
||||
}
|
||||
if d := GetString(v); d != "" {
|
||||
value, _ := strconv.ParseBool(d)
|
||||
return value
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
18
cache/conv_test.go
vendored
18
cache/conv_test.go
vendored
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
|
||||
|
146
cache/file.go
vendored
146
cache/file.go
vendored
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
|
||||
@ -57,12 +65,10 @@ func NewFileCache() *FileCache {
|
||||
|
||||
// Start and begin gc for file cache.
|
||||
// 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
|
||||
json.Unmarshal([]byte(config), &cfg)
|
||||
//fmt.Println(cfg)
|
||||
//fmt.Println(config)
|
||||
if _, ok := cfg["CachePath"]; !ok {
|
||||
cfg["CachePath"] = FileCachePath
|
||||
}
|
||||
@ -75,84 +81,74 @@ func (this *FileCache) StartAndGC(config string) error {
|
||||
if _, ok := cfg["EmbedExpiry"]; !ok {
|
||||
cfg["EmbedExpiry"] = strconv.FormatInt(FileCacheEmbedExpiry, 10)
|
||||
}
|
||||
this.CachePath = cfg["CachePath"]
|
||||
this.FileSuffix = cfg["FileSuffix"]
|
||||
this.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"])
|
||||
this.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"])
|
||||
fc.CachePath = cfg["CachePath"]
|
||||
fc.FileSuffix = cfg["FileSuffix"]
|
||||
fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"])
|
||||
fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"])
|
||||
|
||||
this.Init()
|
||||
fc.Init()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Init will make new dir for file cache if not exist.
|
||||
func (this *FileCache) Init() {
|
||||
app := filepath.Dir(os.Args[0])
|
||||
this.CachePath = filepath.Join(app, this.CachePath)
|
||||
ok, err := exists(this.CachePath)
|
||||
if err != nil { // print error
|
||||
//fmt.Println(err)
|
||||
func (fc *FileCache) Init() {
|
||||
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
|
||||
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
|
||||
}
|
||||
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.
|
||||
func (this *FileCache) getCacheFileName(key string) string {
|
||||
func (fc *FileCache) getCacheFileName(key string) string {
|
||||
m := md5.New()
|
||||
io.WriteString(m, key)
|
||||
keyMd5 := hex.EncodeToString(m.Sum(nil))
|
||||
cachePath := this.CachePath
|
||||
//fmt.Println("cachepath : " , cachePath)
|
||||
//fmt.Println("md5" , keyMd5);
|
||||
switch this.DirectoryLevel {
|
||||
cachePath := fc.CachePath
|
||||
switch fc.DirectoryLevel {
|
||||
case 2:
|
||||
cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4])
|
||||
case 1:
|
||||
cachePath = filepath.Join(cachePath, keyMd5[0:2])
|
||||
}
|
||||
|
||||
ok, err := exists(cachePath)
|
||||
if err != nil {
|
||||
//fmt.Println(err)
|
||||
if ok, _ := exists(cachePath); !ok { // todo : error handle
|
||||
_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle
|
||||
}
|
||||
if !ok {
|
||||
if err = os.MkdirAll(cachePath, os.ModePerm); err != nil {
|
||||
//fmt.Println(err);
|
||||
}
|
||||
}
|
||||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, this.FileSuffix))
|
||||
|
||||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix))
|
||||
}
|
||||
|
||||
// Get value from file cache.
|
||||
// if non-exist or expired, return empty string.
|
||||
func (this *FileCache) Get(key string) interface{} {
|
||||
filename := this.getCacheFileName(key)
|
||||
filedata, err := File_get_contents(filename)
|
||||
//fmt.Println("get length:" , len(filedata));
|
||||
func (fc *FileCache) Get(key string) interface{} {
|
||||
fileData, err := File_get_contents(fc.getCacheFileName(key))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var to FileCacheItem
|
||||
Gob_decode(filedata, &to)
|
||||
Gob_decode(fileData, &to)
|
||||
if to.Expired < time.Now().Unix() {
|
||||
return ""
|
||||
}
|
||||
return to.Data
|
||||
}
|
||||
|
||||
// GetMulti gets values from file cache.
|
||||
// if non-exist or expired, return empty string.
|
||||
func (fc *FileCache) GetMulti(keys []string) []interface{} {
|
||||
var rc []interface{}
|
||||
for _, key := range keys {
|
||||
rc = append(rc, fc.Get(key))
|
||||
}
|
||||
return rc
|
||||
}
|
||||
|
||||
// Put value into file cache.
|
||||
// timeout means how long to keep this file, unit of ms.
|
||||
// if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever.
|
||||
func (this *FileCache) Put(key string, val interface{}, timeout int64) error {
|
||||
func (fc *FileCache) Put(key string, val interface{}, timeout int64) error {
|
||||
gob.Register(val)
|
||||
|
||||
filename := this.getCacheFileName(key)
|
||||
var item FileCacheItem
|
||||
item.Data = val
|
||||
item := FileCacheItem{Data: val}
|
||||
if timeout == FileCacheEmbedExpiry {
|
||||
item.Expired = time.Now().Unix() + (86400 * 365 * 10) // ten years
|
||||
} else {
|
||||
@ -163,13 +159,12 @@ func (this *FileCache) Put(key string, val interface{}, timeout int64) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = File_put_contents(filename, data)
|
||||
return err
|
||||
return File_put_contents(fc.getCacheFileName(key), data)
|
||||
}
|
||||
|
||||
// Delete file cache value.
|
||||
func (this *FileCache) Delete(key string) error {
|
||||
filename := this.getCacheFileName(key)
|
||||
func (fc *FileCache) Delete(key string) error {
|
||||
filename := fc.getCacheFileName(key)
|
||||
if ok, _ := exists(filename); ok {
|
||||
return os.Remove(filename)
|
||||
}
|
||||
@ -177,44 +172,41 @@ func (this *FileCache) Delete(key string) error {
|
||||
}
|
||||
|
||||
// Increase cached int value.
|
||||
// this value is saving forever unless Delete.
|
||||
func (this *FileCache) Incr(key string) error {
|
||||
data := this.Get(key)
|
||||
// fc value is saving forever unless Delete.
|
||||
func (fc *FileCache) Incr(key string) error {
|
||||
data := fc.Get(key)
|
||||
var incr int
|
||||
//fmt.Println(reflect.TypeOf(data).Name())
|
||||
if reflect.TypeOf(data).Name() != "int" {
|
||||
incr = 0
|
||||
} else {
|
||||
incr = data.(int) + 1
|
||||
}
|
||||
this.Put(key, incr, FileCacheEmbedExpiry)
|
||||
fc.Put(key, incr, FileCacheEmbedExpiry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrease cached int value.
|
||||
func (this *FileCache) Decr(key string) error {
|
||||
data := this.Get(key)
|
||||
func (fc *FileCache) Decr(key string) error {
|
||||
data := fc.Get(key)
|
||||
var decr int
|
||||
if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 {
|
||||
decr = 0
|
||||
} else {
|
||||
decr = data.(int) - 1
|
||||
}
|
||||
this.Put(key, decr, FileCacheEmbedExpiry)
|
||||
fc.Put(key, decr, FileCacheEmbedExpiry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check value is exist.
|
||||
func (this *FileCache) IsExist(key string) bool {
|
||||
filename := this.getCacheFileName(key)
|
||||
ret, _ := exists(filename)
|
||||
func (fc *FileCache) IsExist(key string) bool {
|
||||
ret, _ := exists(fc.getCacheFileName(key))
|
||||
return ret
|
||||
}
|
||||
|
||||
// Clean cached files.
|
||||
// not implemented.
|
||||
func (this *FileCache) ClearAll() error {
|
||||
//this.CachePath
|
||||
func (fc *FileCache) ClearAll() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -232,22 +224,22 @@ func exists(path string) (bool, error) {
|
||||
|
||||
// Get bytes to file.
|
||||
// if non-exist, create this file.
|
||||
func File_get_contents(filename string) ([]byte, error) {
|
||||
f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||
if err != nil {
|
||||
return []byte(""), err
|
||||
func File_get_contents(filename string) (data []byte, e error) {
|
||||
f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
return []byte(""), err
|
||||
stat, e := f.Stat()
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
data := make([]byte, stat.Size())
|
||||
result, err := f.Read(data)
|
||||
if int64(result) == stat.Size() {
|
||||
return data, err
|
||||
data = make([]byte, stat.Size())
|
||||
result, e := f.Read(data)
|
||||
if e != nil || int64(result) != stat.Size() {
|
||||
return nil, e
|
||||
}
|
||||
return []byte(""), err
|
||||
return
|
||||
}
|
||||
|
||||
// Put bytes to file.
|
||||
|
167
cache/memcache/memcache.go
vendored
167
cache/memcache/memcache.go
vendored
@ -1,24 +1,48 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
// package memcahe for cache provider
|
||||
//
|
||||
// depend on github.com/bradfitz/gomemcache/memcache
|
||||
//
|
||||
// go install github.com/bradfitz/gomemcache/memcache
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/astaxie/beego/cache/memcache"
|
||||
// "github.com/astaxie/beego/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("memcache", `{"conn":"127.0.0.1:11211"}`)
|
||||
//
|
||||
// more docs http://beego.me/docs/module/cache.md
|
||||
package memcache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/memcache"
|
||||
"github.com/bradfitz/gomemcache/memcache"
|
||||
|
||||
"github.com/astaxie/beego/cache"
|
||||
)
|
||||
|
||||
// Memcache adapter.
|
||||
type MemcacheCache struct {
|
||||
c *memcache.Connection
|
||||
conninfo string
|
||||
conn *memcache.Client
|
||||
conninfo []string
|
||||
}
|
||||
|
||||
// create new memcache adapter.
|
||||
@ -28,32 +52,47 @@ func NewMemCache() *MemcacheCache {
|
||||
|
||||
// get value from memcache.
|
||||
func (rc *MemcacheCache) Get(key string) interface{} {
|
||||
if rc.c == nil {
|
||||
var err error
|
||||
rc.c, err = rc.connectInit()
|
||||
if err != nil {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
v, err := rc.c.Get(key)
|
||||
if err != nil {
|
||||
if item, err := rc.conn.Get(key); err == nil {
|
||||
return string(item.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// get value from memcache.
|
||||
func (rc *MemcacheCache) GetMulti(keys []string) []interface{} {
|
||||
size := len(keys)
|
||||
var rv []interface{}
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
for i := 0; i < size; i++ {
|
||||
rv = append(rv, err)
|
||||
}
|
||||
var contain interface{}
|
||||
if len(v) > 0 {
|
||||
contain = string(v[0].Value)
|
||||
return rv
|
||||
}
|
||||
}
|
||||
mv, err := rc.conn.GetMulti(keys)
|
||||
if err == nil {
|
||||
for _, v := range mv {
|
||||
rv = append(rv, string(v.Value))
|
||||
}
|
||||
return rv
|
||||
} else {
|
||||
contain = nil
|
||||
for i := 0; i < size; i++ {
|
||||
rv = append(rv, err)
|
||||
}
|
||||
return rv
|
||||
}
|
||||
return contain
|
||||
}
|
||||
|
||||
// put value to memcache. only support string.
|
||||
func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error {
|
||||
if rc.c == nil {
|
||||
var err error
|
||||
rc.c, err = rc.connectInit()
|
||||
if err != nil {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -61,69 +100,64 @@ func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error {
|
||||
if !ok {
|
||||
return errors.New("val must string")
|
||||
}
|
||||
stored, err := rc.c.Set(key, 0, uint64(timeout), []byte(v))
|
||||
if err == nil && stored == false {
|
||||
return errors.New("stored fail")
|
||||
}
|
||||
return err
|
||||
item := memcache.Item{Key: key, Value: []byte(v), Expiration: int32(timeout)}
|
||||
return rc.conn.Set(&item)
|
||||
}
|
||||
|
||||
// delete value in memcache.
|
||||
func (rc *MemcacheCache) Delete(key string) error {
|
||||
if rc.c == nil {
|
||||
var err error
|
||||
rc.c, err = rc.connectInit()
|
||||
if err != nil {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.c.Delete(key)
|
||||
return err
|
||||
return rc.conn.Delete(key)
|
||||
}
|
||||
|
||||
// [Not Support]
|
||||
// increase counter.
|
||||
func (rc *MemcacheCache) Incr(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.Increment(key, 1)
|
||||
return err
|
||||
}
|
||||
|
||||
// [Not Support]
|
||||
// decrease counter.
|
||||
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.
|
||||
func (rc *MemcacheCache) IsExist(key string) bool {
|
||||
if rc.c == nil {
|
||||
var err error
|
||||
rc.c, err = rc.connectInit()
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Get(key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
v, err := rc.c.Get(key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// clear all cached in memcache.
|
||||
func (rc *MemcacheCache) ClearAll() error {
|
||||
if rc.c == nil {
|
||||
var err error
|
||||
rc.c, err = rc.connectInit()
|
||||
if err != nil {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := rc.c.FlushAll()
|
||||
return err
|
||||
return rc.conn.FlushAll()
|
||||
}
|
||||
|
||||
// start memcache adapter.
|
||||
@ -135,24 +169,19 @@ func (rc *MemcacheCache) StartAndGC(config string) error {
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
}
|
||||
rc.conninfo = cf["conn"]
|
||||
var err error
|
||||
if rc.c != nil {
|
||||
rc.c, err = rc.connectInit()
|
||||
if err != nil {
|
||||
return errors.New("dial tcp conn error")
|
||||
rc.conninfo = strings.Split(cf["conn"], ";")
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// connect to memcache and keep the connection.
|
||||
func (rc *MemcacheCache) connectInit() (*memcache.Connection, error) {
|
||||
c, err := memcache.Connect(rc.conninfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
func (rc *MemcacheCache) connectInit() error {
|
||||
rc.conn = memcache.New(rc.conninfo...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
106
cache/memcache/memcache_test.go
vendored
Normal file
106
cache/memcache/memcache_test.go
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
// 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 memcache
|
||||
|
||||
import (
|
||||
_ "github.com/bradfitz/gomemcache/memcache"
|
||||
|
||||
"github.com/astaxie/beego/cache"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRedisCache(t *testing.T) {
|
||||
bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`)
|
||||
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, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
if err = bm.Incr("astaxie"); err != nil {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
|
||||
if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 2 {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
if err = bm.Decr("astaxie"); err != nil {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
|
||||
if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || 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 := bm.Get("astaxie").(string); v != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
//test GetMulti
|
||||
if err = bm.Put("astaxie1", "author1", 10); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0].(string) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
|
||||
// test clear all
|
||||
if err = bm.ClearAll(); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
}
|
46
cache/memory.go
vendored
46
cache/memory.go
vendored
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
|
||||
@ -46,15 +54,24 @@ func NewMemoryCache() *MemoryCache {
|
||||
func (bc *MemoryCache) Get(name string) interface{} {
|
||||
bc.lock.RLock()
|
||||
defer bc.lock.RUnlock()
|
||||
itm, ok := bc.items[name]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if itm, ok := bc.items[name]; ok {
|
||||
if (time.Now().Unix() - itm.Lastaccess.Unix()) > itm.expired {
|
||||
go bc.Delete(name)
|
||||
return nil
|
||||
}
|
||||
return itm.val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMulti gets caches from memory.
|
||||
// if non-existed or expired, return nil.
|
||||
func (bc *MemoryCache) GetMulti(names []string) []interface{} {
|
||||
var rc []interface{}
|
||||
for _, name := range names {
|
||||
rc = append(rc, bc.Get(name))
|
||||
}
|
||||
return rc
|
||||
}
|
||||
|
||||
// Put cache to memory.
|
||||
@ -62,12 +79,11 @@ func (bc *MemoryCache) Get(name string) interface{} {
|
||||
func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error {
|
||||
bc.lock.Lock()
|
||||
defer bc.lock.Unlock()
|
||||
t := MemoryItem{
|
||||
bc.items[name] = &MemoryItem{
|
||||
val: value,
|
||||
Lastaccess: time.Now(),
|
||||
expired: expired,
|
||||
}
|
||||
bc.items[name] = &t
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -79,8 +95,7 @@ func (bc *MemoryCache) Delete(name string) error {
|
||||
return errors.New("key not exist")
|
||||
}
|
||||
delete(bc.items, name)
|
||||
_, valid := bc.items[name]
|
||||
if valid {
|
||||
if _, ok := bc.items[name]; ok {
|
||||
return errors.New("delete key error")
|
||||
}
|
||||
return nil
|
||||
@ -197,7 +212,7 @@ func (bc *MemoryCache) vaccuum() {
|
||||
if bc.items == nil {
|
||||
return
|
||||
}
|
||||
for name, _ := range bc.items {
|
||||
for name := range bc.items {
|
||||
bc.item_expired(name)
|
||||
}
|
||||
}
|
||||
@ -211,8 +226,7 @@ func (bc *MemoryCache) item_expired(name string) bool {
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
sec := time.Now().Unix() - itm.Lastaccess.Unix()
|
||||
if sec >= itm.expired {
|
||||
if time.Now().Unix()-itm.Lastaccess.Unix() >= itm.expired {
|
||||
delete(bc.items, name)
|
||||
return true
|
||||
}
|
||||
|
164
cache/redis/redis.go
vendored
164
cache/redis/redis.go
vendored
@ -1,17 +1,41 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
// package redis for cache provider
|
||||
//
|
||||
// depend on github.com/garyburd/redigo/redis
|
||||
//
|
||||
// go install github.com/garyburd/redigo/redis
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/astaxie/beego/cache/redis"
|
||||
// "github.com/astaxie/beego/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("redis", `{"conn":"127.0.0.1:11211"}`)
|
||||
//
|
||||
// more docs http://beego.me/docs/module/cache.md
|
||||
package redis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/beego/redigo/redis"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
|
||||
"github.com/astaxie/beego/cache"
|
||||
)
|
||||
@ -25,7 +49,9 @@ var (
|
||||
type RedisCache struct {
|
||||
p *redis.Pool // redis connection pool
|
||||
conninfo string
|
||||
dbNum int
|
||||
key string
|
||||
password string
|
||||
}
|
||||
|
||||
// create new redis cache with default collection name.
|
||||
@ -43,57 +69,111 @@ func (rc *RedisCache) do(commandName string, args ...interface{}) (reply interfa
|
||||
|
||||
// Get cache from redis.
|
||||
func (rc *RedisCache) Get(key string) interface{} {
|
||||
v, err := rc.do("HGET", rc.key, key)
|
||||
if err != nil {
|
||||
if v, err := rc.do("GET", key); err == nil {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMulti get cache from redis.
|
||||
func (rc *RedisCache) GetMulti(keys []string) []interface{} {
|
||||
size := len(keys)
|
||||
var rv []interface{}
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
var err error
|
||||
for _, key := range keys {
|
||||
err = c.Send("GET", key)
|
||||
if err != nil {
|
||||
goto ERROR
|
||||
}
|
||||
}
|
||||
if err = c.Flush(); err != nil {
|
||||
goto ERROR
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
if v, err := c.Receive(); err == nil {
|
||||
rv = append(rv, v.([]byte))
|
||||
} else {
|
||||
rv = append(rv, err)
|
||||
}
|
||||
}
|
||||
return rv
|
||||
ERROR:
|
||||
rv = rv[0:0]
|
||||
for i := 0; i < size; i++ {
|
||||
rv = append(rv, nil)
|
||||
}
|
||||
|
||||
return v
|
||||
return rv
|
||||
}
|
||||
|
||||
// put cache to redis.
|
||||
// timeout is ignored.
|
||||
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
|
||||
}
|
||||
|
||||
// delete cache in redis.
|
||||
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
|
||||
}
|
||||
|
||||
// check cache exist in redis.
|
||||
// check cache's existence in redis.
|
||||
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 {
|
||||
return false
|
||||
}
|
||||
|
||||
if v == false {
|
||||
if _, err = rc.do("HDEL", rc.key, key); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// increase counter in redis.
|
||||
func (rc *RedisCache) Incr(key string) error {
|
||||
_, err := redis.Bool(rc.do("HINCRBY", rc.key, key, 1))
|
||||
_, err := redis.Bool(rc.do("INCRBY", key, 1))
|
||||
return err
|
||||
}
|
||||
|
||||
// decrease counter in redis.
|
||||
func (rc *RedisCache) Decr(key string) error {
|
||||
_, err := redis.Bool(rc.do("HINCRBY", rc.key, key, -1))
|
||||
_, err := redis.Bool(rc.do("INCRBY", key, -1))
|
||||
return err
|
||||
}
|
||||
|
||||
// clean all cache in redis. delete this redis collection.
|
||||
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
|
||||
}
|
||||
|
||||
// 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,
|
||||
// so no gc operation.
|
||||
func (rc *RedisCache) StartAndGC(config string) error {
|
||||
@ -103,37 +183,55 @@ func (rc *RedisCache) StartAndGC(config string) error {
|
||||
if _, ok := cf["key"]; !ok {
|
||||
cf["key"] = DefaultKey
|
||||
}
|
||||
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
}
|
||||
|
||||
if _, ok := cf["dbNum"]; !ok {
|
||||
cf["dbNum"] = "0"
|
||||
}
|
||||
if _, ok := cf["password"]; !ok {
|
||||
cf["password"] = ""
|
||||
}
|
||||
rc.key = cf["key"]
|
||||
rc.conninfo = cf["conn"]
|
||||
rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
|
||||
rc.password = cf["password"]
|
||||
|
||||
rc.connectInit()
|
||||
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
if err := c.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return c.Err()
|
||||
}
|
||||
|
||||
// connect to redis.
|
||||
func (rc *RedisCache) connectInit() {
|
||||
dialFunc := func() (c redis.Conn, err error) {
|
||||
c, err = redis.Dial("tcp", rc.conninfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rc.password != "" {
|
||||
if _, err := c.Do("AUTH", rc.password); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
_, selecterr := c.Do("SELECT", rc.dbNum)
|
||||
if selecterr != nil {
|
||||
c.Close()
|
||||
return nil, selecterr
|
||||
}
|
||||
return
|
||||
}
|
||||
// initialize a new pool
|
||||
rc.p = &redis.Pool{
|
||||
MaxIdle: 3,
|
||||
IdleTimeout: 180 * time.Second,
|
||||
Dial: func() (redis.Conn, error) {
|
||||
c, err := redis.Dial("tcp", rc.conninfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
},
|
||||
Dial: dialFunc,
|
||||
}
|
||||
}
|
||||
|
||||
|
106
cache/redis/redis_test.go
vendored
Normal file
106
cache/redis/redis_test.go
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
// 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 GetMulti
|
||||
if err = bm.Put("astaxie1", "author1", 10); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if v, _ := redis.String(vv[0], nil); v != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if v, _ := redis.String(vv[1], nil); v != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
|
||||
// test clear all
|
||||
if err = bm.ClearAll(); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
}
|
394
config.go
394
config.go
@ -1,13 +1,20 @@
|
||||
// Beego (http://beego.me/)
|
||||
// @description beego is an open-source, high-performance web framework for the Go programming language.
|
||||
// @link http://github.com/astaxie/beego for the canonical source repository
|
||||
// @license http://github.com/astaxie/beego/blob/master/LICENSE
|
||||
// @authors astaxie
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package beego
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
@ -33,6 +40,7 @@ var (
|
||||
EnableHttpListen bool
|
||||
HttpAddr string
|
||||
HttpPort int
|
||||
ListenTCP4 bool
|
||||
EnableHttpTLS bool
|
||||
HttpsPort int
|
||||
HttpCertFile string
|
||||
@ -40,19 +48,19 @@ var (
|
||||
RecoverPanic bool // flag of auto recover panic
|
||||
AutoRender bool // flag of render template automatically
|
||||
ViewsPath string
|
||||
AppConfig *beegoAppConfig
|
||||
RunMode string // run mode, "dev" or "prod"
|
||||
AppConfig config.ConfigContainer
|
||||
GlobalSessions *session.Manager // global session mananger
|
||||
SessionOn bool // flag of starting session auto. default is false.
|
||||
SessionProvider string // default session provider, memory, mysql , redis ,etc.
|
||||
SessionName string // the cookie name when saving session id into cookie.
|
||||
SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session.
|
||||
SessionSavePath string // if use mysql/redis/file provider, define save path to connection info.
|
||||
SessionHashFunc string // session hash generation func.
|
||||
SessionHashKey string // session hash salt string.
|
||||
SessionCookieLifeTime int // the life time of session id in cookie.
|
||||
SessionAutoSetCookie bool // auto setcookie
|
||||
SessionDomain string // the cookie domain default is empty
|
||||
UseFcgi bool
|
||||
UseStdIo bool
|
||||
MaxMemory int64
|
||||
EnableGzip bool // flag of enable gzip
|
||||
DirectoryIndex bool // flag of display directory index. default is false.
|
||||
@ -72,8 +80,140 @@ var (
|
||||
FlashSeperator string // used to seperate flash key:value
|
||||
AppConfigProvider string // config provider
|
||||
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
|
||||
Graceful bool // use graceful start the server
|
||||
)
|
||||
|
||||
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 {
|
||||
err := b.innerConfig.Set(RunMode+"::"+key, val)
|
||||
if err == nil {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
v := b.String(key)
|
||||
if v != "" {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string {
|
||||
v := b.Strings(key)
|
||||
if len(v) != 0 {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int {
|
||||
v, err := b.Int(key)
|
||||
if err == nil {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 {
|
||||
v, err := b.Int64(key)
|
||||
if err == nil {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool {
|
||||
v, err := b.Bool(key)
|
||||
if err == nil {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 {
|
||||
v, err := b.Float(key)
|
||||
if err == nil {
|
||||
return v
|
||||
}
|
||||
return 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() {
|
||||
// create beego application
|
||||
BeeApp = NewApp()
|
||||
@ -125,12 +265,11 @@ func init() {
|
||||
SessionName = "beegosessionID"
|
||||
SessionGCMaxLifetime = 3600
|
||||
SessionSavePath = ""
|
||||
SessionHashFunc = "sha1"
|
||||
SessionHashKey = "beegoserversessionkey"
|
||||
SessionCookieLifeTime = 0 //set cookie default is the brower life
|
||||
SessionAutoSetCookie = true
|
||||
|
||||
UseFcgi = false
|
||||
UseStdIo = false
|
||||
|
||||
MaxMemory = 1 << 26 //64MB
|
||||
|
||||
@ -155,6 +294,8 @@ func init() {
|
||||
FlashName = "BEEGO_FLASH"
|
||||
FlashSeperator = "BEEGOFLASH"
|
||||
|
||||
RouterCaseSensitive = true
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
// init BeeLogger
|
||||
@ -163,168 +304,167 @@ func init() {
|
||||
if err != nil {
|
||||
fmt.Println("init console log error:", err)
|
||||
}
|
||||
SetLogFuncCall(true)
|
||||
|
||||
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
|
||||
Info(err)
|
||||
ac := config.NewFakeConfig()
|
||||
AppConfig = &beegoAppConfig{ac}
|
||||
Warning(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseConfig parsed default config file.
|
||||
// now only support ini, next will support json.
|
||||
func ParseConfig() (err error) {
|
||||
AppConfig, err = config.NewConfig(AppConfigProvider, AppConfigPath)
|
||||
AppConfig, err = newAppConfig(AppConfigProvider, AppConfigPath)
|
||||
if err != nil {
|
||||
AppConfig = config.NewFakeConfig()
|
||||
return err
|
||||
} else {
|
||||
|
||||
if v, err := getConfig("string", "HttpAddr"); err == nil {
|
||||
HttpAddr = v.(string)
|
||||
}
|
||||
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("int", "HttpPort"); err == nil {
|
||||
HttpPort = v.(int)
|
||||
HttpAddr = AppConfig.String("HttpAddr")
|
||||
|
||||
if v, err := AppConfig.Int("HttpPort"); err == nil {
|
||||
HttpPort = v
|
||||
}
|
||||
|
||||
if v, err := getConfig("bool", "EnableHttpListen"); err == nil {
|
||||
EnableHttpListen = v.(bool)
|
||||
if v, err := AppConfig.Bool("ListenTCP4"); err == nil {
|
||||
ListenTCP4 = v
|
||||
}
|
||||
|
||||
if maxmemory, err := getConfig("int64", "MaxMemory"); err == nil {
|
||||
MaxMemory = maxmemory.(int64)
|
||||
if v, err := AppConfig.Bool("EnableHttpListen"); err == nil {
|
||||
EnableHttpListen = v
|
||||
}
|
||||
|
||||
if appname, _ := getConfig("string", "AppName"); appname != "" {
|
||||
AppName = appname.(string)
|
||||
if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil {
|
||||
MaxMemory = maxmemory
|
||||
}
|
||||
|
||||
if runmode, _ := getConfig("string", "RunMode"); runmode != "" {
|
||||
RunMode = runmode.(string)
|
||||
if appname := AppConfig.String("AppName"); appname != "" {
|
||||
AppName = appname
|
||||
}
|
||||
|
||||
if autorender, err := getConfig("bool", "AutoRender"); err == nil {
|
||||
AutoRender = autorender.(bool)
|
||||
if autorender, err := AppConfig.Bool("AutoRender"); err == nil {
|
||||
AutoRender = autorender
|
||||
}
|
||||
|
||||
if autorecover, err := getConfig("bool", "RecoverPanic"); err == nil {
|
||||
RecoverPanic = autorecover.(bool)
|
||||
if autorecover, err := AppConfig.Bool("RecoverPanic"); err == nil {
|
||||
RecoverPanic = autorecover
|
||||
}
|
||||
|
||||
if views, _ := getConfig("string", "ViewsPath"); views != "" {
|
||||
ViewsPath = views.(string)
|
||||
if views := AppConfig.String("ViewsPath"); views != "" {
|
||||
ViewsPath = views
|
||||
}
|
||||
|
||||
if sessionon, err := getConfig("bool", "SessionOn"); err == nil {
|
||||
SessionOn = sessionon.(bool)
|
||||
if sessionon, err := AppConfig.Bool("SessionOn"); err == nil {
|
||||
SessionOn = sessionon
|
||||
}
|
||||
|
||||
if sessProvider, _ := getConfig("string", "SessionProvider"); sessProvider != "" {
|
||||
SessionProvider = sessProvider.(string)
|
||||
if sessProvider := AppConfig.String("SessionProvider"); sessProvider != "" {
|
||||
SessionProvider = sessProvider
|
||||
}
|
||||
|
||||
if sessName, _ := getConfig("string", "SessionName"); sessName != "" {
|
||||
SessionName = sessName.(string)
|
||||
if sessName := AppConfig.String("SessionName"); sessName != "" {
|
||||
SessionName = sessName
|
||||
}
|
||||
|
||||
if sesssavepath, _ := getConfig("string", "SessionSavePath"); sesssavepath != "" {
|
||||
SessionSavePath = sesssavepath.(string)
|
||||
if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" {
|
||||
SessionSavePath = sesssavepath
|
||||
}
|
||||
|
||||
if sesshashfunc, _ := getConfig("string", "SessionHashFunc"); sesshashfunc != "" {
|
||||
SessionHashFunc = sesshashfunc.(string)
|
||||
if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
|
||||
SessionGCMaxLifetime = sessMaxLifeTime
|
||||
}
|
||||
|
||||
if sesshashkey, _ := getConfig("string", "SessionHashKey"); sesshashkey != "" {
|
||||
SessionHashKey = sesshashkey.(string)
|
||||
if sesscookielifetime, err := AppConfig.Int("SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
|
||||
SessionCookieLifeTime = sesscookielifetime
|
||||
}
|
||||
|
||||
if sessMaxLifeTime, err := getConfig("int64", "SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
|
||||
SessionGCMaxLifetime = sessMaxLifeTime.(int64)
|
||||
if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil {
|
||||
UseFcgi = usefcgi
|
||||
}
|
||||
|
||||
if sesscookielifetime, err := getConfig("int", "SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
|
||||
SessionCookieLifeTime = sesscookielifetime.(int)
|
||||
if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil {
|
||||
EnableGzip = enablegzip
|
||||
}
|
||||
|
||||
if usefcgi, err := getConfig("bool", "UseFcgi"); err == nil {
|
||||
UseFcgi = usefcgi.(bool)
|
||||
if directoryindex, err := AppConfig.Bool("DirectoryIndex"); err == nil {
|
||||
DirectoryIndex = directoryindex
|
||||
}
|
||||
|
||||
if enablegzip, err := getConfig("bool", "EnableGzip"); err == nil {
|
||||
EnableGzip = enablegzip.(bool)
|
||||
if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil {
|
||||
HttpServerTimeOut = timeout
|
||||
}
|
||||
|
||||
if directoryindex, err := getConfig("bool", "DirectoryIndex"); err == nil {
|
||||
DirectoryIndex = directoryindex.(bool)
|
||||
if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil {
|
||||
ErrorsShow = errorsshow
|
||||
}
|
||||
|
||||
if timeout, err := getConfig("int64", "HttpServerTimeOut"); err == nil {
|
||||
HttpServerTimeOut = timeout.(int64)
|
||||
if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil {
|
||||
CopyRequestBody = copyrequestbody
|
||||
}
|
||||
|
||||
if errorsshow, err := getConfig("bool", "ErrorsShow"); err == nil {
|
||||
ErrorsShow = errorsshow.(bool)
|
||||
if xsrfkey := AppConfig.String("XSRFKEY"); xsrfkey != "" {
|
||||
XSRFKEY = xsrfkey
|
||||
}
|
||||
|
||||
if copyrequestbody, err := getConfig("bool", "CopyRequestBody"); err == nil {
|
||||
CopyRequestBody = copyrequestbody.(bool)
|
||||
if enablexsrf, err := AppConfig.Bool("EnableXSRF"); err == nil {
|
||||
EnableXSRF = enablexsrf
|
||||
}
|
||||
|
||||
if xsrfkey, _ := getConfig("string", "XSRFKEY"); xsrfkey != "" {
|
||||
XSRFKEY = xsrfkey.(string)
|
||||
if expire, err := AppConfig.Int("XSRFExpire"); err == nil {
|
||||
XSRFExpire = expire
|
||||
}
|
||||
|
||||
if enablexsrf, err := getConfig("bool", "EnableXSRF"); err == nil {
|
||||
EnableXSRF = enablexsrf.(bool)
|
||||
if tplleft := AppConfig.String("TemplateLeft"); tplleft != "" {
|
||||
TemplateLeft = tplleft
|
||||
}
|
||||
|
||||
if expire, err := getConfig("int", "XSRFExpire"); err == nil {
|
||||
XSRFExpire = expire.(int)
|
||||
if tplright := AppConfig.String("TemplateRight"); tplright != "" {
|
||||
TemplateRight = tplright
|
||||
}
|
||||
|
||||
if tplleft, _ := getConfig("string", "TemplateLeft"); tplleft != "" {
|
||||
TemplateLeft = tplleft.(string)
|
||||
if httptls, err := AppConfig.Bool("EnableHttpTLS"); err == nil {
|
||||
EnableHttpTLS = httptls
|
||||
}
|
||||
|
||||
if tplright, _ := getConfig("string", "TemplateRight"); tplright != "" {
|
||||
TemplateRight = tplright.(string)
|
||||
if httpsport, err := AppConfig.Int("HttpsPort"); err == nil {
|
||||
HttpsPort = httpsport
|
||||
}
|
||||
|
||||
if httptls, err := getConfig("bool", "EnableHttpTLS"); err == nil {
|
||||
EnableHttpTLS = httptls.(bool)
|
||||
if certfile := AppConfig.String("HttpCertFile"); certfile != "" {
|
||||
HttpCertFile = certfile
|
||||
}
|
||||
|
||||
if httpsport, err := getConfig("int", "HttpsPort"); err == nil {
|
||||
HttpsPort = httpsport.(int)
|
||||
if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" {
|
||||
HttpKeyFile = keyfile
|
||||
}
|
||||
|
||||
if certfile, _ := getConfig("string", "HttpCertFile"); certfile != "" {
|
||||
HttpCertFile = certfile.(string)
|
||||
if serverName := AppConfig.String("BeegoServerName"); serverName != "" {
|
||||
BeegoServerName = serverName
|
||||
}
|
||||
|
||||
if keyfile, _ := getConfig("string", "HttpKeyFile"); keyfile != "" {
|
||||
HttpKeyFile = keyfile.(string)
|
||||
if flashname := AppConfig.String("FlashName"); flashname != "" {
|
||||
FlashName = flashname
|
||||
}
|
||||
|
||||
if serverName, _ := getConfig("string", "BeegoServerName"); serverName != "" {
|
||||
BeegoServerName = serverName.(string)
|
||||
if flashseperator := AppConfig.String("FlashSeperator"); flashseperator != "" {
|
||||
FlashSeperator = flashseperator
|
||||
}
|
||||
|
||||
if flashname, _ := getConfig("string", "FlashName"); flashname != "" {
|
||||
FlashName = flashname.(string)
|
||||
}
|
||||
|
||||
if flashseperator, _ := getConfig("string", "FlashSeperator"); flashseperator != "" {
|
||||
FlashSeperator = flashseperator.(string)
|
||||
}
|
||||
|
||||
if sd, _ := getConfig("string", "StaticDir"); sd != "" {
|
||||
if sd := AppConfig.String("StaticDir"); sd != "" {
|
||||
for k := range StaticDir {
|
||||
delete(StaticDir, k)
|
||||
}
|
||||
sds := strings.Fields(sd.(string))
|
||||
sds := strings.Fields(sd)
|
||||
for _, v := range sds {
|
||||
if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
|
||||
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1]
|
||||
@ -334,8 +474,8 @@ func ParseConfig() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if sgz, _ := getConfig("string", "StaticExtensionsToGzip"); sgz != "" {
|
||||
extensions := strings.Split(sgz.(string), ",")
|
||||
if sgz := AppConfig.String("StaticExtensionsToGzip"); sgz != "" {
|
||||
extensions := strings.Split(sgz, ",")
|
||||
if len(extensions) > 0 {
|
||||
StaticExtensionsToGzip = []string{}
|
||||
for _, ext := range extensions {
|
||||
@ -351,63 +491,27 @@ func ParseConfig() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if enableadmin, err := getConfig("bool", "EnableAdmin"); err == nil {
|
||||
EnableAdmin = enableadmin.(bool)
|
||||
if enableadmin, err := AppConfig.Bool("EnableAdmin"); err == nil {
|
||||
EnableAdmin = enableadmin
|
||||
}
|
||||
|
||||
if adminhttpaddr, _ := getConfig("string", "AdminHttpAddr"); adminhttpaddr != "" {
|
||||
AdminHttpAddr = adminhttpaddr.(string)
|
||||
if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" {
|
||||
AdminHttpAddr = adminhttpaddr
|
||||
}
|
||||
|
||||
if adminhttpport, err := getConfig("int", "AdminHttpPort"); err == nil {
|
||||
AdminHttpPort = adminhttpport.(int)
|
||||
if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil {
|
||||
AdminHttpPort = adminhttpport
|
||||
}
|
||||
|
||||
if enabledocs, err := getConfig("bool", "EnableDocs"); err == nil {
|
||||
EnableDocs = enabledocs.(bool)
|
||||
if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil {
|
||||
EnableDocs = enabledocs
|
||||
}
|
||||
|
||||
if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil {
|
||||
RouterCaseSensitive = casesensitive
|
||||
}
|
||||
if graceful, err := AppConfig.Bool("Graceful"); err == nil {
|
||||
Graceful = graceful
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
@ -1,9 +1,44 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Usage:
|
||||
// import(
|
||||
// "github.com/astaxie/beego/config"
|
||||
// )
|
||||
//
|
||||
// cnf, err := config.NewConfig("ini", "config.conf")
|
||||
//
|
||||
// cnf APIS:
|
||||
//
|
||||
// cnf.Set(key, val string) error
|
||||
// cnf.String(key string) string
|
||||
// cnf.Strings(key string) []string
|
||||
// cnf.Int(key string) (int, error)
|
||||
// cnf.Int64(key string) (int64, error)
|
||||
// cnf.Bool(key string) (bool, error)
|
||||
// cnf.Float(key string) (float64, error)
|
||||
// cnf.DefaultString(key string, defaultval string) string
|
||||
// cnf.DefaultStrings(key string, defaultval []string) []string
|
||||
// cnf.DefaultInt(key string, defaultval int) int
|
||||
// cnf.DefaultInt64(key string, defaultval int64) int64
|
||||
// cnf.DefaultBool(key string, defaultval bool) bool
|
||||
// cnf.DefaultFloat(key string, defaultval float64) float64
|
||||
// cnf.DIY(key string) (interface{}, error)
|
||||
// cnf.GetSection(section string) (map[string]string, error)
|
||||
// cnf.SaveConfigFile(filename string) error
|
||||
//
|
||||
// more docs http://beego.me/docs/module/config.md
|
||||
package config
|
||||
|
||||
import (
|
||||
@ -19,12 +54,21 @@ type ConfigContainer interface {
|
||||
Int64(key string) (int64, error)
|
||||
Bool(key string) (bool, error)
|
||||
Float(key string) (float64, error)
|
||||
DefaultString(key string, defaultval string) string // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
|
||||
DefaultStrings(key string, defaultval []string) []string //get string slice
|
||||
DefaultInt(key string, defaultval int) int
|
||||
DefaultInt64(key string, defaultval int64) int64
|
||||
DefaultBool(key string, defaultval bool) bool
|
||||
DefaultFloat(key string, defaultval float64) float64
|
||||
DIY(key string) (interface{}, error)
|
||||
GetSection(section string) (map[string]string, error)
|
||||
SaveConfigFile(filename string) error
|
||||
}
|
||||
|
||||
// Config is the adapter interface for parsing config file to get raw data to ConfigContainer.
|
||||
type Config interface {
|
||||
Parse(key string) (ConfigContainer, error)
|
||||
ParseData(data []byte) (ConfigContainer, error)
|
||||
}
|
||||
|
||||
var adapters = make(map[string]Config)
|
||||
@ -36,7 +80,7 @@ func Register(name string, adapter Config) {
|
||||
if adapter == 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)
|
||||
}
|
||||
adapters[name] = adapter
|
||||
@ -51,3 +95,13 @@ func NewConfig(adapterName, fileaname string) (ConfigContainer, error) {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
@ -17,13 +25,11 @@ type fakeConfigContainer struct {
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) getData(key string) string {
|
||||
key = strings.ToLower(key)
|
||||
return c.data[key]
|
||||
return c.data[strings.ToLower(key)]
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) Set(key, val string) error {
|
||||
key = strings.ToLower(key)
|
||||
c.data[key] = val
|
||||
c.data[strings.ToLower(key)] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -31,34 +37,89 @@ func (c *fakeConfigContainer) String(key string) string {
|
||||
return c.getData(key)
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string {
|
||||
if v := c.getData(key); v == "" {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) Strings(key string) []string {
|
||||
return strings.Split(c.getData(key), ";")
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||
if v := c.Strings(key); len(v) == 0 {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) Int(key string) (int, error) {
|
||||
return strconv.Atoi(c.getData(key))
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) DefaultInt(key string, defaultval int) int {
|
||||
if v, err := c.Int(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) Int64(key string) (int64, error) {
|
||||
return strconv.ParseInt(c.getData(key), 10, 64)
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
||||
if v, err := c.Int64(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) Bool(key string) (bool, error) {
|
||||
return strconv.ParseBool(c.getData(key))
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
||||
if v, err := c.Bool(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) Float(key string) (float64, error) {
|
||||
return strconv.ParseFloat(c.getData(key), 64)
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
||||
if v, err := c.Float(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) DIY(key string) (interface{}, error) {
|
||||
key = strings.ToLower(key)
|
||||
if v, ok := c.data[key]; ok {
|
||||
if v, ok := c.data[strings.ToLower(key)]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, errors.New("key not find")
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) GetSection(section string) (map[string]string, error) {
|
||||
return nil, errors.New("not implement in the fakeConfigContainer")
|
||||
}
|
||||
|
||||
func (c *fakeConfigContainer) SaveConfigFile(filename string) error {
|
||||
return errors.New("not implement in the fakeConfigContainer")
|
||||
}
|
||||
|
||||
var _ ConfigContainer = new(fakeConfigContainer)
|
||||
|
||||
func NewFakeConfig() ConfigContainer {
|
||||
|
291
config/ini.go
291
config/ini.go
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
@ -10,11 +18,15 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@ -27,6 +39,7 @@ var (
|
||||
bDQuote = []byte{'"'} // quote signal
|
||||
sectionStart = []byte{'['} // section start signal
|
||||
sectionEnd = []byte{']'} // section end signal
|
||||
lineBreak = "\n"
|
||||
)
|
||||
|
||||
// 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.
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -53,6 +70,13 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
|
||||
|
||||
var comment bytes.Buffer
|
||||
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
|
||||
for {
|
||||
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) {
|
||||
section = string(line[1 : len(line)-1])
|
||||
section = strings.ToLower(section) // section name case insensitive
|
||||
section = strings.ToLower(string(line[1 : len(line)-1])) // section name case insensitive
|
||||
if comment.Len() > 0 {
|
||||
cfg.sectionComment[section] = comment.String()
|
||||
comment.Reset()
|
||||
@ -89,74 +112,261 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
|
||||
if _, ok := cfg.data[section]; !ok {
|
||||
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])
|
||||
keyValue := bytes.SplitN(line, bEqual, 2)
|
||||
|
||||
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, `"`)
|
||||
}
|
||||
|
||||
key := string(bytes.TrimSpace(keyval[0])) // key name case insensitive
|
||||
key = strings.ToLower(key)
|
||||
cfg.data[section][key] = string(val)
|
||||
if comment.Len() > 0 {
|
||||
cfg.keycomment[section+"."+key] = comment.String()
|
||||
cfg.keyComment[section+"."+key] = comment.String()
|
||||
comment.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (ini *IniConfig) ParseData(data []byte) (ConfigContainer, error) {
|
||||
// Save memory data to temporary file
|
||||
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
|
||||
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
|
||||
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ini.Parse(tmpName)
|
||||
}
|
||||
|
||||
// A Config represents the ini configuration.
|
||||
// When set and get value, support key as section:name type.
|
||||
type IniConfigContainer struct {
|
||||
filename string
|
||||
data map[string]map[string]string // section=> key:val
|
||||
sectionComment map[string]string // section : comment
|
||||
keycomment map[string]string // id: []{comment, key...}; id 1 is for main comment.
|
||||
keyComment map[string]string // id: []{comment, key...}; id 1 is for main comment.
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// Bool returns the boolean value for a given key.
|
||||
func (c *IniConfigContainer) Bool(key string) (bool, error) {
|
||||
key = strings.ToLower(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.
|
||||
func (c *IniConfigContainer) Int(key string) (int, error) {
|
||||
key = strings.ToLower(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.
|
||||
func (c *IniConfigContainer) Int64(key string) (int64, error) {
|
||||
key = strings.ToLower(key)
|
||||
return strconv.ParseInt(c.getdata(key), 10, 64)
|
||||
}
|
||||
|
||||
// DefaultInt64 returns the int64 value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *IniConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
||||
if v, err := c.Int64(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Float returns the float value for a given key.
|
||||
func (c *IniConfigContainer) Float(key string) (float64, error) {
|
||||
key = strings.ToLower(key)
|
||||
return strconv.ParseFloat(c.getdata(key), 64)
|
||||
}
|
||||
|
||||
// DefaultFloat returns the float64 value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *IniConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
||||
if v, err := c.Float(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string value for a given key.
|
||||
func (c *IniConfigContainer) String(key string) string {
|
||||
key = strings.ToLower(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.
|
||||
func (c *IniConfigContainer) Strings(key string) []string {
|
||||
return strings.Split(c.String(key), ";")
|
||||
}
|
||||
|
||||
// DefaultStrings returns the []string value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||
if v := c.Strings(key); len(v) == 0 {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetSection returns map for the given section
|
||||
func (c *IniConfigContainer) GetSection(section string) (map[string]string, error) {
|
||||
if v, ok := c.data[section]; ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return nil, errors.New("not exist setction")
|
||||
}
|
||||
}
|
||||
|
||||
// SaveConfigFile save the config into file
|
||||
func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
|
||||
// Write configuration file by filename.
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
// Save default section at first place
|
||||
if dt, ok := c.data[DEFAULT_SECTION]; ok {
|
||||
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
|
||||
}
|
||||
}
|
||||
// Save named sections
|
||||
for section, dt := range c.data {
|
||||
if section != DEFAULT_SECTION {
|
||||
// Write section comments.
|
||||
if v, ok := c.sectionComment[section]; ok {
|
||||
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Write section name.
|
||||
if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for key, val := range dt {
|
||||
if key != " " {
|
||||
// Write key comments.
|
||||
if v, ok := c.keyComment[key]; ok {
|
||||
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Write key and value.
|
||||
if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put a line between sections.
|
||||
if _, err = buf.WriteString(lineBreak); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = buf.WriteTo(f); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteValue writes a new value for key.
|
||||
// if write to one section, the key need be "section::key".
|
||||
// if the section is not existed, it panics.
|
||||
@ -167,16 +377,19 @@ func (c *IniConfigContainer) Set(key, value string) error {
|
||||
return errors.New("key is empty")
|
||||
}
|
||||
|
||||
var section, k string
|
||||
key = strings.ToLower(key)
|
||||
sectionkey := strings.Split(key, "::")
|
||||
if len(sectionkey) >= 2 {
|
||||
section = sectionkey[0]
|
||||
k = sectionkey[1]
|
||||
var (
|
||||
section, k string
|
||||
sectionKey []string = strings.Split(key, "::")
|
||||
)
|
||||
|
||||
if len(sectionKey) >= 2 {
|
||||
section = sectionKey[0]
|
||||
k = sectionKey[1]
|
||||
} else {
|
||||
section = DEFAULT_SECTION
|
||||
k = sectionkey[0]
|
||||
k = sectionKey[0]
|
||||
}
|
||||
|
||||
if _, ok := c.data[section]; !ok {
|
||||
c.data[section] = make(map[string]string)
|
||||
}
|
||||
@ -186,8 +399,7 @@ func (c *IniConfigContainer) Set(key, value string) error {
|
||||
|
||||
// DIY returns the raw value by a given key.
|
||||
func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
|
||||
key = strings.ToLower(key)
|
||||
if v, ok := c.data[key]; ok {
|
||||
if v, ok := c.data[strings.ToLower(key)]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return v, errors.New("key not find")
|
||||
@ -195,24 +407,25 @@ func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
|
||||
|
||||
// section.key or key
|
||||
func (c *IniConfigContainer) getdata(key string) string {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
if len(key) == 0 {
|
||||
return ""
|
||||
}
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
var section, k string
|
||||
key = strings.ToLower(key)
|
||||
sectionkey := strings.Split(key, "::")
|
||||
if len(sectionkey) >= 2 {
|
||||
section = sectionkey[0]
|
||||
k = sectionkey[1]
|
||||
var (
|
||||
section, k string
|
||||
sectionKey []string = strings.Split(strings.ToLower(key), "::")
|
||||
)
|
||||
if len(sectionKey) >= 2 {
|
||||
section = sectionKey[0]
|
||||
k = sectionKey[1]
|
||||
} else {
|
||||
section = DEFAULT_SECTION
|
||||
k = sectionkey[0]
|
||||
k = sectionKey[0]
|
||||
}
|
||||
if v, ok := c.data[section]; ok {
|
||||
if vv, o := v[k]; o {
|
||||
if vv, ok := v[k]; ok {
|
||||
return vv
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
|
174
config/json.go
174
config/json.go
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
@ -26,17 +34,23 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
x := &JsonConfigContainer{
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
content, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
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 {
|
||||
var wrappingArray []interface{}
|
||||
err2 := json.Unmarshal(content, &wrappingArray)
|
||||
err2 := json.Unmarshal(data, &wrappingArray)
|
||||
if err2 != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -54,80 +68,152 @@ type JsonConfigContainer struct {
|
||||
|
||||
// Bool returns the boolean value for a given key.
|
||||
func (c *JsonConfigContainer) Bool(key string) (bool, error) {
|
||||
val := c.getdata(key)
|
||||
val := c.getData(key)
|
||||
if val != nil {
|
||||
if v, ok := val.(bool); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
}
|
||||
return false, errors.New("not bool value")
|
||||
}
|
||||
} else {
|
||||
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.
|
||||
func (c *JsonConfigContainer) Int(key string) (int, error) {
|
||||
val := c.getdata(key)
|
||||
val := c.getData(key)
|
||||
if val != nil {
|
||||
if v, ok := val.(float64); ok {
|
||||
return int(v), nil
|
||||
} else {
|
||||
}
|
||||
return 0, errors.New("not int value")
|
||||
}
|
||||
} else {
|
||||
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.
|
||||
func (c *JsonConfigContainer) Int64(key string) (int64, error) {
|
||||
val := c.getdata(key)
|
||||
val := c.getData(key)
|
||||
if val != nil {
|
||||
if v, ok := val.(float64); ok {
|
||||
return int64(v), nil
|
||||
} else {
|
||||
}
|
||||
return 0, errors.New("not int64 value")
|
||||
}
|
||||
} else {
|
||||
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.
|
||||
func (c *JsonConfigContainer) Float(key string) (float64, error) {
|
||||
val := c.getdata(key)
|
||||
val := c.getData(key)
|
||||
if val != nil {
|
||||
if v, ok := val.(float64); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
}
|
||||
return 0.0, errors.New("not float64 value")
|
||||
}
|
||||
} else {
|
||||
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.
|
||||
func (c *JsonConfigContainer) String(key string) string {
|
||||
val := c.getdata(key)
|
||||
val := c.getData(key)
|
||||
if val != nil {
|
||||
if v, ok := val.(string); ok {
|
||||
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.
|
||||
func (c *JsonConfigContainer) Strings(key string) []string {
|
||||
stringVal := c.String(key)
|
||||
if stringVal == "" {
|
||||
return []string{}
|
||||
}
|
||||
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 {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
@ -137,40 +223,40 @@ func (c *JsonConfigContainer) Set(key, val string) error {
|
||||
|
||||
// DIY returns the raw value by a given key.
|
||||
func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) {
|
||||
val := c.getdata(key)
|
||||
val := c.getData(key)
|
||||
if val != nil {
|
||||
return val, nil
|
||||
} else {
|
||||
return nil, errors.New("not exist key")
|
||||
}
|
||||
return nil, errors.New("not exist key")
|
||||
}
|
||||
|
||||
// section.key or key
|
||||
func (c *JsonConfigContainer) getdata(key string) interface{} {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
func (c *JsonConfigContainer) getData(key string) interface{} {
|
||||
if len(key) == 0 {
|
||||
return nil
|
||||
}
|
||||
sectionkey := strings.Split(key, "::")
|
||||
if len(sectionkey) >= 2 {
|
||||
cruval, ok := c.data[sectionkey[0]]
|
||||
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
sectionKeys := strings.Split(key, "::")
|
||||
if len(sectionKeys) >= 2 {
|
||||
curValue, ok := c.data[sectionKeys[0]]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
for _, key := range sectionkey[1:] {
|
||||
if v, ok := cruval.(map[string]interface{}); !ok {
|
||||
return nil
|
||||
} else if cruval, ok = v[key]; !ok {
|
||||
for _, key := range sectionKeys[1:] {
|
||||
if v, ok := curValue.(map[string]interface{}); ok {
|
||||
if curValue, ok = v[key]; !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return cruval
|
||||
} else {
|
||||
}
|
||||
return curValue
|
||||
}
|
||||
if v, ok := c.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
@ -13,6 +21,7 @@ import (
|
||||
|
||||
var jsoncontext = `{
|
||||
"appname": "beeapi",
|
||||
"testnames": "foo;bar",
|
||||
"httpport": 8080,
|
||||
"mysqlport": 3600,
|
||||
"PI": 3.1415976,
|
||||
@ -61,13 +70,13 @@ func TestJsonStartsWithArray(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rootArray, err := jsonconf.DIY("rootArray")
|
||||
if (err != nil) {
|
||||
if err != nil {
|
||||
t.Error("array does not exist as element")
|
||||
}
|
||||
rootArrayCasted := rootArray.([]interface{})
|
||||
if (rootArrayCasted == nil) {
|
||||
if rootArrayCasted == nil {
|
||||
t.Error("array from root is nil")
|
||||
}else {
|
||||
} else {
|
||||
elem := rootArrayCasted[0].(map[string]interface{})
|
||||
if elem["url"] != "user" || elem["serviceAPI"] != "http://www.test.com/user" {
|
||||
t.Error("array[0] values are not valid")
|
||||
@ -114,6 +123,12 @@ func TestJson(t *testing.T) {
|
||||
if jsonconf.String("runmode") != "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 {
|
||||
t.Error(v)
|
||||
t.Fatal(err)
|
||||
@ -171,4 +186,8 @@ func TestJson(t *testing.T) {
|
||||
if _, err := jsonconf.Bool("unknown"); err == nil {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,45 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
// package xml for config provider
|
||||
//
|
||||
// depend on github.com/beego/x2j
|
||||
//
|
||||
// go install github.com/beego/x2j
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/astaxie/beego/config/xml"
|
||||
// "github.com/astaxie/beego/config"
|
||||
// )
|
||||
//
|
||||
// cnf, err := config.NewConfig("xml", "config.xml")
|
||||
//
|
||||
// more docs http://beego.me/docs/module/config.md
|
||||
package xml
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/config"
|
||||
"github.com/beego/x2j"
|
||||
@ -21,31 +48,41 @@ import (
|
||||
// XmlConfig is a xml config parser and implements Config interface.
|
||||
// xml configurations should be included in <config></config> tag.
|
||||
// only support key/value pair as <key>value</key> as each item.
|
||||
type XMLConfig struct {
|
||||
}
|
||||
type XMLConfig struct{}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
x := &XMLConfigContainer{
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
x := &XMLConfigContainer{data: make(map[string]interface{})}
|
||||
content, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d, err := x2j.DocToMap(string(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
x.data = d["config"].(map[string]interface{})
|
||||
return x, nil
|
||||
}
|
||||
|
||||
func (x *XMLConfig) ParseData(data []byte) (config.ConfigContainer, error) {
|
||||
// Save memory data to temporary file
|
||||
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
|
||||
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
|
||||
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x.Parse(tmpName)
|
||||
}
|
||||
|
||||
// A Config represents the xml configuration.
|
||||
type XMLConfigContainer struct {
|
||||
data map[string]interface{}
|
||||
@ -57,21 +94,61 @@ func (c *XMLConfigContainer) Bool(key string) (bool, error) {
|
||||
return strconv.ParseBool(c.data[key].(string))
|
||||
}
|
||||
|
||||
// DefaultBool return the bool value if has no error
|
||||
// otherwise return the defaultval
|
||||
func (c *XMLConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
||||
if v, err := c.Bool(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Int returns the integer value for a given key.
|
||||
func (c *XMLConfigContainer) Int(key string) (int, error) {
|
||||
return strconv.Atoi(c.data[key].(string))
|
||||
}
|
||||
|
||||
// DefaultInt returns the integer value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *XMLConfigContainer) DefaultInt(key string, defaultval int) int {
|
||||
if v, err := c.Int(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Int64 returns the int64 value for a given key.
|
||||
func (c *XMLConfigContainer) Int64(key string) (int64, error) {
|
||||
return strconv.ParseInt(c.data[key].(string), 10, 64)
|
||||
}
|
||||
|
||||
// DefaultInt64 returns the int64 value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *XMLConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
||||
if v, err := c.Int64(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Float returns the float value for a given key.
|
||||
func (c *XMLConfigContainer) Float(key string) (float64, error) {
|
||||
return strconv.ParseFloat(c.data[key].(string), 64)
|
||||
}
|
||||
|
||||
// DefaultFloat returns the float64 value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *XMLConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
||||
if v, err := c.Float(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string value for a given key.
|
||||
func (c *XMLConfigContainer) String(key string) string {
|
||||
if v, ok := c.data[key].(string); ok {
|
||||
@ -80,11 +157,56 @@ func (c *XMLConfigContainer) String(key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// DefaultString returns the string value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *XMLConfigContainer) DefaultString(key string, defaultval string) string {
|
||||
if v := c.String(key); v == "" {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Strings returns the []string value for a given key.
|
||||
func (c *XMLConfigContainer) Strings(key string) []string {
|
||||
return strings.Split(c.String(key), ";")
|
||||
}
|
||||
|
||||
// DefaultStrings returns the []string value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *XMLConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||
if v := c.Strings(key); len(v) == 0 {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetSection returns map for the given section
|
||||
func (c *XMLConfigContainer) GetSection(section string) (map[string]string, error) {
|
||||
if v, ok := c.data[section]; ok {
|
||||
return v.(map[string]string), nil
|
||||
} else {
|
||||
return nil, errors.New("not exist setction")
|
||||
}
|
||||
}
|
||||
|
||||
// SaveConfigFile save the config into file
|
||||
func (c *XMLConfigContainer) SaveConfigFile(filename string) (err error) {
|
||||
// Write configuration file by filename.
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
b, err := xml.MarshalIndent(c.data, " ", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteValue writes a new value for key.
|
||||
func (c *XMLConfigContainer) Set(key, val string) error {
|
||||
c.Lock()
|
||||
|
@ -1,10 +1,18 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
package xml
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
@ -1,59 +1,92 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
// package yaml for config provider
|
||||
//
|
||||
// depend on github.com/beego/goyaml2
|
||||
//
|
||||
// go install github.com/beego/goyaml2
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/astaxie/beego/config/yaml"
|
||||
// "github.com/astaxie/beego/config"
|
||||
// )
|
||||
//
|
||||
// cnf, err := config.NewConfig("yaml", "config.yaml")
|
||||
//
|
||||
// more docs http://beego.me/docs/module/config.md
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/config"
|
||||
"github.com/beego/goyaml2"
|
||||
)
|
||||
|
||||
// YAMLConfig is a yaml config parser and implements Config interface.
|
||||
type YAMLConfig struct {
|
||||
}
|
||||
type YAMLConfig struct{}
|
||||
|
||||
// Parse returns a ConfigContainer with parsed yaml config map.
|
||||
func (yaml *YAMLConfig) Parse(filename string) (config.ConfigContainer, error) {
|
||||
y := &YAMLConfigContainer{
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
func (yaml *YAMLConfig) Parse(filename string) (y config.ConfigContainer, err error) {
|
||||
cnf, err := ReadYmlReader(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
y = &YAMLConfigContainer{
|
||||
data: cnf,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (yaml *YAMLConfig) ParseData(data []byte) (config.ConfigContainer, error) {
|
||||
// Save memory data to temporary file
|
||||
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
|
||||
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
|
||||
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
y.data = cnf
|
||||
return y, nil
|
||||
return yaml.Parse(tmpName)
|
||||
}
|
||||
|
||||
// Read yaml file to map.
|
||||
// if json like, use json package, unless goyaml2 package.
|
||||
func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
|
||||
err = nil
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
err = nil
|
||||
|
||||
buf, err := ioutil.ReadAll(f)
|
||||
if err != nil || len(buf) < 3 {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
if err == nil {
|
||||
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))
|
||||
if _err != nil {
|
||||
log.Println("Goyaml2 ERR>", string(buf), _err)
|
||||
//err = goyaml.Unmarshal(buf, &cnf)
|
||||
err = _err
|
||||
data, err := goyaml2.Read(bytes.NewBuffer(buf))
|
||||
if err != nil {
|
||||
log.Println("Goyaml2 ERR>", string(buf), err)
|
||||
return
|
||||
}
|
||||
if _map == nil {
|
||||
|
||||
if data == nil {
|
||||
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 {
|
||||
log.Println("Not a Map? >> ", string(buf), _map)
|
||||
log.Println("Not a Map? >> ", string(buf), data)
|
||||
cnf = nil
|
||||
}
|
||||
return
|
||||
@ -93,6 +126,16 @@ func (c *YAMLConfigContainer) Bool(key string) (bool, error) {
|
||||
return false, errors.New("not bool value")
|
||||
}
|
||||
|
||||
// DefaultBool return the bool value if has no error
|
||||
// otherwise return the defaultval
|
||||
func (c *YAMLConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
||||
if v, err := c.Bool(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Int returns the integer value for a given key.
|
||||
func (c *YAMLConfigContainer) Int(key string) (int, error) {
|
||||
if v, ok := c.data[key].(int64); ok {
|
||||
@ -101,6 +144,16 @@ func (c *YAMLConfigContainer) Int(key string) (int, error) {
|
||||
return 0, errors.New("not int value")
|
||||
}
|
||||
|
||||
// DefaultInt returns the integer value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *YAMLConfigContainer) DefaultInt(key string, defaultval int) int {
|
||||
if v, err := c.Int(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Int64 returns the int64 value for a given key.
|
||||
func (c *YAMLConfigContainer) Int64(key string) (int64, error) {
|
||||
if v, ok := c.data[key].(int64); ok {
|
||||
@ -109,6 +162,16 @@ func (c *YAMLConfigContainer) Int64(key string) (int64, error) {
|
||||
return 0, errors.New("not bool value")
|
||||
}
|
||||
|
||||
// DefaultInt64 returns the int64 value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *YAMLConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
||||
if v, err := c.Int64(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Float returns the float value for a given key.
|
||||
func (c *YAMLConfigContainer) Float(key string) (float64, error) {
|
||||
if v, ok := c.data[key].(float64); ok {
|
||||
@ -117,6 +180,16 @@ func (c *YAMLConfigContainer) Float(key string) (float64, error) {
|
||||
return 0.0, errors.New("not float64 value")
|
||||
}
|
||||
|
||||
// DefaultFloat returns the float64 value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *YAMLConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
||||
if v, err := c.Float(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string value for a given key.
|
||||
func (c *YAMLConfigContainer) String(key string) string {
|
||||
if v, ok := c.data[key].(string); ok {
|
||||
@ -125,11 +198,52 @@ func (c *YAMLConfigContainer) String(key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// DefaultString returns the string value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *YAMLConfigContainer) DefaultString(key string, defaultval string) string {
|
||||
if v := c.String(key); v == "" {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Strings returns the []string value for a given key.
|
||||
func (c *YAMLConfigContainer) Strings(key string) []string {
|
||||
return strings.Split(c.String(key), ";")
|
||||
}
|
||||
|
||||
// DefaultStrings returns the []string value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *YAMLConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||
if v := c.Strings(key); len(v) == 0 {
|
||||
return defaultval
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetSection returns map for the given section
|
||||
func (c *YAMLConfigContainer) GetSection(section string) (map[string]string, error) {
|
||||
if v, ok := c.data[section]; ok {
|
||||
return v.(map[string]string), nil
|
||||
} else {
|
||||
return nil, errors.New("not exist setction")
|
||||
}
|
||||
}
|
||||
|
||||
// SaveConfigFile save the config into file
|
||||
func (c *YAMLConfigContainer) SaveConfigFile(filename string) (err error) {
|
||||
// Write configuration file by filename.
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
err = goyaml2.Write(f, c.data)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteValue writes a new value for key.
|
||||
func (c *YAMLConfigContainer) Set(key, val string) error {
|
||||
c.Lock()
|
||||
|
@ -1,10 +1,18 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
|
@ -1,9 +1,24 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/astaxie/beego/context"
|
||||
//
|
||||
// ctx := context.Context{Request:req,ResponseWriter:rw}
|
||||
//
|
||||
// more docs http://beego.me/docs/module/context.md
|
||||
package context
|
||||
|
||||
import (
|
||||
@ -16,7 +31,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/middleware"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
// Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter.
|
||||
@ -26,39 +41,27 @@ type Context struct {
|
||||
Output *BeegoOutput
|
||||
Request *http.Request
|
||||
ResponseWriter http.ResponseWriter
|
||||
_xsrf_token string
|
||||
}
|
||||
|
||||
// Redirect does redirection to localurl with http header status code.
|
||||
// It sends http response header directly.
|
||||
func (ctx *Context) Redirect(status int, localurl string) {
|
||||
ctx.Output.Header("Location", localurl)
|
||||
ctx.Output.SetStatus(status)
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
|
||||
// Abort stops this request.
|
||||
// if middleware.ErrorMaps exists, panic body.
|
||||
// if middleware.HTTPExceptionMaps exists, panic HTTPException struct with status and body string.
|
||||
// if beego.ErrorMaps exists, panic body.
|
||||
func (ctx *Context) Abort(status int, body string) {
|
||||
ctx.Output.SetStatus(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
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
panic(body)
|
||||
}
|
||||
|
||||
// Write string to response body.
|
||||
// it sends response body.
|
||||
func (ctx *Context) WriteString(content string) {
|
||||
ctx.Output.Body([]byte(content))
|
||||
ctx.ResponseWriter.Write([]byte(content))
|
||||
}
|
||||
|
||||
// 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}, "|")
|
||||
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
|
||||
}
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package context
|
||||
|
||||
@ -13,13 +21,22 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/session"
|
||||
)
|
||||
|
||||
// BeegoInput operates the http request header ,data ,cookie and body.
|
||||
// Regexes for checking the accept headers
|
||||
// TODO make sure these are correct
|
||||
var (
|
||||
acceptsHtmlRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`)
|
||||
acceptsXmlRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`)
|
||||
acceptsJsonRegex = regexp.MustCompile(`(application/json)(?:,|$)`)
|
||||
)
|
||||
|
||||
// BeegoInput operates the http request header, data, cookie and body.
|
||||
// it also contains router params and current session.
|
||||
type BeegoInput struct {
|
||||
CruSession session.SessionStore
|
||||
@ -52,7 +69,7 @@ func (input *BeegoInput) Uri() string {
|
||||
|
||||
// Url returns request url path (without query string, fragment).
|
||||
func (input *BeegoInput) Url() string {
|
||||
return input.Request.URL.String()
|
||||
return input.Request.URL.Path
|
||||
}
|
||||
|
||||
// Site returns base site url as scheme://domain type.
|
||||
@ -64,11 +81,11 @@ func (input *BeegoInput) Site() string {
|
||||
func (input *BeegoInput) Scheme() string {
|
||||
if input.Request.URL.Scheme != "" {
|
||||
return input.Request.URL.Scheme
|
||||
} else if input.Request.TLS == nil {
|
||||
return "http"
|
||||
} else {
|
||||
return "https"
|
||||
}
|
||||
if input.Request.TLS == nil {
|
||||
return "http"
|
||||
}
|
||||
return "https"
|
||||
}
|
||||
|
||||
// Domain returns host name.
|
||||
@ -145,16 +162,31 @@ func (input *BeegoInput) IsSecure() bool {
|
||||
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 {
|
||||
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 {
|
||||
return strings.Contains(input.Header("Content-Type"), "multipart/form-data")
|
||||
}
|
||||
|
||||
// Checks if request accepts html response
|
||||
func (input *BeegoInput) AcceptsHtml() bool {
|
||||
return acceptsHtmlRegex.MatchString(input.Header("Accept"))
|
||||
}
|
||||
|
||||
// Checks if request accepts xml response
|
||||
func (input *BeegoInput) AcceptsXml() bool {
|
||||
return acceptsXmlRegex.MatchString(input.Header("Accept"))
|
||||
}
|
||||
|
||||
// Checks if request accepts json response
|
||||
func (input *BeegoInput) AcceptsJson() bool {
|
||||
return acceptsJsonRegex.MatchString(input.Header("Accept"))
|
||||
}
|
||||
|
||||
// IP returns request client ip.
|
||||
// if in proxy, return first proxy id.
|
||||
// if error, return 127.0.0.1.
|
||||
@ -181,16 +213,24 @@ func (input *BeegoInput) Proxy() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Referer returns http referer header.
|
||||
func (input *BeegoInput) Referer() string {
|
||||
return input.Header("Referer")
|
||||
}
|
||||
|
||||
// Refer returns http referer header.
|
||||
func (input *BeegoInput) Refer() string {
|
||||
return input.Header("Referer")
|
||||
return input.Referer()
|
||||
}
|
||||
|
||||
// SubDomains returns sub domain string.
|
||||
// if aa.bb.domain.com, returns aa.bb .
|
||||
func (input *BeegoInput) SubDomains() string {
|
||||
parts := strings.Split(input.Host(), ".")
|
||||
return strings.Join(parts[len(parts)-2:], ".")
|
||||
if len(parts) >= 3 {
|
||||
return strings.Join(parts[:len(parts)-2], ".")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Port returns request client port.
|
||||
@ -229,6 +269,7 @@ func (input *BeegoInput) Query(key string) string {
|
||||
}
|
||||
|
||||
// Header returns request header item string by a given string.
|
||||
// if non-existed, return empty string.
|
||||
func (input *BeegoInput) Header(key string) string {
|
||||
return input.Request.Header.Get(key)
|
||||
}
|
||||
@ -244,11 +285,12 @@ func (input *BeegoInput) Cookie(key string) string {
|
||||
}
|
||||
|
||||
// Session returns current session item value by a given key.
|
||||
// if non-existed, return empty string.
|
||||
func (input *BeegoInput) Session(key interface{}) interface{} {
|
||||
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 {
|
||||
requestbody, _ := ioutil.ReadAll(input.Request.Body)
|
||||
input.Request.Body.Close()
|
||||
@ -288,7 +330,7 @@ func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error {
|
||||
// Bind data from request.Form[key] to dest
|
||||
// like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie
|
||||
// var id int beegoInput.Bind(&id, "id") id ==123
|
||||
// var isok bool beegoInput.Bind(&isok, "isok") id ==true
|
||||
// var isok bool beegoInput.Bind(&isok, "isok") isok ==true
|
||||
// var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2
|
||||
// ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2]
|
||||
// ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array]
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package context
|
||||
|
||||
@ -62,3 +70,45 @@ func TestParse(t *testing.T) {
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package context
|
||||
|
||||
@ -21,6 +29,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BeegoOutput does work for sending response header.
|
||||
@ -68,6 +77,14 @@ func (output *BeegoOutput) Body(content []byte) {
|
||||
} else {
|
||||
output.Header("Content-Length", strconv.Itoa(len(content)))
|
||||
}
|
||||
|
||||
// Write status code if it has been set manually
|
||||
// Set it to 0 afterwards to prevent "multiple response.WriteHeader calls"
|
||||
if output.Status != 0 {
|
||||
output.Context.ResponseWriter.WriteHeader(output.Status)
|
||||
output.Status = 0
|
||||
}
|
||||
|
||||
output_writer.Write(content)
|
||||
switch output_writer.(type) {
|
||||
case *gzip.Writer:
|
||||
@ -82,23 +99,25 @@ func (output *BeegoOutput) Body(content []byte) {
|
||||
func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value))
|
||||
|
||||
//fix cookie not work in IE
|
||||
if len(others) > 0 {
|
||||
switch v := others[0].(type) {
|
||||
case int:
|
||||
if v > 0 {
|
||||
fmt.Fprintf(&b, "; Max-Age=%d", v)
|
||||
fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v)
|
||||
} else if v < 0 {
|
||||
fmt.Fprintf(&b, "; Max-Age=0")
|
||||
}
|
||||
case int64:
|
||||
if v > 0 {
|
||||
fmt.Fprintf(&b, "; Max-Age=%d", v)
|
||||
fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v)
|
||||
} else if v < 0 {
|
||||
fmt.Fprintf(&b, "; Max-Age=0")
|
||||
}
|
||||
case int32:
|
||||
if v > 0 {
|
||||
fmt.Fprintf(&b, "; Max-Age=%d", v)
|
||||
fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v)
|
||||
} else if v < 0 {
|
||||
fmt.Fprintf(&b, "; Max-Age=0")
|
||||
}
|
||||
@ -172,7 +191,7 @@ func sanitizeValue(v string) string {
|
||||
// Json writes json to response body.
|
||||
// if coding is true, it converts utf-8 to \u0000 type.
|
||||
func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error {
|
||||
output.Header("Content-Type", "application/json;charset=UTF-8")
|
||||
output.Header("Content-Type", "application/json; charset=utf-8")
|
||||
var content []byte
|
||||
var err error
|
||||
if hasIndent {
|
||||
@ -193,7 +212,7 @@ func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) e
|
||||
|
||||
// Jsonp writes jsonp to response body.
|
||||
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 err error
|
||||
if hasIndent {
|
||||
@ -219,7 +238,7 @@ func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
|
||||
|
||||
// Xml writes xml string to response body.
|
||||
func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error {
|
||||
output.Header("Content-Type", "application/xml;charset=UTF-8")
|
||||
output.Header("Content-Type", "application/xml; charset=utf-8")
|
||||
var content []byte
|
||||
var err error
|
||||
if hasIndent {
|
||||
@ -267,7 +286,6 @@ func (output *BeegoOutput) ContentType(ext string) {
|
||||
// SetStatus sets response status code.
|
||||
// It writes response header directly.
|
||||
func (output *BeegoOutput) SetStatus(status int) {
|
||||
output.Context.ResponseWriter.WriteHeader(status)
|
||||
output.Status = status
|
||||
}
|
||||
|
||||
|
218
controller.go
218
controller.go
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@ -22,7 +30,6 @@ import (
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/session"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
//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.
|
||||
func (c *Controller) Abort(code string) {
|
||||
status, err := strconv.Atoi(code)
|
||||
if err == nil {
|
||||
c.Ctx.Abort(status, code)
|
||||
} else {
|
||||
c.Ctx.Abort(200, code)
|
||||
if err != nil {
|
||||
status = 200
|
||||
}
|
||||
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.
|
||||
@ -277,7 +295,7 @@ func (c *Controller) StopRun() {
|
||||
|
||||
// UrlFor does another controller handler in this request function.
|
||||
// it goes to this controller method if endpoint is not clear.
|
||||
func (c *Controller) UrlFor(endpoint string, values ...string) string {
|
||||
func (c *Controller) UrlFor(endpoint string, values ...interface{}) string {
|
||||
if len(endpoint) <= 0 {
|
||||
return ""
|
||||
}
|
||||
@ -351,38 +369,128 @@ func (c *Controller) ParseForm(obj interface{}) error {
|
||||
return ParseForm(c.Input(), obj)
|
||||
}
|
||||
|
||||
// GetString returns the input value by key string.
|
||||
func (c *Controller) GetString(key string) string {
|
||||
return c.Ctx.Input.Query(key)
|
||||
// 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, def ...string) string {
|
||||
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.
|
||||
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()
|
||||
if f == nil {
|
||||
return []string{}
|
||||
return defv
|
||||
}
|
||||
|
||||
vs := f[key]
|
||||
if len(vs) > 0 {
|
||||
return vs
|
||||
} else {
|
||||
return defv
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// GetInt returns input value as int64.
|
||||
func (c *Controller) GetInt(key string) (int64, error) {
|
||||
return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64)
|
||||
// GetInt returns input as an int or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt(key string, def ...int) (int, error) {
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
return strconv.Atoi(strv)
|
||||
} else if len(def) > 0 {
|
||||
return def[0], nil
|
||||
} else {
|
||||
return strconv.Atoi(strv)
|
||||
}
|
||||
}
|
||||
|
||||
// GetBool returns input value as bool.
|
||||
func (c *Controller) GetBool(key string) (bool, error) {
|
||||
return strconv.ParseBool(c.Ctx.Input.Query(key))
|
||||
// GetInt8 return input as an int8 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt8(key string, def ...int8) (int8, error) {
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
i64, err := strconv.ParseInt(strv, 10, 8)
|
||||
i8 := int8(i64)
|
||||
return i8, err
|
||||
} else if len(def) > 0 {
|
||||
return def[0], nil
|
||||
} else {
|
||||
i64, err := strconv.ParseInt(strv, 10, 8)
|
||||
i8 := int8(i64)
|
||||
return i8, err
|
||||
}
|
||||
}
|
||||
|
||||
// GetFloat returns input value as float64.
|
||||
func (c *Controller) GetFloat(key string) (float64, error) {
|
||||
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64)
|
||||
// GetInt16 returns input as an int16 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
i64, err := strconv.ParseInt(strv, 10, 16)
|
||||
i16 := int16(i64)
|
||||
return i16, err
|
||||
} else if len(def) > 0 {
|
||||
return def[0], nil
|
||||
} else {
|
||||
i64, err := strconv.ParseInt(strv, 10, 16)
|
||||
i16 := int16(i64)
|
||||
return i16, err
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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 if len(def) > 0 {
|
||||
return def[0], nil
|
||||
} else {
|
||||
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32)
|
||||
i32 := int32(i64)
|
||||
return i32, err
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
return strconv.ParseInt(strv, 10, 64)
|
||||
} else if len(def) > 0 {
|
||||
return def[0], nil
|
||||
} else {
|
||||
return strconv.ParseInt(strv, 10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
return strconv.ParseBool(strv)
|
||||
} else if len(def) > 0 {
|
||||
return def[0], nil
|
||||
} else {
|
||||
return strconv.ParseBool(strv)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
return strconv.ParseFloat(strv, 64)
|
||||
} else if len(def) > 0 {
|
||||
return def[0], nil
|
||||
} else {
|
||||
return strconv.ParseFloat(strv, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// GetFile returns the file data in file upload field named as key.
|
||||
@ -391,6 +499,41 @@ func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader,
|
||||
return c.Ctx.Request.FormFile(key)
|
||||
}
|
||||
|
||||
// GetFiles return multi-upload files
|
||||
// files, err:=c.Getfiles("myfiles")
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusNoContent)
|
||||
// return
|
||||
// }
|
||||
// for i, _ := range files {
|
||||
// //for each fileheader, get a handle to the actual file
|
||||
// file, err := files[i].Open()
|
||||
// defer file.Close()
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// //create destination file making sure the path is writeable.
|
||||
// dst, err := os.Create("upload/" + files[i].Filename)
|
||||
// defer dst.Close()
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// //copy the uploaded file to the destination file
|
||||
// if _, err := io.Copy(dst, file); err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
func (c *Controller) GetFiles(key string) ([]*multipart.FileHeader, error) {
|
||||
files, ok := c.Ctx.Request.MultipartForm.File[key]
|
||||
if ok {
|
||||
return files, nil
|
||||
}
|
||||
return nil, http.ErrMissingFile
|
||||
}
|
||||
|
||||
// SaveToFile saves uploaded file to new path.
|
||||
// it only operates the first one of mutil-upload form file field.
|
||||
func (c *Controller) SaveToFile(fromfile, tofile string) error {
|
||||
@ -474,18 +617,13 @@ func (c *Controller) SetSecureCookie(Secret, name, value string, others ...inter
|
||||
// XsrfToken creates a xsrf token string and returns.
|
||||
func (c *Controller) XsrfToken() string {
|
||||
if c._xsrf_token == "" {
|
||||
token, ok := c.GetSecureCookie(XSRFKEY, "_xsrf")
|
||||
if !ok {
|
||||
var expire int64
|
||||
if c.XSRFExpire > 0 {
|
||||
expire = int64(c.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
|
||||
}
|
||||
@ -497,19 +635,7 @@ func (c *Controller) CheckXsrfCookie() bool {
|
||||
if !c.EnableXSRF {
|
||||
return true
|
||||
}
|
||||
token := c.GetString("_xsrf")
|
||||
if token == "" {
|
||||
token = c.Ctx.Request.Header.Get("X-Xsrftoken")
|
||||
}
|
||||
if token == "" {
|
||||
token = c.Ctx.Request.Header.Get("X-Csrftoken")
|
||||
}
|
||||
if token == "" {
|
||||
c.Ctx.Abort(403, "'_xsrf' argument missing from POST")
|
||||
} else if c._xsrf_token != token {
|
||||
c.Ctx.Abort(403, "XSRF cookie does not match POST argument")
|
||||
}
|
||||
return true
|
||||
return c.Ctx.CheckXsrfCookie()
|
||||
}
|
||||
|
||||
// XsrfFormHtml writes an input field contains xsrf token value.
|
||||
|
75
controller_test.go
Normal file
75
controller_test.go
Normal 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
14
docs.go
@ -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
|
||||
|
||||
import (
|
||||
|
490
error.go
Normal file
490
error.go
Normal 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.ResponseWriter.WriteHeader(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
|
||||
}
|
||||
if h, ok := ErrorMaps[errcode]; ok {
|
||||
executeError(h, ctx, code)
|
||||
return
|
||||
} else if h, ok := ErrorMaps["503"]; ok {
|
||||
executeError(h, ctx, code)
|
||||
return
|
||||
} else {
|
||||
ctx.ResponseWriter.WriteHeader(code)
|
||||
ctx.WriteString(errcode)
|
||||
}
|
||||
}
|
||||
|
||||
func executeError(err *errorInfo, ctx *context.Context, code int) {
|
||||
if err.errorType == errorTypeHandler {
|
||||
ctx.ResponseWriter.WriteHeader(code)
|
||||
err.handler(ctx.ResponseWriter, ctx.Request)
|
||||
return
|
||||
}
|
||||
if err.errorType == errorTypeController {
|
||||
ctx.Output.SetStatus(code)
|
||||
//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 AutoRender {
|
||||
if err := execController.Render(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// finish all runrouter. release resource
|
||||
execController.Finish()
|
||||
}
|
||||
}
|
@ -17,47 +17,47 @@ type ObjectController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (this *ObjectController) Post() {
|
||||
func (o *ObjectController) Post() {
|
||||
var ob models.Object
|
||||
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
|
||||
json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
|
||||
objectid := models.AddOne(ob)
|
||||
this.Data["json"] = map[string]string{"ObjectId": objectid}
|
||||
this.ServeJson()
|
||||
o.Data["json"] = map[string]string{"ObjectId": objectid}
|
||||
o.ServeJson()
|
||||
}
|
||||
|
||||
func (this *ObjectController) Get() {
|
||||
objectId := this.Ctx.Input.Params[":objectId"]
|
||||
func (o *ObjectController) Get() {
|
||||
objectId := o.Ctx.Input.Params[":objectId"]
|
||||
if objectId != "" {
|
||||
ob, err := models.GetOne(objectId)
|
||||
if err != nil {
|
||||
this.Data["json"] = err
|
||||
o.Data["json"] = err
|
||||
} else {
|
||||
this.Data["json"] = ob
|
||||
o.Data["json"] = ob
|
||||
}
|
||||
} else {
|
||||
obs := models.GetAll()
|
||||
this.Data["json"] = obs
|
||||
o.Data["json"] = obs
|
||||
}
|
||||
this.ServeJson()
|
||||
o.ServeJson()
|
||||
}
|
||||
|
||||
func (this *ObjectController) Put() {
|
||||
objectId := this.Ctx.Input.Params[":objectId"]
|
||||
func (o *ObjectController) Put() {
|
||||
objectId := o.Ctx.Input.Params[":objectId"]
|
||||
var ob models.Object
|
||||
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
|
||||
json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
|
||||
|
||||
err := models.Update(objectId, ob.Score)
|
||||
if err != nil {
|
||||
this.Data["json"] = err
|
||||
o.Data["json"] = err
|
||||
} else {
|
||||
this.Data["json"] = "update success!"
|
||||
o.Data["json"] = "update success!"
|
||||
}
|
||||
this.ServeJson()
|
||||
o.ServeJson()
|
||||
}
|
||||
|
||||
func (this *ObjectController) Delete() {
|
||||
objectId := this.Ctx.Input.Params[":objectId"]
|
||||
func (o *ObjectController) Delete() {
|
||||
objectId := o.Ctx.Input.Params[":objectId"]
|
||||
models.Delete(objectId)
|
||||
this.Data["json"] = "delete success!"
|
||||
this.ServeJson()
|
||||
o.Data["json"] = "delete success!"
|
||||
o.ServeJson()
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
// Beego (http://beego.me/)
|
||||
|
||||
// @description beego is an open-source, high-performance web framework for the Go programming language.
|
||||
|
||||
// @link http://github.com/astaxie/beego for the canonical source repository
|
||||
|
||||
// @license http://github.com/astaxie/beego/blob/master/LICENSE
|
||||
|
||||
// @authors astaxie
|
||||
|
||||
package main
|
||||
|
@ -14,7 +14,7 @@ type MainController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (this *MainController) Get() {
|
||||
this.Data["host"] = this.Ctx.Request.Host
|
||||
this.TplNames = "index.tpl"
|
||||
func (m *MainController) Get() {
|
||||
m.Data["host"] = m.Ctx.Request.Host
|
||||
m.TplNames = "index.tpl"
|
||||
}
|
||||
|
@ -154,10 +154,10 @@ var upgrader = websocket.Upgrader{
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
func (this *WSController) Get() {
|
||||
ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil)
|
||||
func (w *WSController) Get() {
|
||||
ws, err := upgrader.Upgrade(w.Ctx.ResponseWriter, w.Ctx.Request, nil)
|
||||
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
|
||||
} else if err != nil {
|
||||
return
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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">
|
||||
$(function() {
|
||||
|
||||
|
38
filter.go
38
filter.go
@ -1,23 +1,39 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package beego
|
||||
|
||||
// FilterRouter defines filter operation before controller handler execution.
|
||||
// it can match patterned url and do filter function when action arrives.
|
||||
import "github.com/astaxie/beego/context"
|
||||
|
||||
// FilterFunc defines a filter function which is invoked before the controller handler is executed.
|
||||
type FilterFunc func(*context.Context)
|
||||
|
||||
// FilterRouter defines a filter operation which is invoked before the controller handler is executed.
|
||||
// It can match the URL against a pattern, and execute a filter function
|
||||
// when a request with a matching URL arrives.
|
||||
type FilterRouter struct {
|
||||
filterFunc FilterFunc
|
||||
tree *Tree
|
||||
pattern string
|
||||
returnOnOutput bool
|
||||
}
|
||||
|
||||
// ValidRouter check current request is valid for this filter.
|
||||
// if matched, returns parsed params in this request by defined filter router pattern.
|
||||
func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) {
|
||||
isok, params := f.tree.Match(router)
|
||||
// ValidRouter checks if the current request is matched by this filter.
|
||||
// If the request is matched, the values of the URL parameters defined
|
||||
// by the filter pattern are also returned.
|
||||
func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) {
|
||||
isok, params := f.tree.Match(url)
|
||||
if isok == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
|
36
flash.go
36
flash.go
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@ -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.
|
||||
func (fd *FlashData) Notice(msg string, args ...interface{}) {
|
||||
if len(args) == 0 {
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@ -17,12 +25,12 @@ type TestFlashController struct {
|
||||
Controller
|
||||
}
|
||||
|
||||
func (this *TestFlashController) TestWriteFlash() {
|
||||
func (t *TestFlashController) TestWriteFlash() {
|
||||
flash := NewFlash()
|
||||
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
|
||||
this.ServeJson(true)
|
||||
t.ServeJson(true)
|
||||
}
|
||||
|
||||
func TestFlashHeader(t *testing.T) {
|
||||
|
13
grace/conn.go
Normal file
13
grace/conn.go
Normal file
@ -0,0 +1,13 @@
|
||||
package grace
|
||||
|
||||
import "net"
|
||||
|
||||
type graceConn struct {
|
||||
net.Conn
|
||||
server *graceServer
|
||||
}
|
||||
|
||||
func (c graceConn) Close() error {
|
||||
c.server.wg.Done()
|
||||
return c.Conn.Close()
|
||||
}
|
150
grace/grace.go
Normal file
150
grace/grace.go
Normal file
@ -0,0 +1,150 @@
|
||||
// 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.
|
||||
|
||||
// Description: http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// import(
|
||||
// "log"
|
||||
// "net/http"
|
||||
// "os"
|
||||
//
|
||||
// "github.com/astaxie/beego/grace"
|
||||
// )
|
||||
//
|
||||
// func handler(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Write([]byte("WORLD!"))
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// mux := http.NewServeMux()
|
||||
// mux.HandleFunc("/hello", handler)
|
||||
//
|
||||
// err := grace.ListenAndServe("localhost:8080", mux1)
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// }
|
||||
// log.Println("Server on 8080 stopped")
|
||||
// os.Exit(0)
|
||||
// }
|
||||
package grace
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
PRE_SIGNAL = iota
|
||||
POST_SIGNAL
|
||||
|
||||
STATE_INIT
|
||||
STATE_RUNNING
|
||||
STATE_SHUTTING_DOWN
|
||||
STATE_TERMINATE
|
||||
)
|
||||
|
||||
var (
|
||||
regLock *sync.Mutex
|
||||
runningServers map[string]*graceServer
|
||||
runningServersOrder []string
|
||||
socketPtrOffsetMap map[string]uint
|
||||
runningServersForked bool
|
||||
|
||||
DefaultReadTimeOut time.Duration
|
||||
DefaultWriteTimeOut time.Duration
|
||||
DefaultMaxHeaderBytes int
|
||||
DefaultTimeout time.Duration
|
||||
|
||||
isChild bool
|
||||
socketOrder string
|
||||
)
|
||||
|
||||
func init() {
|
||||
regLock = &sync.Mutex{}
|
||||
flag.BoolVar(&isChild, "graceful", false, "listen on open fd (after forking)")
|
||||
flag.StringVar(&socketOrder, "socketorder", "", "previous initialization order - used when more than one listener was started")
|
||||
runningServers = make(map[string]*graceServer)
|
||||
runningServersOrder = []string{}
|
||||
socketPtrOffsetMap = make(map[string]uint)
|
||||
|
||||
DefaultMaxHeaderBytes = 0
|
||||
|
||||
DefaultTimeout = 60 * time.Second
|
||||
}
|
||||
|
||||
// NewServer returns a new graceServer.
|
||||
func NewServer(addr string, handler http.Handler) (srv *graceServer) {
|
||||
regLock.Lock()
|
||||
defer regLock.Unlock()
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
if len(socketOrder) > 0 {
|
||||
for i, addr := range strings.Split(socketOrder, ",") {
|
||||
socketPtrOffsetMap[addr] = uint(i)
|
||||
}
|
||||
} else {
|
||||
socketPtrOffsetMap[addr] = uint(len(runningServersOrder))
|
||||
}
|
||||
|
||||
srv = &graceServer{
|
||||
wg: sync.WaitGroup{},
|
||||
sigChan: make(chan os.Signal),
|
||||
isChild: isChild,
|
||||
SignalHooks: map[int]map[os.Signal][]func(){
|
||||
PRE_SIGNAL: map[os.Signal][]func(){
|
||||
syscall.SIGHUP: []func(){},
|
||||
syscall.SIGINT: []func(){},
|
||||
syscall.SIGTERM: []func(){},
|
||||
},
|
||||
POST_SIGNAL: map[os.Signal][]func(){
|
||||
syscall.SIGHUP: []func(){},
|
||||
syscall.SIGINT: []func(){},
|
||||
syscall.SIGTERM: []func(){},
|
||||
},
|
||||
},
|
||||
state: STATE_INIT,
|
||||
Network: "tcp",
|
||||
}
|
||||
srv.Server = &http.Server{}
|
||||
srv.Server.Addr = addr
|
||||
srv.Server.ReadTimeout = DefaultReadTimeOut
|
||||
srv.Server.WriteTimeout = DefaultWriteTimeOut
|
||||
srv.Server.MaxHeaderBytes = DefaultMaxHeaderBytes
|
||||
srv.Server.Handler = handler
|
||||
|
||||
runningServersOrder = append(runningServersOrder, addr)
|
||||
runningServers[addr] = srv
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// refer http.ListenAndServe
|
||||
func ListenAndServe(addr string, handler http.Handler) error {
|
||||
server := NewServer(addr, handler)
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
||||
// refer http.ListenAndServeTLS
|
||||
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error {
|
||||
server := NewServer(addr, handler)
|
||||
return server.ListenAndServeTLS(certFile, keyFile)
|
||||
}
|
62
grace/listener.go
Normal file
62
grace/listener.go
Normal file
@ -0,0 +1,62 @@
|
||||
package grace
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type graceListener struct {
|
||||
net.Listener
|
||||
stop chan error
|
||||
stopped bool
|
||||
server *graceServer
|
||||
}
|
||||
|
||||
func newGraceListener(l net.Listener, srv *graceServer) (el *graceListener) {
|
||||
el = &graceListener{
|
||||
Listener: l,
|
||||
stop: make(chan error),
|
||||
server: srv,
|
||||
}
|
||||
go func() {
|
||||
_ = <-el.stop
|
||||
el.stopped = true
|
||||
el.stop <- el.Listener.Close()
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (gl *graceListener) Accept() (c net.Conn, err error) {
|
||||
tc, err := gl.Listener.(*net.TCPListener).AcceptTCP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
|
||||
c = graceConn{
|
||||
Conn: tc,
|
||||
server: gl.server,
|
||||
}
|
||||
|
||||
gl.server.wg.Add(1)
|
||||
return
|
||||
}
|
||||
|
||||
func (el *graceListener) Close() error {
|
||||
if el.stopped {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
el.stop <- nil
|
||||
return <-el.stop
|
||||
}
|
||||
|
||||
func (el *graceListener) File() *os.File {
|
||||
// returns a dup(2) - FD_CLOEXEC flag *not* set
|
||||
tl := el.Listener.(*net.TCPListener)
|
||||
fl, _ := tl.File()
|
||||
return fl
|
||||
}
|
292
grace/server.go
Normal file
292
grace/server.go
Normal file
@ -0,0 +1,292 @@
|
||||
package grace
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type graceServer struct {
|
||||
*http.Server
|
||||
GraceListener net.Listener
|
||||
SignalHooks map[int]map[os.Signal][]func()
|
||||
tlsInnerListener *graceListener
|
||||
wg sync.WaitGroup
|
||||
sigChan chan os.Signal
|
||||
isChild bool
|
||||
state uint8
|
||||
Network string
|
||||
}
|
||||
|
||||
// Serve accepts incoming connections on the Listener l,
|
||||
// creating a new service goroutine for each.
|
||||
// The service goroutines read requests and then call srv.Handler to reply to them.
|
||||
func (srv *graceServer) Serve() (err error) {
|
||||
srv.state = STATE_RUNNING
|
||||
err = srv.Server.Serve(srv.GraceListener)
|
||||
log.Println(syscall.Getpid(), "Waiting for connections to finish...")
|
||||
srv.wg.Wait()
|
||||
srv.state = STATE_TERMINATE
|
||||
return
|
||||
}
|
||||
|
||||
// ListenAndServe listens on the TCP network address srv.Addr and then calls Serve
|
||||
// to handle requests on incoming connections. If srv.Addr is blank, ":http" is
|
||||
// used.
|
||||
func (srv *graceServer) ListenAndServe() (err error) {
|
||||
addr := srv.Addr
|
||||
if addr == "" {
|
||||
addr = ":http"
|
||||
}
|
||||
|
||||
go srv.handleSignals()
|
||||
|
||||
l, err := srv.getListener(addr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
srv.GraceListener = newGraceListener(l, srv)
|
||||
|
||||
if srv.isChild {
|
||||
process, err := os.FindProcess(os.Getppid())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
err = process.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println(os.Getpid(), srv.Addr)
|
||||
return srv.Serve()
|
||||
}
|
||||
|
||||
// ListenAndServeTLS listens on the TCP network address srv.Addr and then calls
|
||||
// Serve to handle requests on incoming TLS connections.
|
||||
//
|
||||
// Filenames containing a certificate and matching private key for the server must
|
||||
// be provided. If the certificate is signed by a certificate authority, the
|
||||
// certFile should be the concatenation of the server's certificate followed by the
|
||||
// CA's certificate.
|
||||
//
|
||||
// If srv.Addr is blank, ":https" is used.
|
||||
func (srv *graceServer) ListenAndServeTLS(certFile, keyFile string) (err error) {
|
||||
addr := srv.Addr
|
||||
if addr == "" {
|
||||
addr = ":https"
|
||||
}
|
||||
|
||||
config := &tls.Config{}
|
||||
if srv.TLSConfig != nil {
|
||||
*config = *srv.TLSConfig
|
||||
}
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
go srv.handleSignals()
|
||||
|
||||
l, err := srv.getListener(addr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
srv.tlsInnerListener = newGraceListener(l, srv)
|
||||
srv.GraceListener = tls.NewListener(srv.tlsInnerListener, config)
|
||||
|
||||
if srv.isChild {
|
||||
process, err := os.FindProcess(os.Getppid())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
err = process.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Println(os.Getpid(), srv.Addr)
|
||||
return srv.Serve()
|
||||
}
|
||||
|
||||
// getListener either opens a new socket to listen on, or takes the acceptor socket
|
||||
// it got passed when restarted.
|
||||
func (srv *graceServer) getListener(laddr string) (l net.Listener, err error) {
|
||||
if srv.isChild {
|
||||
var ptrOffset uint = 0
|
||||
if len(socketPtrOffsetMap) > 0 {
|
||||
ptrOffset = socketPtrOffsetMap[laddr]
|
||||
log.Println("laddr", laddr, "ptr offset", socketPtrOffsetMap[laddr])
|
||||
}
|
||||
|
||||
f := os.NewFile(uintptr(3+ptrOffset), "")
|
||||
l, err = net.FileListener(f)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("net.FileListener error: %v", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
l, err = net.Listen(srv.Network, laddr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("net.Listen error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// handleSignals listens for os Signals and calls any hooked in function that the
|
||||
// user had registered with the signal.
|
||||
func (srv *graceServer) handleSignals() {
|
||||
var sig os.Signal
|
||||
|
||||
signal.Notify(
|
||||
srv.sigChan,
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
)
|
||||
|
||||
pid := syscall.Getpid()
|
||||
for {
|
||||
sig = <-srv.sigChan
|
||||
srv.signalHooks(PRE_SIGNAL, sig)
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
log.Println(pid, "Received SIGHUP. forking.")
|
||||
err := srv.fork()
|
||||
if err != nil {
|
||||
log.Println("Fork err:", err)
|
||||
}
|
||||
case syscall.SIGINT:
|
||||
log.Println(pid, "Received SIGINT.")
|
||||
srv.shutdown()
|
||||
case syscall.SIGTERM:
|
||||
log.Println(pid, "Received SIGTERM.")
|
||||
srv.shutdown()
|
||||
default:
|
||||
log.Printf("Received %v: nothing i care about...\n", sig)
|
||||
}
|
||||
srv.signalHooks(POST_SIGNAL, sig)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *graceServer) signalHooks(ppFlag int, sig os.Signal) {
|
||||
if _, notSet := srv.SignalHooks[ppFlag][sig]; !notSet {
|
||||
return
|
||||
}
|
||||
for _, f := range srv.SignalHooks[ppFlag][sig] {
|
||||
f()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// shutdown closes the listener so that no new connections are accepted. it also
|
||||
// starts a goroutine that will serverTimeout (stop all running requests) the server
|
||||
// after DefaultTimeout.
|
||||
func (srv *graceServer) shutdown() {
|
||||
if srv.state != STATE_RUNNING {
|
||||
return
|
||||
}
|
||||
|
||||
srv.state = STATE_SHUTTING_DOWN
|
||||
if DefaultTimeout >= 0 {
|
||||
go srv.serverTimeout(DefaultTimeout)
|
||||
}
|
||||
err := srv.GraceListener.Close()
|
||||
if err != nil {
|
||||
log.Println(syscall.Getpid(), "Listener.Close() error:", err)
|
||||
} else {
|
||||
log.Println(syscall.Getpid(), srv.GraceListener.Addr(), "Listener closed.")
|
||||
}
|
||||
}
|
||||
|
||||
// serverTimeout forces the server to shutdown in a given timeout - whether it
|
||||
// finished outstanding requests or not. if Read/WriteTimeout are not set or the
|
||||
// max header size is very big a connection could hang
|
||||
func (srv *graceServer) serverTimeout(d time.Duration) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Println("WaitGroup at 0", r)
|
||||
}
|
||||
}()
|
||||
if srv.state != STATE_SHUTTING_DOWN {
|
||||
return
|
||||
}
|
||||
time.Sleep(d)
|
||||
log.Println("[STOP - Hammer Time] Forcefully shutting down parent")
|
||||
for {
|
||||
if srv.state == STATE_TERMINATE {
|
||||
break
|
||||
}
|
||||
srv.wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *graceServer) fork() (err error) {
|
||||
regLock.Lock()
|
||||
defer regLock.Unlock()
|
||||
if runningServersForked {
|
||||
return
|
||||
}
|
||||
runningServersForked = true
|
||||
|
||||
var files = make([]*os.File, len(runningServers))
|
||||
var orderArgs = make([]string, len(runningServers))
|
||||
for _, srvPtr := range runningServers {
|
||||
switch srvPtr.GraceListener.(type) {
|
||||
case *graceListener:
|
||||
files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.GraceListener.(*graceListener).File()
|
||||
default:
|
||||
files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.tlsInnerListener.File()
|
||||
}
|
||||
orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr
|
||||
}
|
||||
|
||||
log.Println(files)
|
||||
path := os.Args[0]
|
||||
var args []string
|
||||
if len(os.Args) > 1 {
|
||||
for _, arg := range os.Args[1:] {
|
||||
if arg == "-graceful" {
|
||||
break
|
||||
}
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
args = append(args, "-graceful")
|
||||
if len(runningServers) > 1 {
|
||||
args = append(args, fmt.Sprintf(`-socketorder=%s`, strings.Join(orderArgs, ",")))
|
||||
log.Println(args)
|
||||
}
|
||||
cmd := exec.Command(path, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.ExtraFiles = files
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("Restart: Failed to launch, error: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -6,53 +6,71 @@ httplib is an libs help you to curl remote url.
|
||||
## GET
|
||||
you can use Get to crawl data.
|
||||
|
||||
import "httplib"
|
||||
import "github.com/astaxie/beego/httplib"
|
||||
|
||||
str, err := httplib.Get("http://beego.me/").String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// error
|
||||
}
|
||||
fmt.Println(str)
|
||||
|
||||
## POST
|
||||
POST data to remote url
|
||||
|
||||
b:=httplib.Post("http://beego.me/")
|
||||
b.Param("username","astaxie")
|
||||
b.Param("password","123456")
|
||||
str, err := b.String()
|
||||
req := httplib.Post("http://beego.me/")
|
||||
req.Param("username","astaxie")
|
||||
req.Param("password","123456")
|
||||
str, err := req.String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// error
|
||||
}
|
||||
fmt.Println(str)
|
||||
|
||||
## set timeout
|
||||
you can set timeout in request.default is 60 seconds.
|
||||
## Set timeout
|
||||
|
||||
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)
|
||||
|
||||
set post timeout:
|
||||
|
||||
// POST
|
||||
httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
|
||||
|
||||
- first param is connectTimeout.
|
||||
- second param is readWriteTimeout
|
||||
|
||||
## debug
|
||||
if you want to debug the request info, set the debug on
|
||||
## Debug
|
||||
|
||||
If you want to debug the request info, set the debug on
|
||||
|
||||
httplib.Get("http://beego.me/").Debug(true)
|
||||
|
||||
## support HTTPS client
|
||||
if request url is https. You can set the client support TSL:
|
||||
## Set HTTP Basic Auth
|
||||
|
||||
str, err := Get("http://beego.me/").SetBasicAuth("user", "passwd").String()
|
||||
if err != nil {
|
||||
// error
|
||||
}
|
||||
fmt.Println(str)
|
||||
|
||||
## Set HTTPS
|
||||
|
||||
If request url is https, You can set the client support TSL:
|
||||
|
||||
httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
|
||||
more info about the tls.Config please visit http://golang.org/pkg/crypto/tls/#Config
|
||||
More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config
|
||||
|
||||
## Set HTTP Version
|
||||
|
||||
some servers need to specify the protocol version of HTTP
|
||||
|
||||
httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1")
|
||||
|
||||
## Set Cookie
|
||||
|
||||
## set cookie
|
||||
some http request need setcookie. So set it like this:
|
||||
|
||||
cookie := &http.Cookie{}
|
||||
@ -60,21 +78,20 @@ some http request need setcookie. So set it like this:
|
||||
cookie.Value = "astaxie"
|
||||
httplib.Get("http://beego.me/").SetCookie(cookie)
|
||||
|
||||
## upload file
|
||||
httplib support mutil file upload, use `b.PostFile()`
|
||||
## Upload file
|
||||
|
||||
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()
|
||||
httplib support mutil file upload, use `req.PostFile()`
|
||||
|
||||
req := httplib.Post("http://beego.me/")
|
||||
req.Param("username","astaxie")
|
||||
req.PostFile("uploadfile1", "httplib.pdf")
|
||||
str, err := req.String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// error
|
||||
}
|
||||
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)
|
||||
|
@ -1,18 +1,44 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/astaxie/beego/httplib"
|
||||
//
|
||||
// b := httplib.Post("http://beego.me/")
|
||||
// b.Param("username","astaxie")
|
||||
// b.Param("password","123456")
|
||||
// b.PostFile("uploadfile1", "httplib.pdf")
|
||||
// b.PostFile("uploadfile2", "httplib.txt")
|
||||
// str, err := b.String()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// fmt.Println(str)
|
||||
//
|
||||
// more docs http://beego.me/docs/module/httplib.md
|
||||
package httplib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -25,11 +51,18 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false}
|
||||
var defaultSetting = BeegoHttpSettings{
|
||||
UserAgent: "beegoServer",
|
||||
ConnectTimeout: 60 * time.Second,
|
||||
ReadWriteTimeout: 60 * time.Second,
|
||||
Gzip: true,
|
||||
DumpBody: true,
|
||||
}
|
||||
|
||||
var defaultCookieJar http.CookieJar
|
||||
var settingMutex sync.Mutex
|
||||
|
||||
// createDefaultCookieJar creates a global cookiejar to store cookies.
|
||||
// createDefaultCookie creates a global cookiejar to store cookies.
|
||||
func createDefaultCookie() {
|
||||
settingMutex.Lock()
|
||||
defer settingMutex.Unlock()
|
||||
@ -49,44 +82,54 @@ func SetDefaultSetting(setting BeegoHttpSettings) {
|
||||
}
|
||||
}
|
||||
|
||||
// return *BeegoHttpRequest with specific method
|
||||
func NewBeegoRequest(rawurl, method string) *BeegoHttpRequest {
|
||||
var resp http.Response
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
req := http.Request{
|
||||
URL: u,
|
||||
Method: method,
|
||||
Header: make(http.Header),
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
}
|
||||
return &BeegoHttpRequest{
|
||||
url: rawurl,
|
||||
req: &req,
|
||||
params: map[string]string{},
|
||||
files: map[string]string{},
|
||||
setting: defaultSetting,
|
||||
resp: &resp,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns *BeegoHttpRequest with GET method.
|
||||
func Get(url string) *BeegoHttpRequest {
|
||||
var req http.Request
|
||||
req.Method = "GET"
|
||||
req.Header = http.Header{}
|
||||
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
|
||||
return NewBeegoRequest(url, "GET")
|
||||
}
|
||||
|
||||
// Post returns *BeegoHttpRequest with POST method.
|
||||
func Post(url string) *BeegoHttpRequest {
|
||||
var req http.Request
|
||||
req.Method = "POST"
|
||||
req.Header = http.Header{}
|
||||
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
|
||||
return NewBeegoRequest(url, "POST")
|
||||
}
|
||||
|
||||
// Put returns *BeegoHttpRequest with PUT method.
|
||||
func Put(url string) *BeegoHttpRequest {
|
||||
var req http.Request
|
||||
req.Method = "PUT"
|
||||
req.Header = http.Header{}
|
||||
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
|
||||
return NewBeegoRequest(url, "PUT")
|
||||
}
|
||||
|
||||
// Delete returns *BeegoHttpRequest DELETE GET method.
|
||||
// Delete returns *BeegoHttpRequest DELETE method.
|
||||
func Delete(url string) *BeegoHttpRequest {
|
||||
var req http.Request
|
||||
req.Method = "DELETE"
|
||||
req.Header = http.Header{}
|
||||
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
|
||||
return NewBeegoRequest(url, "DELETE")
|
||||
}
|
||||
|
||||
// Head returns *BeegoHttpRequest with HEAD method.
|
||||
func Head(url string) *BeegoHttpRequest {
|
||||
var req http.Request
|
||||
req.Method = "HEAD"
|
||||
req.Header = http.Header{}
|
||||
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
|
||||
return NewBeegoRequest(url, "HEAD")
|
||||
}
|
||||
|
||||
// BeegoHttpSettings
|
||||
@ -99,6 +142,8 @@ type BeegoHttpSettings struct {
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
Transport http.RoundTripper
|
||||
EnableCookie bool
|
||||
Gzip bool
|
||||
DumpBody bool
|
||||
}
|
||||
|
||||
// BeegoHttpRequest provides more useful methods for requesting one url than http.Request.
|
||||
@ -108,6 +153,14 @@ type BeegoHttpRequest struct {
|
||||
params map[string]string
|
||||
files map[string]string
|
||||
setting BeegoHttpSettings
|
||||
resp *http.Response
|
||||
body []byte
|
||||
dump []byte
|
||||
}
|
||||
|
||||
// get request
|
||||
func (b *BeegoHttpRequest) GetRequest() *http.Request {
|
||||
return b.req
|
||||
}
|
||||
|
||||
// Change request settings
|
||||
@ -116,6 +169,12 @@ func (b *BeegoHttpRequest) Setting(setting BeegoHttpSettings) *BeegoHttpRequest
|
||||
return b
|
||||
}
|
||||
|
||||
// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
|
||||
func (b *BeegoHttpRequest) SetBasicAuth(username, password string) *BeegoHttpRequest {
|
||||
b.req.SetBasicAuth(username, password)
|
||||
return b
|
||||
}
|
||||
|
||||
// SetEnableCookie sets enable/disable cookiejar
|
||||
func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest {
|
||||
b.setting.EnableCookie = enable
|
||||
@ -123,7 +182,7 @@ func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest {
|
||||
}
|
||||
|
||||
// SetUserAgent sets User-Agent header field
|
||||
func (b *BeegoHttpRequest) SetAgent(useragent string) *BeegoHttpRequest {
|
||||
func (b *BeegoHttpRequest) SetUserAgent(useragent string) *BeegoHttpRequest {
|
||||
b.setting.UserAgent = useragent
|
||||
return b
|
||||
}
|
||||
@ -134,6 +193,17 @@ func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
// Dump Body.
|
||||
func (b *BeegoHttpRequest) DumpBody(isdump bool) *BeegoHttpRequest {
|
||||
b.setting.DumpBody = isdump
|
||||
return b
|
||||
}
|
||||
|
||||
// return the DumpRequest
|
||||
func (b *BeegoHttpRequest) DumpRequest() []byte {
|
||||
return b.dump
|
||||
}
|
||||
|
||||
// SetTimeout sets connect time out and read-write time out for BeegoRequest.
|
||||
func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest {
|
||||
b.setting.ConnectTimeout = connectTimeout
|
||||
@ -153,6 +223,12 @@ func (b *BeegoHttpRequest) Header(key, value string) *BeegoHttpRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
// Set HOST
|
||||
func (b *BeegoHttpRequest) SetHost(host string) *BeegoHttpRequest {
|
||||
b.req.Host = host
|
||||
return b
|
||||
}
|
||||
|
||||
// Set the protocol version for incoming requests.
|
||||
// Client requests always use HTTP/1.1.
|
||||
func (b *BeegoHttpRequest) SetProtocolVersion(vers string) *BeegoHttpRequest {
|
||||
@ -222,7 +298,87 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
// JsonBody adds request raw body encoding by JSON.
|
||||
func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) {
|
||||
if b.req.Body == nil && obj != nil {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
enc := json.NewEncoder(buf)
|
||||
if err := enc.Encode(obj); err != nil {
|
||||
return b, err
|
||||
}
|
||||
b.req.Body = ioutil.NopCloser(buf)
|
||||
b.req.ContentLength = int64(buf.Len())
|
||||
b.req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
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/PUT/PATCH url and body
|
||||
if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && 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) {
|
||||
if b.resp.StatusCode != 0 {
|
||||
return b.resp, nil
|
||||
}
|
||||
resp, err := b.SendOut()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.resp = resp
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *BeegoHttpRequest) SendOut() (*http.Response, error) {
|
||||
var paramBody string
|
||||
if len(b.params) > 0 {
|
||||
var buf bytes.Buffer
|
||||
@ -236,63 +392,13 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
paramBody = paramBody[0 : len(paramBody)-1]
|
||||
}
|
||||
|
||||
if b.req.Method == "GET" && len(paramBody) > 0 {
|
||||
if strings.Index(b.url, "?") != -1 {
|
||||
b.url += "&" + paramBody
|
||||
} else {
|
||||
b.url = b.url + "?" + paramBody
|
||||
}
|
||||
} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
|
||||
if len(b.files) > 0 {
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
for formname, filename := range b.files {
|
||||
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for k, v := range b.params {
|
||||
bodyWriter.WriteField(k, v)
|
||||
}
|
||||
contentType := bodyWriter.FormDataContentType()
|
||||
bodyWriter.Close()
|
||||
b.Header("Content-Type", contentType)
|
||||
b.req.Body = ioutil.NopCloser(bodyBuf)
|
||||
b.req.ContentLength = int64(bodyBuf.Len())
|
||||
} else {
|
||||
b.Header("Content-Type", "application/x-www-form-urlencoded")
|
||||
b.Body(paramBody)
|
||||
}
|
||||
}
|
||||
|
||||
b.buildUrl(paramBody)
|
||||
url, err := url.Parse(b.url)
|
||||
if url.Scheme == "" {
|
||||
b.url = "http://" + b.url
|
||||
url, err = url.Parse(b.url)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -318,14 +424,12 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var jar http.CookieJar
|
||||
var jar http.CookieJar = nil
|
||||
if b.setting.EnableCookie {
|
||||
if defaultCookieJar == nil {
|
||||
createDefaultCookie()
|
||||
}
|
||||
jar = defaultCookieJar
|
||||
} else {
|
||||
jar = nil
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
@ -333,15 +437,18 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
resp, err := client.Do(b.req)
|
||||
if b.setting.ShowDebug {
|
||||
dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Println(err.Error())
|
||||
}
|
||||
return resp, nil
|
||||
b.dump = dump
|
||||
}
|
||||
return client.Do(b.req)
|
||||
}
|
||||
|
||||
// String returns the body string in response.
|
||||
@ -358,6 +465,9 @@ func (b *BeegoHttpRequest) String() (string, error) {
|
||||
// Bytes returns the body []byte in response.
|
||||
// it calls Response inner.
|
||||
func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
|
||||
if b.body != nil {
|
||||
return b.body, nil
|
||||
}
|
||||
resp, err := b.getResponse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -366,11 +476,16 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
|
||||
reader, err := gzip.NewReader(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
b.body, err = ioutil.ReadAll(reader)
|
||||
} else {
|
||||
b.body, err = ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
return b.body, err
|
||||
}
|
||||
|
||||
// ToFile saves the body data in response to one file.
|
||||
@ -391,10 +506,7 @@ func (b *BeegoHttpRequest) ToFile(filename string) error {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToJson returns the map that marshals from the body bytes as json in response .
|
||||
@ -404,25 +516,17 @@ func (b *BeegoHttpRequest) ToJson(v interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(data, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// ToXml returns the map that marshals from the body bytes as xml in response .
|
||||
// it calls Response inner.
|
||||
func (b *BeegoHttpRequest) ToXML(v interface{}) error {
|
||||
func (b *BeegoHttpRequest) ToXml(v interface{}) error {
|
||||
data, err := b.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = xml.Unmarshal(data, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return xml.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// Response executes request client gets response mannually.
|
||||
@ -437,7 +541,7 @@ func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, ad
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn.SetDeadline(time.Now().Add(rwTimeout))
|
||||
return conn, nil
|
||||
err = conn.SetDeadline(time.Now().Add(rwTimeout))
|
||||
return conn, err
|
||||
}
|
||||
}
|
||||
|
@ -1,100 +1,216 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package httplib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetUrl(t *testing.T) {
|
||||
resp, err := Get("http://beego.me").Debug(true).Response()
|
||||
func TestResponse(t *testing.T) {
|
||||
req := Get("http://httpbin.org/get")
|
||||
resp, err := req.Response()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.Body == nil {
|
||||
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")
|
||||
}
|
||||
t.Log(resp)
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(str) == 0 {
|
||||
t.Fatal("has no info")
|
||||
t.Log(b)
|
||||
|
||||
s, err := req.String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(s)
|
||||
|
||||
if string(b) != s {
|
||||
t.Fatal("request data not match")
|
||||
}
|
||||
}
|
||||
|
||||
func ExamplePost(t *testing.T) {
|
||||
b := Post("http://beego.me/").Debug(true)
|
||||
b.Param("username", "astaxie")
|
||||
b.Param("password", "hello")
|
||||
b.PostFile("uploadfile", "httplib_test.go")
|
||||
str, err := b.String()
|
||||
func TestSimplePost(t *testing.T) {
|
||||
v := "smallfish"
|
||||
req := Post("http://httpbin.org/post")
|
||||
req.Param("username", v)
|
||||
|
||||
str, err := req.String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
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) {
|
||||
fmt.Println("TestSimpleGetString==========================================")
|
||||
html, err := Get("http://httpbin.org/headers").SetAgent("beegoooooo").String()
|
||||
//func TestPostFile(t *testing.T) {
|
||||
// v := "smallfish"
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(html)
|
||||
fmt.Println("TestSimpleGetString==========================================")
|
||||
t.Log(str)
|
||||
}
|
||||
|
||||
func TestSimpleGetStringWithDefaultCookie(t *testing.T) {
|
||||
fmt.Println("TestSimpleGetStringWithDefaultCookie==========================================")
|
||||
html, err := Get("http://httpbin.org/cookies/set?k1=v1").SetEnableCookie(true).String()
|
||||
func TestSimpleDelete(t *testing.T) {
|
||||
str, err := Delete("http://httpbin.org/delete").String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(html)
|
||||
html, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(html)
|
||||
fmt.Println("TestSimpleGetStringWithDefaultCookie==========================================")
|
||||
t.Log(str)
|
||||
}
|
||||
|
||||
func TestDefaultSetting(t *testing.T) {
|
||||
fmt.Println("TestDefaultSetting==========================================")
|
||||
var def BeegoHttpSettings
|
||||
def.EnableCookie = true
|
||||
//def.ShowDebug = true
|
||||
def.UserAgent = "UserAgent"
|
||||
//def.ConnectTimeout = 60*time.Second
|
||||
//def.ReadWriteTimeout = 60*time.Second
|
||||
def.Transport = nil //http.DefaultTransport
|
||||
SetDefaultSetting(def)
|
||||
func TestWithCookie(t *testing.T) {
|
||||
v := "smallfish"
|
||||
str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(str)
|
||||
|
||||
html, err := Get("http://httpbin.org/headers").String()
|
||||
str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(html)
|
||||
html, err = Get("http://httpbin.org/headers").String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Log(str)
|
||||
|
||||
n := strings.Index(str, v)
|
||||
if n == -1 {
|
||||
t.Fatal(v + " not found in cookie")
|
||||
}
|
||||
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
95
log.go
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@ -14,12 +22,14 @@ import (
|
||||
|
||||
// Log levels to control the logging output.
|
||||
const (
|
||||
LevelTrace = iota
|
||||
LevelDebug
|
||||
LevelInfo
|
||||
LevelWarning
|
||||
LevelError
|
||||
LevelEmergency = iota
|
||||
LevelAlert
|
||||
LevelCritical
|
||||
LevelError
|
||||
LevelWarning
|
||||
LevelNotice
|
||||
LevelInformational
|
||||
LevelDebug
|
||||
)
|
||||
|
||||
// SetLogLevel sets the global log level used by the simple
|
||||
@ -45,29 +55,12 @@ func SetLogger(adaptername string, config string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Trace logs a message at trace level.
|
||||
func Trace(v ...interface{}) {
|
||||
BeeLogger.Trace(generateFmtStr(len(v)), v...)
|
||||
func Emergency(v ...interface{}) {
|
||||
BeeLogger.Emergency(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Debug logs a message at debug level.
|
||||
func Debug(v ...interface{}) {
|
||||
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...)
|
||||
func Alert(v ...interface{}) {
|
||||
BeeLogger.Alert(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Critical logs a message at critical level.
|
||||
@ -75,6 +68,46 @@ func Critical(v ...interface{}) {
|
||||
BeeLogger.Critical(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Error logs a message at error level.
|
||||
func Error(v ...interface{}) {
|
||||
BeeLogger.Error(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Warning logs a message at warning level.
|
||||
func Warning(v ...interface{}) {
|
||||
BeeLogger.Warning(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// compatibility alias for Warning()
|
||||
func Warn(v ...interface{}) {
|
||||
BeeLogger.Warn(generateFmtStr(len(v)), 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...)
|
||||
}
|
||||
|
||||
// compatibility alias for Warning()
|
||||
func Info(v ...interface{}) {
|
||||
BeeLogger.Info(generateFmtStr(len(v)), 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.
|
||||
// compatibility alias for Warning()
|
||||
func Trace(v ...interface{}) {
|
||||
BeeLogger.Trace(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
func generateFmtStr(n int) string {
|
||||
return strings.Repeat("%v ", n)
|
||||
}
|
||||
|
31
logs/conn.go
31
logs/conn.go
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
@ -35,17 +43,13 @@ func NewConn() LoggerInterface {
|
||||
// init connection writer with json config.
|
||||
// json config only need key "level".
|
||||
func (c *ConnWriter) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal([]byte(jsonconfig), c)
|
||||
}
|
||||
|
||||
// write message in connection.
|
||||
// if connection is down, try to re-connect.
|
||||
func (c *ConnWriter) WriteMsg(msg string, level int) error {
|
||||
if level < c.Level {
|
||||
if level > c.Level {
|
||||
return nil
|
||||
}
|
||||
if c.neddedConnectOnMsg() {
|
||||
@ -69,10 +73,9 @@ func (c *ConnWriter) Flush() {
|
||||
|
||||
// destroy connection writer and close tcp listener.
|
||||
func (c *ConnWriter) Destroy() {
|
||||
if c.innerWriter == nil {
|
||||
return
|
||||
}
|
||||
if c.innerWriter != nil {
|
||||
c.innerWriter.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConnWriter) connect() error {
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
@ -13,5 +21,5 @@ import (
|
||||
func TestConn(t *testing.T) {
|
||||
log := NewLogger(1000)
|
||||
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
||||
log.Info("info")
|
||||
log.Informational("informational")
|
||||
}
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
@ -24,12 +32,14 @@ func NewBrush(color string) Brush {
|
||||
}
|
||||
|
||||
var colors = []Brush{
|
||||
NewBrush("1;36"), // Trace cyan
|
||||
NewBrush("1;34"), // Debug blue
|
||||
NewBrush("1;32"), // Info green
|
||||
NewBrush("1;33"), // Warn yellow
|
||||
NewBrush("1;37"), // Emergency white
|
||||
NewBrush("1;36"), // Alert cyan
|
||||
NewBrush("1;35"), // Critical magenta
|
||||
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.
|
||||
@ -40,9 +50,10 @@ type ConsoleWriter struct {
|
||||
|
||||
// create ConsoleWriter returning as LoggerInterface.
|
||||
func NewConsole() LoggerInterface {
|
||||
cw := new(ConsoleWriter)
|
||||
cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
||||
cw.Level = LevelTrace
|
||||
cw := &ConsoleWriter{
|
||||
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
Level: LevelDebug,
|
||||
}
|
||||
return cw
|
||||
}
|
||||
|
||||
@ -52,23 +63,20 @@ func (c *ConsoleWriter) Init(jsonconfig string) error {
|
||||
if len(jsonconfig) == 0 {
|
||||
return nil
|
||||
}
|
||||
err := json.Unmarshal([]byte(jsonconfig), c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal([]byte(jsonconfig), c)
|
||||
}
|
||||
|
||||
// write message in console.
|
||||
func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
|
||||
if level < c.Level {
|
||||
if level > c.Level {
|
||||
return nil
|
||||
}
|
||||
if goos := runtime.GOOS; goos == "windows" {
|
||||
c.lg.Println(msg)
|
||||
} else {
|
||||
c.lg.Println(colors[level](msg))
|
||||
return nil
|
||||
}
|
||||
c.lg.Println(colors[level](msg))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
@ -10,22 +18,29 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Try each log level in decreasing order of priority.
|
||||
func testConsoleCalls(bl *BeeLogger) {
|
||||
bl.Emergency("emergency")
|
||||
bl.Alert("alert")
|
||||
bl.Critical("critical")
|
||||
bl.Error("error")
|
||||
bl.Warning("warning")
|
||||
bl.Notice("notice")
|
||||
bl.Informational("informational")
|
||||
bl.Debug("debug")
|
||||
}
|
||||
|
||||
// Test console logging by visually comparing the lines being output with and
|
||||
// without a log level specification.
|
||||
func TestConsole(t *testing.T) {
|
||||
log := NewLogger(10000)
|
||||
log.EnableFuncCallDepth(true)
|
||||
log.SetLogger("console", "")
|
||||
log.Trace("trace")
|
||||
log.Info("info")
|
||||
log.Warn("warning")
|
||||
log.Debug("debug")
|
||||
log.Critical("critical")
|
||||
log1 := NewLogger(10000)
|
||||
log1.EnableFuncCallDepth(true)
|
||||
log1.SetLogger("console", "")
|
||||
testConsoleCalls(log1)
|
||||
|
||||
log2 := NewLogger(100)
|
||||
log2.SetLogger("console", `{"level":1}`)
|
||||
log.Trace("trace")
|
||||
log.Info("info")
|
||||
log.Warn("warning")
|
||||
log.Debug("debug")
|
||||
log.Critical("critical")
|
||||
log2.SetLogger("console", `{"level":3}`)
|
||||
testConsoleCalls(log2)
|
||||
}
|
||||
|
||||
func BenchmarkConsole(b *testing.B) {
|
||||
@ -33,6 +48,6 @@ func BenchmarkConsole(b *testing.B) {
|
||||
log.EnableFuncCallDepth(true)
|
||||
log.SetLogger("console", "")
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Trace("trace")
|
||||
log.Debug("debug")
|
||||
}
|
||||
}
|
||||
|
76
logs/es/es.go
Normal file
76
logs/es/es.go
Normal file
@ -0,0 +1,76 @@
|
||||
package es
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/belogik/goes"
|
||||
)
|
||||
|
||||
func NewES() logs.LoggerInterface {
|
||||
cw := &esLogger{
|
||||
Level: logs.LevelDebug,
|
||||
}
|
||||
return cw
|
||||
}
|
||||
|
||||
type esLogger struct {
|
||||
*goes.Connection
|
||||
DSN string `json:"dsn"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// {"dsn":"http://localhost:9200/","level":1}
|
||||
func (el *esLogger) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), el)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if el.DSN == "" {
|
||||
return errors.New("empty dsn")
|
||||
} else if u, err := url.Parse(el.DSN); err != nil {
|
||||
return err
|
||||
} else if u.Path == "" {
|
||||
return errors.New("missing prefix")
|
||||
} else if host, port, err := net.SplitHostPort(u.Host); err != nil {
|
||||
return err
|
||||
} else {
|
||||
conn := goes.NewConnection(host, port)
|
||||
el.Connection = conn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (el *esLogger) WriteMsg(msg string, level int) error {
|
||||
if level > el.Level {
|
||||
return nil
|
||||
}
|
||||
t := time.Now()
|
||||
vals := make(map[string]interface{})
|
||||
vals["@timestamp"] = t.Format(time.RFC3339)
|
||||
vals["@msg"] = msg
|
||||
d := goes.Document{
|
||||
Index: fmt.Sprintf("%04d.%02d.%02d", t.Year(), t.Month(), t.Day()),
|
||||
Type: "logs",
|
||||
Fields: vals,
|
||||
}
|
||||
_, err := el.Index(d, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (el *esLogger) Destroy() {
|
||||
|
||||
}
|
||||
|
||||
func (el *esLogger) Flush() {
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
logs.Register("es", NewES)
|
||||
}
|
74
logs/file.go
74
logs/file.go
@ -1,16 +1,25 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -114,11 +123,7 @@ func (w *FileLogWriter) startLogger() error {
|
||||
return err
|
||||
}
|
||||
w.mw.SetFd(fd)
|
||||
err = w.initFd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return w.initFd()
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) docheck(size int) {
|
||||
@ -138,7 +143,7 @@ func (w *FileLogWriter) docheck(size int) {
|
||||
|
||||
// write logger message into file.
|
||||
func (w *FileLogWriter) WriteMsg(msg string, level int) error {
|
||||
if level < w.Level {
|
||||
if level > w.Level {
|
||||
return nil
|
||||
}
|
||||
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.daily_opendate = time.Now().Day()
|
||||
w.maxlines_curlines = 0
|
||||
if finfo.Size() > 0 {
|
||||
content, err := ioutil.ReadFile(w.Filename)
|
||||
count, err := w.lines()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.maxlines_curlines = len(strings.Split(string(content), "\n"))
|
||||
} else {
|
||||
w.maxlines_curlines = 0
|
||||
w.maxlines_curlines = count
|
||||
}
|
||||
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.
|
||||
// new file name like xx.log.2013-01-01.2
|
||||
func (w *FileLogWriter) DoRotate() error {
|
||||
@ -218,13 +249,20 @@ func (w *FileLogWriter) DoRotate() error {
|
||||
|
||||
func (w *FileLogWriter) deleteOldLog() {
|
||||
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 strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
@ -10,6 +18,7 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -17,12 +26,14 @@ import (
|
||||
func TestFile(t *testing.T) {
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("file", `{"filename":"test.log"}`)
|
||||
log.Trace("test")
|
||||
log.Info("info")
|
||||
log.Debug("debug")
|
||||
log.Warn("warning")
|
||||
log.Informational("info")
|
||||
log.Notice("notice")
|
||||
log.Warning("warning")
|
||||
log.Error("error")
|
||||
log.Alert("alert")
|
||||
log.Critical("critical")
|
||||
log.Emergency("emergency")
|
||||
time.Sleep(time.Second * 4)
|
||||
f, err := os.Open("test.log")
|
||||
if err != nil {
|
||||
@ -39,21 +50,24 @@ func TestFile(t *testing.T) {
|
||||
linenum++
|
||||
}
|
||||
}
|
||||
if linenum != 6 {
|
||||
t.Fatal(linenum, "not line 6")
|
||||
var expected = LevelDebug + 1
|
||||
if linenum != expected {
|
||||
t.Fatal(linenum, "not "+strconv.Itoa(expected)+" lines")
|
||||
}
|
||||
os.Remove("test.log")
|
||||
}
|
||||
|
||||
func TestFile2(t *testing.T) {
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("file", `{"filename":"test2.log","level":2}`)
|
||||
log.Trace("test")
|
||||
log.Info("info")
|
||||
log.SetLogger("file", fmt.Sprintf(`{"filename":"test2.log","level":%d}`, LevelError))
|
||||
log.Debug("debug")
|
||||
log.Warn("warning")
|
||||
log.Info("info")
|
||||
log.Notice("notice")
|
||||
log.Warning("warning")
|
||||
log.Error("error")
|
||||
log.Alert("alert")
|
||||
log.Critical("critical")
|
||||
log.Emergency("emergency")
|
||||
time.Sleep(time.Second * 4)
|
||||
f, err := os.Open("test2.log")
|
||||
if err != nil {
|
||||
@ -70,8 +84,9 @@ func TestFile2(t *testing.T) {
|
||||
linenum++
|
||||
}
|
||||
}
|
||||
if linenum != 4 {
|
||||
t.Fatal(linenum, "not line 4")
|
||||
var expected = LevelError + 1
|
||||
if linenum != expected {
|
||||
t.Fatal(linenum, "not "+strconv.Itoa(expected)+" lines")
|
||||
}
|
||||
os.Remove("test2.log")
|
||||
}
|
||||
@ -79,17 +94,19 @@ func TestFile2(t *testing.T) {
|
||||
func TestFileRotate(t *testing.T) {
|
||||
log := NewLogger(10000)
|
||||
log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`)
|
||||
log.Trace("test")
|
||||
log.Info("info")
|
||||
log.Debug("debug")
|
||||
log.Warn("warning")
|
||||
log.Info("info")
|
||||
log.Notice("notice")
|
||||
log.Warning("warning")
|
||||
log.Error("error")
|
||||
log.Alert("alert")
|
||||
log.Critical("critical")
|
||||
log.Emergency("emergency")
|
||||
time.Sleep(time.Second * 4)
|
||||
rotatename := "test3.log" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1)
|
||||
b, err := exists(rotatename)
|
||||
if !b || err != nil {
|
||||
t.Fatal("rotate not gen")
|
||||
t.Fatal("rotate not generated")
|
||||
}
|
||||
os.Remove(rotatename)
|
||||
os.Remove("test3.log")
|
||||
@ -110,7 +127,7 @@ func BenchmarkFile(b *testing.B) {
|
||||
log := NewLogger(100000)
|
||||
log.SetLogger("file", `{"filename":"test4.log"}`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Trace("trace")
|
||||
log.Debug("debug")
|
||||
}
|
||||
os.Remove("test4.log")
|
||||
}
|
||||
|
227
logs/log.go
227
logs/log.go
@ -1,9 +1,35 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/astaxie/beego/logs"
|
||||
//
|
||||
// log := NewLogger(10000)
|
||||
// log.SetLogger("console", "")
|
||||
//
|
||||
// > the first params stand for how many channel
|
||||
//
|
||||
// Use it like this:
|
||||
//
|
||||
// log.Trace("trace")
|
||||
// log.Info("info")
|
||||
// log.Warn("warning")
|
||||
// log.Debug("debug")
|
||||
// log.Critical("critical")
|
||||
//
|
||||
// more docs http://beego.me/docs/module/logs.md
|
||||
package logs
|
||||
|
||||
import (
|
||||
@ -13,14 +39,25 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// RFC5424 log message levels.
|
||||
const (
|
||||
// log message levels
|
||||
LevelTrace = iota
|
||||
LevelDebug
|
||||
LevelInfo
|
||||
LevelWarn
|
||||
LevelError
|
||||
LevelEmergency = iota
|
||||
LevelAlert
|
||||
LevelCritical
|
||||
LevelError
|
||||
LevelWarning
|
||||
LevelNotice
|
||||
LevelInformational
|
||||
LevelDebug
|
||||
)
|
||||
|
||||
// Legacy loglevel constants to ensure backwards compatibility.
|
||||
//
|
||||
// Deprecated: will be removed in 1.5.0.
|
||||
const (
|
||||
LevelInfo = LevelInformational
|
||||
LevelTrace = LevelDebug
|
||||
LevelWarn = LevelWarning
|
||||
)
|
||||
|
||||
type loggerType func() LoggerInterface
|
||||
@ -55,6 +92,7 @@ type BeeLogger struct {
|
||||
level int
|
||||
enableFuncCallDepth bool
|
||||
loggerFuncCallDepth int
|
||||
asynchronous bool
|
||||
msg chan *logMsg
|
||||
outputs map[string]LoggerInterface
|
||||
}
|
||||
@ -69,10 +107,15 @@ type logMsg struct {
|
||||
// if the buffering chan is full, logger adapters write to file or other way.
|
||||
func NewLogger(channellen int64) *BeeLogger {
|
||||
bl := new(BeeLogger)
|
||||
bl.level = LevelDebug
|
||||
bl.loggerFuncCallDepth = 2
|
||||
bl.msg = make(chan *logMsg, channellen)
|
||||
bl.outputs = make(map[string]LoggerInterface)
|
||||
//bl.SetLogger("console", "") // default output to console
|
||||
return bl
|
||||
}
|
||||
|
||||
func (bl *BeeLogger) Async() *BeeLogger {
|
||||
bl.asynchronous = true
|
||||
go bl.startLogger()
|
||||
return bl
|
||||
}
|
||||
@ -110,28 +153,37 @@ func (bl *BeeLogger) DelLogger(adaptername string) error {
|
||||
}
|
||||
|
||||
func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
|
||||
if bl.level > loglevel {
|
||||
return nil
|
||||
}
|
||||
lm := new(logMsg)
|
||||
lm.level = loglevel
|
||||
if bl.enableFuncCallDepth {
|
||||
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
|
||||
if ok {
|
||||
if !ok {
|
||||
file = "???"
|
||||
line = 0
|
||||
}
|
||||
_, filename := path.Split(file)
|
||||
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
|
||||
} else {
|
||||
lm.msg = msg
|
||||
}
|
||||
} else {
|
||||
lm.msg = msg
|
||||
}
|
||||
if bl.asynchronous {
|
||||
bl.msg <- lm
|
||||
} else {
|
||||
for name, l := range bl.outputs {
|
||||
err := l.WriteMsg(lm.msg, lm.level)
|
||||
if err != nil {
|
||||
fmt.Println("unable to WriteMsg to adapter:", name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// set log message level.
|
||||
// if message level (such as LevelTrace) is less than logger level (such as LevelWarn), ignore message.
|
||||
// Set log message level.
|
||||
//
|
||||
// If message level (such as LevelDebug) is higher than logger level (such as LevelWarning),
|
||||
// log providers will not even be sent the message.
|
||||
func (bl *BeeLogger) SetLevel(l int) {
|
||||
bl.level = l
|
||||
}
|
||||
@ -141,58 +193,132 @@ func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
|
||||
bl.loggerFuncCallDepth = d
|
||||
}
|
||||
|
||||
// get log funcCallDepth for wrapper
|
||||
func (bl *BeeLogger) GetLogFuncCallDepth() int {
|
||||
return bl.loggerFuncCallDepth
|
||||
}
|
||||
|
||||
// enable log funcCallDepth
|
||||
func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
|
||||
bl.enableFuncCallDepth = b
|
||||
}
|
||||
|
||||
// start logger chan reading.
|
||||
// when chan is full, write logs.
|
||||
// when chan is not empty, write logs.
|
||||
func (bl *BeeLogger) startLogger() {
|
||||
for {
|
||||
select {
|
||||
case bm := <-bl.msg:
|
||||
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.
|
||||
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[T] "+format, v...)
|
||||
bl.writerMsg(LevelTrace, msg)
|
||||
// Log EMERGENCY level message.
|
||||
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
|
||||
if LevelEmergency > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[M] "+format, v...)
|
||||
bl.writerMsg(LevelEmergency, msg)
|
||||
}
|
||||
|
||||
// log debug level message.
|
||||
func (bl *BeeLogger) Debug(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[D] "+format, v...)
|
||||
bl.writerMsg(LevelDebug, msg)
|
||||
// Log ALERT level message.
|
||||
func (bl *BeeLogger) Alert(format string, v ...interface{}) {
|
||||
if LevelAlert > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[A] "+format, v...)
|
||||
bl.writerMsg(LevelAlert, msg)
|
||||
}
|
||||
|
||||
// log info level message.
|
||||
func (bl *BeeLogger) Info(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[I] "+format, v...)
|
||||
bl.writerMsg(LevelInfo, msg)
|
||||
// Log CRITICAL level message.
|
||||
func (bl *BeeLogger) Critical(format string, v ...interface{}) {
|
||||
if LevelCritical > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[C] "+format, v...)
|
||||
bl.writerMsg(LevelCritical, msg)
|
||||
}
|
||||
|
||||
// log warn level message.
|
||||
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[W] "+format, v...)
|
||||
bl.writerMsg(LevelWarn, msg)
|
||||
}
|
||||
|
||||
// log error level message.
|
||||
// Log ERROR level message.
|
||||
func (bl *BeeLogger) Error(format string, v ...interface{}) {
|
||||
if LevelError > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[E] "+format, v...)
|
||||
bl.writerMsg(LevelError, msg)
|
||||
}
|
||||
|
||||
// log critical level message.
|
||||
func (bl *BeeLogger) Critical(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[C] "+format, v...)
|
||||
bl.writerMsg(LevelCritical, msg)
|
||||
// Log WARNING level message.
|
||||
func (bl *BeeLogger) Warning(format string, v ...interface{}) {
|
||||
if LevelWarning > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[W] "+format, v...)
|
||||
bl.writerMsg(LevelWarning, msg)
|
||||
}
|
||||
|
||||
// Log NOTICE level message.
|
||||
func (bl *BeeLogger) Notice(format string, v ...interface{}) {
|
||||
if LevelNotice > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[N] "+format, v...)
|
||||
bl.writerMsg(LevelNotice, msg)
|
||||
}
|
||||
|
||||
// Log INFORMATIONAL level message.
|
||||
func (bl *BeeLogger) Informational(format string, v ...interface{}) {
|
||||
if LevelInformational > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[I] "+format, v...)
|
||||
bl.writerMsg(LevelInformational, msg)
|
||||
}
|
||||
|
||||
// Log DEBUG level message.
|
||||
func (bl *BeeLogger) Debug(format string, v ...interface{}) {
|
||||
if LevelDebug > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[D] "+format, v...)
|
||||
bl.writerMsg(LevelDebug, msg)
|
||||
}
|
||||
|
||||
// Log WARN level message.
|
||||
// compatibility alias for Warning()
|
||||
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
|
||||
if LevelWarning > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[W] "+format, v...)
|
||||
bl.writerMsg(LevelWarning, msg)
|
||||
}
|
||||
|
||||
// Log INFO level message.
|
||||
// compatibility alias for Informational()
|
||||
func (bl *BeeLogger) Info(format string, v ...interface{}) {
|
||||
if LevelInformational > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[I] "+format, v...)
|
||||
bl.writerMsg(LevelInformational, msg)
|
||||
}
|
||||
|
||||
// Log TRACE level message.
|
||||
// compatibility alias for Debug()
|
||||
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
|
||||
if LevelDebug > bl.level {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("[D] "+format, v...)
|
||||
bl.writerMsg(LevelDebug, msg)
|
||||
}
|
||||
|
||||
// flush all chan data.
|
||||
@ -208,12 +334,15 @@ func (bl *BeeLogger) Close() {
|
||||
if len(bl.msg) > 0 {
|
||||
bm := <-bl.msg
|
||||
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)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, l := range bl.outputs {
|
||||
l.Flush()
|
||||
l.Destroy()
|
||||
|
112
logs/smtp.go
112
logs/smtp.go
@ -1,21 +1,32 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
subjectPhrase = "Diagnostic message from server"
|
||||
// no usage
|
||||
// subjectPhrase = "Diagnostic message from 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"`
|
||||
Host string `json:"Host"`
|
||||
Subject string `json:"subject"`
|
||||
FromAddress string `json:"fromAddress"`
|
||||
RecipientAddresses []string `json:"sendTos"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
@ -40,6 +52,7 @@ func NewSmtpWriter() LoggerInterface {
|
||||
// "password:"password",
|
||||
// "host":"smtp.gmail.com:465",
|
||||
// "subject":"email title",
|
||||
// "fromAddress":"from@example.com",
|
||||
// "sendTos":["email1","email2"],
|
||||
// "level":LevelError
|
||||
// }
|
||||
@ -51,37 +64,90 @@ func (s *SmtpWriter) Init(jsonconfig string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SmtpWriter) GetSmtpAuth(host string) smtp.Auth {
|
||||
if len(strings.Trim(s.Username, " ")) == 0 && len(strings.Trim(s.Password, " ")) == 0 {
|
||||
return nil
|
||||
}
|
||||
return smtp.PlainAuth(
|
||||
"",
|
||||
s.Username,
|
||||
s.Password,
|
||||
host,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SmtpWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error {
|
||||
client, err := smtp.Dial(hostAddressWithPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, _, _ := net.SplitHostPort(hostAddressWithPort)
|
||||
tlsConn := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: host,
|
||||
}
|
||||
if err = client.StartTLS(tlsConn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if auth != nil {
|
||||
if err = client.Auth(auth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = client.Mail(fromAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rec := range recipients {
|
||||
if err = client.Rcpt(rec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write([]byte(msgContent))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.Quit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// write message in smtp writer.
|
||||
// it will send an email with subject and only this message.
|
||||
func (s *SmtpWriter) WriteMsg(msg string, level int) error {
|
||||
if level < s.Level {
|
||||
if level > s.Level {
|
||||
return nil
|
||||
}
|
||||
|
||||
hp := strings.Split(s.Host, ":")
|
||||
|
||||
// Set up authentication information.
|
||||
auth := smtp.PlainAuth(
|
||||
"",
|
||||
s.Username,
|
||||
s.Password,
|
||||
hp[0],
|
||||
)
|
||||
auth := s.GetSmtpAuth(hp[0])
|
||||
|
||||
// Connect to the server, authenticate, set the sender and recipient,
|
||||
// and send the email all in one step.
|
||||
content_type := "Content-Type: text/plain" + "; charset=UTF-8"
|
||||
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.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)
|
||||
|
||||
err := smtp.SendMail(
|
||||
s.Host,
|
||||
auth,
|
||||
s.Username,
|
||||
s.RecipientAddresses,
|
||||
mailmsg,
|
||||
)
|
||||
|
||||
return err
|
||||
return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
|
||||
}
|
||||
|
||||
// implementing method. empty.
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logs
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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"}
|
||||
}
|
@ -1,90 +1,71 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/astaxie/beego/middleware"
|
||||
//
|
||||
// I18N = middleware.NewLocale("conf/i18n.conf", beego.AppConfig.String("language"))
|
||||
//
|
||||
// more docs: http://beego.me/docs/module/i18n.md
|
||||
package middleware
|
||||
|
||||
//import (
|
||||
// "github.com/astaxie/beego/config"
|
||||
// "os"
|
||||
// "path"
|
||||
//)
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
//type Translation struct {
|
||||
// filetype string
|
||||
// CurrentLocal string
|
||||
// Locales map[string]map[string]string
|
||||
//}
|
||||
type Translation struct {
|
||||
filepath string
|
||||
CurrentLocal string
|
||||
Locales map[string]map[string]string
|
||||
}
|
||||
|
||||
//func NewLocale(filetype string) *Translation {
|
||||
// return &Translation{
|
||||
// filetype: filetype,
|
||||
// CurrentLocal: "zh",
|
||||
// Locales: make(map[string]map[string]string),
|
||||
// }
|
||||
//}
|
||||
func NewLocale(filepath string, defaultlocal string) *Translation {
|
||||
file, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
panic("open " + filepath + " err :" + err.Error())
|
||||
}
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
panic("read " + filepath + " err :" + err.Error())
|
||||
}
|
||||
|
||||
//func (t *Translation) loadTranslations(dirPath string) error {
|
||||
// dir, err := os.Open(dirPath)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer dir.Close()
|
||||
i18n := make(map[string]map[string]string)
|
||||
if err = json.Unmarshal(data, &i18n); err != nil {
|
||||
panic("json.Unmarshal " + filepath + " err :" + err.Error())
|
||||
}
|
||||
return &Translation{
|
||||
filepath: filepath,
|
||||
CurrentLocal: defaultlocal,
|
||||
Locales: i18n,
|
||||
}
|
||||
}
|
||||
|
||||
// names, err := dir.Readdirnames(-1)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
func (t *Translation) SetLocale(local string) {
|
||||
t.CurrentLocal = local
|
||||
}
|
||||
|
||||
// for _, name := range names {
|
||||
// fullPath := path.Join(dirPath, name)
|
||||
|
||||
// fi, err := os.Stat(fullPath)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if fi.IsDir() {
|
||||
// continue
|
||||
// } 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
|
||||
//}
|
||||
func (t *Translation) Translate(key string, local string) string {
|
||||
if local == "" {
|
||||
local = t.CurrentLocal
|
||||
}
|
||||
if ct, ok := t.Locales[key]; ok {
|
||||
if v, o := ct[local]; o {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
46
migration/ddl.go
Normal file
46
migration/ddl.go
Normal 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
280
migration/migration.go
Normal 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
|
||||
}
|
19
mime.go
19
mime.go
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@ -32,6 +40,7 @@ var mimemaps map[string]string = map[string]string{
|
||||
".ani": "application/x-navi-animation",
|
||||
".aos": "application/x-nokia-9000-communicator-add-on-software",
|
||||
".aps": "application/mime",
|
||||
".apk": "application/vnd.android.package-archive",
|
||||
".arc": "application/x-arc-compressed",
|
||||
".arj": "application/arj",
|
||||
".art": "image/x-jg",
|
||||
|
27
namespace.go
27
namespace.go
@ -1,15 +1,24 @@
|
||||
// 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
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package beego
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
beecontext "github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/middleware"
|
||||
)
|
||||
|
||||
type namespaceCond func(*beecontext.Context) bool
|
||||
@ -47,7 +56,7 @@ func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
|
||||
func (n *Namespace) Cond(cond namespaceCond) *Namespace {
|
||||
fn := func(ctx *beecontext.Context) {
|
||||
if !cond(ctx) {
|
||||
middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed")
|
||||
exception("405", ctx)
|
||||
}
|
||||
}
|
||||
if v, ok := n.handlers.filters[BeforeRouter]; ok {
|
||||
@ -207,7 +216,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
|
||||
n.handlers.routers[k] = t
|
||||
}
|
||||
}
|
||||
if n.handlers.enableFilter {
|
||||
if ni.handlers.enableFilter {
|
||||
for pos, filterList := range ni.handlers.filters {
|
||||
for _, mr := range filterList {
|
||||
t := NewTree()
|
||||
@ -258,9 +267,11 @@ func addPrefix(t *Tree, prefix string) {
|
||||
}
|
||||
for _, l := range t.leaves {
|
||||
if c, ok := l.runObject.(*controllerInfo); ok {
|
||||
if !strings.HasPrefix(c.pattern, prefix) {
|
||||
c.pattern = prefix + c.pattern
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
|
@ -6,8 +6,6 @@ A powerful orm framework for go.
|
||||
|
||||
It is heavily influenced by Django ORM, SQLAlchemy.
|
||||
|
||||
now, beta, unstable, may be changing some api make your app build failed.
|
||||
|
||||
**Support Database:**
|
||||
|
||||
* MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
|
||||
@ -119,7 +117,7 @@ o.Begin()
|
||||
...
|
||||
user := User{Name: "slene"}
|
||||
id, err := o.Insert(&user)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
o.Commit()
|
||||
} else {
|
||||
o.Rollback()
|
||||
@ -154,10 +152,5 @@ note: not recommend use this in product env.
|
||||
|
||||
more details and examples in docs and test
|
||||
|
||||
* [中文](http://beego.me/docs/Models_Overview?lang=zh)
|
||||
* [English](http://beego.me/docs/Models_Overview?lang=en)
|
||||
[documents](http://beego.me/docs/mvc/model/overview.md)
|
||||
|
||||
## TODO
|
||||
- some unrealized api
|
||||
- examples
|
||||
- docs
|
||||
|
18
orm/cmd.go
18
orm/cmd.go
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
@ -96,7 +104,11 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string {
|
||||
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.
|
||||
@ -144,6 +156,13 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
||||
column += " " + "NOT NULL"
|
||||
}
|
||||
|
||||
//if fi.initial.String() != "" {
|
||||
// column += " DEFAULT " + fi.initial.String()
|
||||
//}
|
||||
|
||||
// Append attribute DEFAULT
|
||||
column += getColumnDefault(fi)
|
||||
|
||||
if fi.unique {
|
||||
column += " " + "UNIQUE"
|
||||
}
|
||||
@ -227,3 +246,44 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
||||
|
||||
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
|
||||
}
|
||||
|
134
orm/db.go
134
orm/db.go
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
@ -36,6 +44,8 @@ var (
|
||||
"gte": true,
|
||||
"lt": true,
|
||||
"lte": true,
|
||||
"eq": true,
|
||||
"nq": true,
|
||||
"startswith": true,
|
||||
"endswith": true,
|
||||
"istartswith": true,
|
||||
@ -114,6 +124,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
|
||||
if nb.Valid {
|
||||
value = nb.Bool
|
||||
}
|
||||
} else if field.Kind() == reflect.Ptr {
|
||||
if field.IsNil() {
|
||||
value = nil
|
||||
} else {
|
||||
value = field.Elem().Bool()
|
||||
}
|
||||
} else {
|
||||
value = field.Bool()
|
||||
}
|
||||
@ -123,6 +139,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
|
||||
if ns.Valid {
|
||||
value = ns.String
|
||||
}
|
||||
} else if field.Kind() == reflect.Ptr {
|
||||
if field.IsNil() {
|
||||
value = nil
|
||||
} else {
|
||||
value = field.Elem().String()
|
||||
}
|
||||
} else {
|
||||
value = field.String()
|
||||
}
|
||||
@ -132,6 +154,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
|
||||
if nf.Valid {
|
||||
value = nf.Float64
|
||||
}
|
||||
} else if field.Kind() == reflect.Ptr {
|
||||
if field.IsNil() {
|
||||
value = nil
|
||||
} else {
|
||||
value = field.Elem().Float()
|
||||
}
|
||||
} else {
|
||||
vu := field.Interface()
|
||||
if _, ok := vu.(float32); ok {
|
||||
@ -153,13 +181,27 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
|
||||
default:
|
||||
switch {
|
||||
case fi.fieldType&IsPostiveIntegerField > 0:
|
||||
if field.Kind() == reflect.Ptr {
|
||||
if field.IsNil() {
|
||||
value = nil
|
||||
} else {
|
||||
value = field.Elem().Uint()
|
||||
}
|
||||
} else {
|
||||
value = field.Uint()
|
||||
}
|
||||
case fi.fieldType&IsIntegerField > 0:
|
||||
if ni, ok := field.Interface().(sql.NullInt64); ok {
|
||||
value = nil
|
||||
if ni.Valid {
|
||||
value = ni.Int64
|
||||
}
|
||||
} else if field.Kind() == reflect.Ptr {
|
||||
if field.IsNil() {
|
||||
value = nil
|
||||
} else {
|
||||
value = field.Elem().Int()
|
||||
}
|
||||
} else {
|
||||
value = field.Int()
|
||||
}
|
||||
@ -284,7 +326,7 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo
|
||||
query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ?", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q)
|
||||
|
||||
refs := make([]interface{}, colsNum)
|
||||
for i, _ := range refs {
|
||||
for i := range refs {
|
||||
var ref interface{}
|
||||
refs[i] = &ref
|
||||
}
|
||||
@ -383,7 +425,7 @@ func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []s
|
||||
Q := d.ins.TableQuote()
|
||||
|
||||
marks := make([]string, len(names))
|
||||
for i, _ := range marks {
|
||||
for i := range marks {
|
||||
marks[i] = "?"
|
||||
}
|
||||
|
||||
@ -653,7 +695,7 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
|
||||
}
|
||||
|
||||
marks := make([]string, len(args))
|
||||
for i, _ := range marks {
|
||||
for i := range marks {
|
||||
marks[i] = "?"
|
||||
}
|
||||
sql := fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))
|
||||
@ -784,7 +826,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
|
||||
}
|
||||
|
||||
refs := make([]interface{}, colsNum)
|
||||
for i, _ := range refs {
|
||||
for i := range refs {
|
||||
var ref interface{}
|
||||
refs[i] = &ref
|
||||
}
|
||||
@ -924,7 +966,7 @@ func (d *dbBase) GenerateOperatorSql(mi *modelInfo, fi *fieldInfo, operator stri
|
||||
switch operator {
|
||||
case "in":
|
||||
marks := make([]string, len(params))
|
||||
for i, _ := range marks {
|
||||
for i := range marks {
|
||||
marks[i] = "?"
|
||||
}
|
||||
sql = fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))
|
||||
@ -1169,6 +1211,11 @@ setValue:
|
||||
nb.Valid = true
|
||||
}
|
||||
field.Set(reflect.ValueOf(nb))
|
||||
} else if field.Kind() == reflect.Ptr {
|
||||
if value != nil {
|
||||
v := value.(bool)
|
||||
field.Set(reflect.ValueOf(&v))
|
||||
}
|
||||
} else {
|
||||
if value == nil {
|
||||
value = false
|
||||
@ -1186,6 +1233,11 @@ setValue:
|
||||
ns.Valid = true
|
||||
}
|
||||
field.Set(reflect.ValueOf(ns))
|
||||
} else if field.Kind() == reflect.Ptr {
|
||||
if value != nil {
|
||||
v := value.(string)
|
||||
field.Set(reflect.ValueOf(&v))
|
||||
}
|
||||
} else {
|
||||
if value == nil {
|
||||
value = ""
|
||||
@ -1200,6 +1252,56 @@ setValue:
|
||||
}
|
||||
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:
|
||||
if fieldType&IsPostiveIntegerField > 0 {
|
||||
if isNative {
|
||||
@ -1236,6 +1338,16 @@ setValue:
|
||||
nf.Valid = true
|
||||
}
|
||||
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 {
|
||||
|
||||
if value == nil {
|
||||
@ -1350,7 +1462,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond
|
||||
}
|
||||
|
||||
refs := make([]interface{}, len(cols))
|
||||
for i, _ := range refs {
|
||||
for i := range refs {
|
||||
var ref interface{}
|
||||
refs[i] = &ref
|
||||
}
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
@ -136,7 +144,7 @@ func detectTZ(al *alias) {
|
||||
if engine != "" {
|
||||
al.Engine = engine
|
||||
} else {
|
||||
engine = "INNODB"
|
||||
al.Engine = "INNODB"
|
||||
}
|
||||
|
||||
case DR_Sqlite:
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
@ -22,6 +30,8 @@ var mysqlOperators = map[string]string{
|
||||
"gte": ">= ?",
|
||||
"lt": "< ?",
|
||||
"lte": "<= ?",
|
||||
"eq": "= ?",
|
||||
"ne": "!= ?",
|
||||
"startswith": "LIKE BINARY ?",
|
||||
"endswith": "LIKE BINARY ?",
|
||||
"istartswith": "LIKE ?",
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
@ -21,6 +29,8 @@ var postgresOperators = map[string]string{
|
||||
"gte": ">= ?",
|
||||
"lt": "< ?",
|
||||
"lte": "<= ?",
|
||||
"eq": "= ?",
|
||||
"ne": "!= ?",
|
||||
"startswith": "LIKE ?",
|
||||
"endswith": "LIKE ?",
|
||||
"istartswith": "LIKE UPPER(?)",
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
@ -21,6 +29,8 @@ var sqliteOperators = map[string]string{
|
||||
"gte": ">= ?",
|
||||
"lt": "< ?",
|
||||
"lte": "<= ?",
|
||||
"eq": "= ?",
|
||||
"ne": "!= ?",
|
||||
"startswith": "LIKE ? ESCAPE '\\'",
|
||||
"endswith": "LIKE ? ESCAPE '\\'",
|
||||
"istartswith": "LIKE ? ESCAPE '\\'",
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
|
@ -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 的数据库
|
@ -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
|
@ -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
|
@ -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;
|
@ -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 的值
|
@ -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,等
|
@ -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 name,value 的值 以 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, ", ")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
@ -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#模型字段与数据库类型的对应) 推荐的数据库对应使用的类型
|
@ -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) 可以进行判断
|
@ -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
|
||||
```
|
@ -1,17 +0,0 @@
|
||||
## 事务处理
|
||||
|
||||
orm 可以简单的进行事务操作
|
||||
|
||||
```go
|
||||
o := NewOrm()
|
||||
err := o.Begin()
|
||||
// 事务处理过程
|
||||
...
|
||||
...
|
||||
// 此过程中的所有使用 o Ormer 对象的查询都在事务处理范围内
|
||||
if SomeError {
|
||||
err = o.Rollback()
|
||||
} else {
|
||||
err = o.Commit()
|
||||
}
|
||||
```
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
@ -108,6 +116,7 @@ type fieldInfo struct {
|
||||
null bool
|
||||
index bool
|
||||
unique bool
|
||||
colDefault bool
|
||||
initial StrTo
|
||||
size int
|
||||
auto_now bool
|
||||
@ -272,6 +281,11 @@ checkType:
|
||||
fi.pk = attrs["pk"]
|
||||
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 {
|
||||
case RelManyToMany, RelReverseMany, RelReverseOne:
|
||||
fi.null = false
|
||||
|
@ -1,8 +1,16 @@
|
||||
// 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 slene
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user