mirror of https://github.com/beego/bee.git
bee pack auto generate comment router files
This commit is contained in:
parent
10bb0454f6
commit
5a7792d8b4
|
@ -18,6 +18,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/astaxie/beego"
|
||||||
"github.com/beego/bee/cmd/commands"
|
"github.com/beego/bee/cmd/commands"
|
||||||
"github.com/beego/bee/cmd/commands/version"
|
"github.com/beego/bee/cmd/commands/version"
|
||||||
beeLogger "github.com/beego/bee/logger"
|
beeLogger "github.com/beego/bee/logger"
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Contributing to beego
|
||||||
|
|
||||||
|
beego is an open source project.
|
||||||
|
|
||||||
|
It is the work of hundreds of contributors. We appreciate your help!
|
||||||
|
|
||||||
|
Here are instructions to get you started. They are probably not perfect,
|
||||||
|
please let us know if anything feels wrong or incomplete.
|
||||||
|
|
||||||
|
## Contribution guidelines
|
||||||
|
|
||||||
|
### Pull requests
|
||||||
|
|
||||||
|
First of all. beego follow the gitflow. So please send you pull request
|
||||||
|
to **develop** branch. We will close the pull request to master branch.
|
||||||
|
|
||||||
|
We are always happy to receive pull requests, and do our best to
|
||||||
|
review them as fast as possible. Not sure if that typo is worth a pull
|
||||||
|
request? Do it! We will appreciate it.
|
||||||
|
|
||||||
|
If your pull request is not accepted on the first try, don't be
|
||||||
|
discouraged! Sometimes we can make a mistake, please do more explaining
|
||||||
|
for us. We will appreciate it.
|
||||||
|
|
||||||
|
We're trying very hard to keep beego simple and fast. We don't want it
|
||||||
|
to do everything for everybody. This means that we might decide against
|
||||||
|
incorporating a new feature. But we will give you some advice on how to
|
||||||
|
do it in other way.
|
||||||
|
|
||||||
|
### Create issues
|
||||||
|
|
||||||
|
Any significant improvement should be documented as [a GitHub
|
||||||
|
issue](https://github.com/astaxie/beego/issues) before anybody
|
||||||
|
starts working on it.
|
||||||
|
|
||||||
|
Also when filing an issue, make sure to answer these five questions:
|
||||||
|
|
||||||
|
- What version of beego are you using (bee version)?
|
||||||
|
- What operating system and processor architecture are you using?
|
||||||
|
- What did you do?
|
||||||
|
- What did you expect to see?
|
||||||
|
- What did you see instead?
|
||||||
|
|
||||||
|
### but check existing issues and docs first!
|
||||||
|
|
||||||
|
Please take a moment to check that an issue doesn't already exist
|
||||||
|
documenting your bug report or improvement proposal. If it does, it
|
||||||
|
never hurts to add a quick "+1" or "I have this problem too". This will
|
||||||
|
help prioritize the most common problems and requests.
|
||||||
|
|
||||||
|
Also if you don't know how to use it. please make sure you have read though
|
||||||
|
the docs in http://beego.me/docs
|
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright 2014 astaxie
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) [![Go Report Card](https://goreportcard.com/badge/github.com/astaxie/beego)](https://goreportcard.com/report/github.com/astaxie/beego)
|
||||||
|
|
||||||
|
|
||||||
|
beego is used for rapid development of RESTful APIs, web apps and backend services in Go.
|
||||||
|
It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding.
|
||||||
|
|
||||||
|
###### More info at [beego.me](http://beego.me).
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
#### Download and install
|
||||||
|
|
||||||
|
go get github.com/astaxie/beego
|
||||||
|
|
||||||
|
#### Create file `hello.go`
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/astaxie/beego"
|
||||||
|
|
||||||
|
func main(){
|
||||||
|
beego.Run()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
#### Build and run
|
||||||
|
|
||||||
|
go build hello.go
|
||||||
|
./hello
|
||||||
|
|
||||||
|
#### Go to [http://localhost:8080](http://localhost:8080)
|
||||||
|
|
||||||
|
Congratulations! You've just built your first **beego** app.
|
||||||
|
|
||||||
|
###### Please see [Documentation](http://beego.me/docs) for more.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* RESTful support
|
||||||
|
* MVC architecture
|
||||||
|
* Modularity
|
||||||
|
* Auto API documents
|
||||||
|
* Annotation router
|
||||||
|
* Namespace
|
||||||
|
* Powerful development tools
|
||||||
|
* Full stack for Web & API
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
* [English](http://beego.me/docs/intro/)
|
||||||
|
* [中文文档](http://beego.me/docs/intro/)
|
||||||
|
* [Русский](http://beego.me/docs/intro/)
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
* [http://beego.me/community](http://beego.me/community)
|
||||||
|
* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
beego source code is licensed under the Apache Licence, Version 2.0
|
||||||
|
(http://www.apache.org/licenses/LICENSE-2.0.html).
|
|
@ -0,0 +1,404 @@
|
||||||
|
// 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"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/grace"
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
"github.com/astaxie/beego/toolbox"
|
||||||
|
"github.com/astaxie/beego/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BeeAdminApp is the default adminApp used by admin module.
|
||||||
|
var beeAdminApp *adminApp
|
||||||
|
|
||||||
|
// FilterMonitorFunc is default monitor filter when admin module is enable.
|
||||||
|
// if this func returns, admin module records qbs for this request by condition of this function logic.
|
||||||
|
// usage:
|
||||||
|
// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool {
|
||||||
|
// if method == "POST" {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// if t.Nanoseconds() < 100 {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// if strings.HasPrefix(requestPath, "/astaxie") {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// beego.FilterMonitorFunc = MyFilterMonitor.
|
||||||
|
var FilterMonitorFunc func(string, string, time.Duration, string, int) bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
beeAdminApp = &adminApp{
|
||||||
|
routers: make(map[string]http.HandlerFunc),
|
||||||
|
}
|
||||||
|
beeAdminApp.Route("/", adminIndex)
|
||||||
|
beeAdminApp.Route("/qps", qpsIndex)
|
||||||
|
beeAdminApp.Route("/prof", profIndex)
|
||||||
|
beeAdminApp.Route("/healthcheck", healthcheck)
|
||||||
|
beeAdminApp.Route("/task", taskStatus)
|
||||||
|
beeAdminApp.Route("/listconf", listConf)
|
||||||
|
FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminIndex is the default http.Handler for admin module.
|
||||||
|
// it matches url pattern "/".
|
||||||
|
func adminIndex(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
execTpl(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
data := make(map[interface{}]interface{})
|
||||||
|
data["Content"] = toolbox.StatisticsMap.GetMap()
|
||||||
|
execTpl(rw, data, qpsTpl, defaultScriptsTpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListConf is the http.Handler of displaying all beego configuration values as key/value pair.
|
||||||
|
// it's registered with url pattern "/listconf" in admin module.
|
||||||
|
func listConf(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
r.ParseForm()
|
||||||
|
command := r.Form.Get("command")
|
||||||
|
if command == "" {
|
||||||
|
rw.Write([]byte("command not support"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make(map[interface{}]interface{})
|
||||||
|
switch command {
|
||||||
|
case "conf":
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
list("BConfig", BConfig, m)
|
||||||
|
m["AppConfigPath"] = appConfigPath
|
||||||
|
m["AppConfigProvider"] = appConfigProvider
|
||||||
|
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
|
||||||
|
tmpl = template.Must(tmpl.Parse(configTpl))
|
||||||
|
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
|
||||||
|
|
||||||
|
data["Content"] = m
|
||||||
|
|
||||||
|
tmpl.Execute(rw, data)
|
||||||
|
|
||||||
|
case "router":
|
||||||
|
content := PrintTree()
|
||||||
|
content["Fields"] = []string{
|
||||||
|
"Router Pattern",
|
||||||
|
"Methods",
|
||||||
|
"Controller",
|
||||||
|
}
|
||||||
|
data["Content"] = content
|
||||||
|
data["Title"] = "Routers"
|
||||||
|
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||||
|
case "filter":
|
||||||
|
var (
|
||||||
|
content = map[string]interface{}{
|
||||||
|
"Fields": []string{
|
||||||
|
"Router Pattern",
|
||||||
|
"Filter Function",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
filterTypes = []string{}
|
||||||
|
filterTypeData = make(map[string]interface{})
|
||||||
|
)
|
||||||
|
|
||||||
|
if BeeApp.Handlers.enableFilter {
|
||||||
|
var filterType string
|
||||||
|
for k, fr := range map[int]string{
|
||||||
|
BeforeStatic: "Before Static",
|
||||||
|
BeforeRouter: "Before Router",
|
||||||
|
BeforeExec: "Before Exec",
|
||||||
|
AfterExec: "After Exec",
|
||||||
|
FinishRouter: "Finish Router"} {
|
||||||
|
if bf := BeeApp.Handlers.filters[k]; len(bf) > 0 {
|
||||||
|
filterType = fr
|
||||||
|
filterTypes = append(filterTypes, filterType)
|
||||||
|
resultList := new([][]string)
|
||||||
|
for _, f := range bf {
|
||||||
|
var result = []string{
|
||||||
|
f.pattern,
|
||||||
|
utils.GetFuncName(f.filterFunc),
|
||||||
|
}
|
||||||
|
*resultList = append(*resultList, result)
|
||||||
|
}
|
||||||
|
filterTypeData[filterType] = resultList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content["Data"] = filterTypeData
|
||||||
|
content["Methods"] = filterTypes
|
||||||
|
|
||||||
|
data["Content"] = content
|
||||||
|
data["Title"] = "Filters"
|
||||||
|
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
|
||||||
|
default:
|
||||||
|
rw.Write([]byte("command not support"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func list(root string, p interface{}, m map[string]interface{}) {
|
||||||
|
pt := reflect.TypeOf(p)
|
||||||
|
pv := reflect.ValueOf(p)
|
||||||
|
if pt.Kind() == reflect.Ptr {
|
||||||
|
pt = pt.Elem()
|
||||||
|
pv = pv.Elem()
|
||||||
|
}
|
||||||
|
for i := 0; i < pv.NumField(); i++ {
|
||||||
|
var key string
|
||||||
|
if root == "" {
|
||||||
|
key = pt.Field(i).Name
|
||||||
|
} else {
|
||||||
|
key = root + "." + pt.Field(i).Name
|
||||||
|
}
|
||||||
|
if pv.Field(i).Kind() == reflect.Struct {
|
||||||
|
list(key, pv.Field(i).Interface(), m)
|
||||||
|
} else {
|
||||||
|
m[key] = pv.Field(i).Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintTree prints all registered routers.
|
||||||
|
func PrintTree() map[string]interface{} {
|
||||||
|
var (
|
||||||
|
content = map[string]interface{}{}
|
||||||
|
methods = []string{}
|
||||||
|
methodsData = make(map[string]interface{})
|
||||||
|
)
|
||||||
|
for method, t := range BeeApp.Handlers.routers {
|
||||||
|
|
||||||
|
resultList := new([][]string)
|
||||||
|
|
||||||
|
printTree(resultList, t)
|
||||||
|
|
||||||
|
methods = append(methods, method)
|
||||||
|
methodsData[method] = resultList
|
||||||
|
}
|
||||||
|
|
||||||
|
content["Data"] = methodsData
|
||||||
|
content["Methods"] = methods
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTree(resultList *[][]string, t *Tree) {
|
||||||
|
for _, tr := range t.fixrouters {
|
||||||
|
printTree(resultList, tr)
|
||||||
|
}
|
||||||
|
if t.wildcard != nil {
|
||||||
|
printTree(resultList, t.wildcard)
|
||||||
|
}
|
||||||
|
for _, l := range t.leaves {
|
||||||
|
if v, ok := l.runObject.(*ControllerInfo); ok {
|
||||||
|
if v.routerType == routerTypeBeego {
|
||||||
|
var result = []string{
|
||||||
|
v.pattern,
|
||||||
|
fmt.Sprintf("%s", v.methods),
|
||||||
|
v.controllerType.String(),
|
||||||
|
}
|
||||||
|
*resultList = append(*resultList, result)
|
||||||
|
} else if v.routerType == routerTypeRESTFul {
|
||||||
|
var result = []string{
|
||||||
|
v.pattern,
|
||||||
|
fmt.Sprintf("%s", v.methods),
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
*resultList = append(*resultList, result)
|
||||||
|
} else if v.routerType == routerTypeHandler {
|
||||||
|
var result = []string{
|
||||||
|
v.pattern,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
*resultList = append(*resultList, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProfIndex is a http.Handler for showing profile command.
|
||||||
|
// it's in url pattern "/prof" in admin module.
|
||||||
|
func profIndex(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
r.ParseForm()
|
||||||
|
command := r.Form.Get("command")
|
||||||
|
if command == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
format = r.Form.Get("format")
|
||||||
|
data = make(map[interface{}]interface{})
|
||||||
|
result bytes.Buffer
|
||||||
|
)
|
||||||
|
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
|
||||||
|
defaultTpl := defaultScriptsTpl
|
||||||
|
if command == "gc summary" {
|
||||||
|
defaultTpl = gcAjaxTpl
|
||||||
|
}
|
||||||
|
execTpl(rw, data, profillingTpl, defaultTpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
var (
|
||||||
|
result []string
|
||||||
|
data = make(map[interface{}]interface{})
|
||||||
|
resultList = new([][]string)
|
||||||
|
content = map[string]interface{}{
|
||||||
|
"Fields": []string{"Name", "Message", "Status"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for name, h := range toolbox.AdminCheckList {
|
||||||
|
if err := h.Check(); err != nil {
|
||||||
|
result = []string{
|
||||||
|
"error",
|
||||||
|
name,
|
||||||
|
err.Error(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = []string{
|
||||||
|
"success",
|
||||||
|
name,
|
||||||
|
"OK",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*resultList = append(*resultList, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
content["Data"] = resultList
|
||||||
|
data["Content"] = content
|
||||||
|
data["Title"] = "Health Check"
|
||||||
|
execTpl(rw, data, healthCheckTpl, defaultScriptsTpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskStatus is a http.Handler with running task status (task name, status and the last execution).
|
||||||
|
// it's in "/task" pattern in admin module.
|
||||||
|
func taskStatus(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
data := make(map[interface{}]interface{})
|
||||||
|
|
||||||
|
// Run Task
|
||||||
|
req.ParseForm()
|
||||||
|
taskname := req.Form.Get("taskname")
|
||||||
|
if taskname != "" {
|
||||||
|
if t, ok := toolbox.AdminTaskList[taskname]; ok {
|
||||||
|
if err := t.Run(); err != nil {
|
||||||
|
data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
|
||||||
|
}
|
||||||
|
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus())}
|
||||||
|
} else {
|
||||||
|
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Tasks
|
||||||
|
content := make(map[string]interface{})
|
||||||
|
resultList := new([][]string)
|
||||||
|
var fields = []string{
|
||||||
|
"Task Name",
|
||||||
|
"Task Spec",
|
||||||
|
"Task Status",
|
||||||
|
"Last Time",
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
for tname, tk := range toolbox.AdminTaskList {
|
||||||
|
result := []string{
|
||||||
|
tname,
|
||||||
|
tk.GetSpec(),
|
||||||
|
tk.GetStatus(),
|
||||||
|
tk.GetPrev().String(),
|
||||||
|
}
|
||||||
|
*resultList = append(*resultList, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
content["Fields"] = fields
|
||||||
|
content["Data"] = resultList
|
||||||
|
data["Content"] = content
|
||||||
|
data["Title"] = "Tasks"
|
||||||
|
execTpl(rw, data, tasksTpl, defaultScriptsTpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execTpl(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) {
|
||||||
|
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
|
||||||
|
for _, tpl := range tpls {
|
||||||
|
tmpl = template.Must(tmpl.Parse(tpl))
|
||||||
|
}
|
||||||
|
tmpl.Execute(rw, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adminApp is an http.HandlerFunc map used as beeAdminApp.
|
||||||
|
type adminApp struct {
|
||||||
|
routers map[string]http.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route adds http.HandlerFunc to adminApp with url pattern.
|
||||||
|
func (admin *adminApp) Route(pattern string, f http.HandlerFunc) {
|
||||||
|
admin.routers[pattern] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run adminApp http server.
|
||||||
|
// Its addr is defined in configuration file as adminhttpaddr and adminhttpport.
|
||||||
|
func (admin *adminApp) Run() {
|
||||||
|
if len(toolbox.AdminTaskList) > 0 {
|
||||||
|
toolbox.StartTask()
|
||||||
|
}
|
||||||
|
addr := BConfig.Listen.AdminAddr
|
||||||
|
|
||||||
|
if BConfig.Listen.AdminPort != 0 {
|
||||||
|
addr = fmt.Sprintf("%s:%d", BConfig.Listen.AdminAddr, BConfig.Listen.AdminPort)
|
||||||
|
}
|
||||||
|
for p, f := range admin.routers {
|
||||||
|
http.Handle(p, f)
|
||||||
|
}
|
||||||
|
logs.Info("Admin server Running on %s", addr)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if BConfig.Listen.Graceful {
|
||||||
|
err = grace.ListenAndServe(addr, nil)
|
||||||
|
} else {
|
||||||
|
err = http.ListenAndServe(addr, nil)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logs.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,479 @@
|
||||||
|
// 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 (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/fcgi"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/grace"
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
"github.com/astaxie/beego/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// BeeApp is an application instance
|
||||||
|
BeeApp *App
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// create beego application
|
||||||
|
BeeApp = NewApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
// App defines beego application with a new PatternServeMux.
|
||||||
|
type App struct {
|
||||||
|
Handlers *ControllerRegister
|
||||||
|
Server *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewApp returns a new beego application.
|
||||||
|
func NewApp() *App {
|
||||||
|
cr := NewControllerRegister()
|
||||||
|
app := &App{Handlers: cr, Server: &http.Server{}}
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
// MiddleWare function for http.Handler
|
||||||
|
type MiddleWare func(http.Handler) http.Handler
|
||||||
|
|
||||||
|
// Run beego application.
|
||||||
|
func (app *App) Run(mws ...MiddleWare) {
|
||||||
|
addr := BConfig.Listen.HTTPAddr
|
||||||
|
|
||||||
|
if BConfig.Listen.HTTPPort != 0 {
|
||||||
|
addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPAddr, BConfig.Listen.HTTPPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
l net.Listener
|
||||||
|
endRunning = make(chan bool, 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// run cgi server
|
||||||
|
if BConfig.Listen.EnableFcgi {
|
||||||
|
if BConfig.Listen.EnableStdIo {
|
||||||
|
if err = fcgi.Serve(nil, app.Handlers); err == nil { // standard I/O
|
||||||
|
logs.Info("Use FCGI via standard I/O")
|
||||||
|
} else {
|
||||||
|
logs.Critical("Cannot use FCGI via standard I/O", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if BConfig.Listen.HTTPPort == 0 {
|
||||||
|
// remove the Socket file before start
|
||||||
|
if utils.FileExists(addr) {
|
||||||
|
os.Remove(addr)
|
||||||
|
}
|
||||||
|
l, err = net.Listen("unix", addr)
|
||||||
|
} else {
|
||||||
|
l, err = net.Listen("tcp", addr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logs.Critical("Listen: ", err)
|
||||||
|
}
|
||||||
|
if err = fcgi.Serve(l, app.Handlers); err != nil {
|
||||||
|
logs.Critical("fcgi.Serve: ", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Server.Handler = app.Handlers
|
||||||
|
for i := len(mws) - 1; i >= 0; i-- {
|
||||||
|
if mws[i] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
app.Server.Handler = mws[i](app.Server.Handler)
|
||||||
|
}
|
||||||
|
app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
|
||||||
|
app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
|
||||||
|
app.Server.ErrorLog = logs.GetLogger("HTTP")
|
||||||
|
|
||||||
|
// run graceful mode
|
||||||
|
if BConfig.Listen.Graceful {
|
||||||
|
httpsAddr := BConfig.Listen.HTTPSAddr
|
||||||
|
app.Server.Addr = httpsAddr
|
||||||
|
if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
|
||||||
|
go func() {
|
||||||
|
time.Sleep(20 * time.Microsecond)
|
||||||
|
if BConfig.Listen.HTTPSPort != 0 {
|
||||||
|
httpsAddr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
|
||||||
|
app.Server.Addr = httpsAddr
|
||||||
|
}
|
||||||
|
server := grace.NewServer(httpsAddr, app.Handlers)
|
||||||
|
server.Server.ReadTimeout = app.Server.ReadTimeout
|
||||||
|
server.Server.WriteTimeout = app.Server.WriteTimeout
|
||||||
|
if BConfig.Listen.EnableMutualHTTPS {
|
||||||
|
|
||||||
|
if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil {
|
||||||
|
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
endRunning <- true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
||||||
|
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
endRunning <- true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if BConfig.Listen.EnableHTTP {
|
||||||
|
go func() {
|
||||||
|
server := grace.NewServer(addr, app.Handlers)
|
||||||
|
server.Server.ReadTimeout = app.Server.ReadTimeout
|
||||||
|
server.Server.WriteTimeout = app.Server.WriteTimeout
|
||||||
|
if BConfig.Listen.ListenTCP4 {
|
||||||
|
server.Network = "tcp4"
|
||||||
|
}
|
||||||
|
if err := server.ListenAndServe(); err != nil {
|
||||||
|
logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
endRunning <- true
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
<-endRunning
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// run normal mode
|
||||||
|
if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
|
||||||
|
go func() {
|
||||||
|
time.Sleep(20 * time.Microsecond)
|
||||||
|
if BConfig.Listen.HTTPSPort != 0 {
|
||||||
|
app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
|
||||||
|
} else if BConfig.Listen.EnableHTTP {
|
||||||
|
BeeLogger.Info("Start https server error, conflict with http.Please reset https port")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logs.Info("https server Running on https://%s", app.Server.Addr)
|
||||||
|
if BConfig.Listen.EnableMutualHTTPS {
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile)
|
||||||
|
if err != nil {
|
||||||
|
BeeLogger.Info("MutualHTTPS should provide TrustCaFile")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pool.AppendCertsFromPEM(data)
|
||||||
|
app.Server.TLSConfig = &tls.Config{
|
||||||
|
ClientCAs: pool,
|
||||||
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
||||||
|
logs.Critical("ListenAndServeTLS: ", err)
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
endRunning <- true
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if BConfig.Listen.EnableHTTP {
|
||||||
|
go func() {
|
||||||
|
app.Server.Addr = addr
|
||||||
|
logs.Info("http server Running on http://%s", app.Server.Addr)
|
||||||
|
if BConfig.Listen.ListenTCP4 {
|
||||||
|
ln, err := net.Listen("tcp4", app.Server.Addr)
|
||||||
|
if err != nil {
|
||||||
|
logs.Critical("ListenAndServe: ", err)
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
endRunning <- true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = app.Server.Serve(ln); err != nil {
|
||||||
|
logs.Critical("ListenAndServe: ", err)
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
endRunning <- true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := app.Server.ListenAndServe(); err != nil {
|
||||||
|
logs.Critical("ListenAndServe: ", err)
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
endRunning <- true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
<-endRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
// Router adds a patterned controller handler to BeeApp.
|
||||||
|
// it's an alias method of App.Router.
|
||||||
|
// usage:
|
||||||
|
// simple router
|
||||||
|
// beego.Router("/admin", &admin.UserController{})
|
||||||
|
// beego.Router("/admin/index", &admin.ArticleController{})
|
||||||
|
//
|
||||||
|
// regex router
|
||||||
|
//
|
||||||
|
// beego.Router("/api/:id([0-9]+)", &controllers.RController{})
|
||||||
|
//
|
||||||
|
// custom rules
|
||||||
|
// beego.Router("/api/list",&RestController{},"*:ListFood")
|
||||||
|
// beego.Router("/api/create",&RestController{},"post:CreateFood")
|
||||||
|
// beego.Router("/api/update",&RestController{},"put:UpdateFood")
|
||||||
|
// beego.Router("/api/delete",&RestController{},"delete:DeleteFood")
|
||||||
|
func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
|
||||||
|
BeeApp.Handlers.Add(rootpath, c, mappingMethods...)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful
|
||||||
|
// in web applications that inherit most routes from a base webapp via the underscore
|
||||||
|
// import, and aim to overwrite only certain paths.
|
||||||
|
// The method parameter can be empty or "*" for all HTTP methods, or a particular
|
||||||
|
// method type (e.g. "GET" or "POST") for selective removal.
|
||||||
|
//
|
||||||
|
// Usage (replace "GET" with "*" for all methods):
|
||||||
|
// beego.UnregisterFixedRoute("/yourpreviouspath", "GET")
|
||||||
|
// beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage")
|
||||||
|
func UnregisterFixedRoute(fixedRoute string, method string) *App {
|
||||||
|
subPaths := splitPath(fixedRoute)
|
||||||
|
if method == "" || method == "*" {
|
||||||
|
for m := range HTTPMETHOD {
|
||||||
|
if _, ok := BeeApp.Handlers.routers[m]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if BeeApp.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") {
|
||||||
|
findAndRemoveSingleTree(BeeApp.Handlers.routers[m])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
findAndRemoveTree(subPaths, BeeApp.Handlers.routers[m], m)
|
||||||
|
}
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
// Single HTTP method
|
||||||
|
um := strings.ToUpper(method)
|
||||||
|
if _, ok := BeeApp.Handlers.routers[um]; ok {
|
||||||
|
if BeeApp.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") {
|
||||||
|
findAndRemoveSingleTree(BeeApp.Handlers.routers[um])
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
findAndRemoveTree(subPaths, BeeApp.Handlers.routers[um], um)
|
||||||
|
}
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) {
|
||||||
|
for i := range entryPointTree.fixrouters {
|
||||||
|
if entryPointTree.fixrouters[i].prefix == paths[0] {
|
||||||
|
if len(paths) == 1 {
|
||||||
|
if len(entryPointTree.fixrouters[i].fixrouters) > 0 {
|
||||||
|
// If the route had children subtrees, remove just the functional leaf,
|
||||||
|
// to allow children to function as before
|
||||||
|
if len(entryPointTree.fixrouters[i].leaves) > 0 {
|
||||||
|
entryPointTree.fixrouters[i].leaves[0] = nil
|
||||||
|
entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Remove the *Tree from the fixrouters slice
|
||||||
|
entryPointTree.fixrouters[i] = nil
|
||||||
|
|
||||||
|
if i == len(entryPointTree.fixrouters)-1 {
|
||||||
|
entryPointTree.fixrouters = entryPointTree.fixrouters[:i]
|
||||||
|
} else {
|
||||||
|
entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findAndRemoveSingleTree(entryPointTree *Tree) {
|
||||||
|
if entryPointTree == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(entryPointTree.fixrouters) > 0 {
|
||||||
|
// If the route had children subtrees, remove just the functional leaf,
|
||||||
|
// to allow children to function as before
|
||||||
|
if len(entryPointTree.leaves) > 0 {
|
||||||
|
entryPointTree.leaves[0] = nil
|
||||||
|
entryPointTree.leaves = entryPointTree.leaves[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include will generate router file in the router/xxx.go from the controller's comments
|
||||||
|
// usage:
|
||||||
|
// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
|
||||||
|
// type BankAccount struct{
|
||||||
|
// beego.Controller
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// register the function
|
||||||
|
// func (b *BankAccount)Mapping(){
|
||||||
|
// b.Mapping("ShowAccount" , b.ShowAccount)
|
||||||
|
// b.Mapping("ModifyAccount", b.ModifyAccount)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
// //@router /account/:id [get]
|
||||||
|
// func (b *BankAccount) ShowAccount(){
|
||||||
|
// //logic
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// //@router /account/:id [post]
|
||||||
|
// func (b *BankAccount) ModifyAccount(){
|
||||||
|
// //logic
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// the comments @router url methodlist
|
||||||
|
// url support all the function Router's pattern
|
||||||
|
// methodlist [get post head put delete options *]
|
||||||
|
func Include(cList ...ControllerInterface) *App {
|
||||||
|
BeeApp.Handlers.Include(cList...)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESTRouter adds a restful controller handler to BeeApp.
|
||||||
|
// its' controller implements beego.ControllerInterface and
|
||||||
|
// defines a param "pattern/:objectId" to visit each resource.
|
||||||
|
func RESTRouter(rootpath string, c ControllerInterface) *App {
|
||||||
|
Router(rootpath, c)
|
||||||
|
Router(path.Join(rootpath, ":objectId"), c)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoRouter adds defined controller handler to BeeApp.
|
||||||
|
// it's same to App.AutoRouter.
|
||||||
|
// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
|
||||||
|
// visit the url /main/list to exec List function or /main/page to exec Page function.
|
||||||
|
func AutoRouter(c ControllerInterface) *App {
|
||||||
|
BeeApp.Handlers.AddAuto(c)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoPrefix adds controller handler to BeeApp with prefix.
|
||||||
|
// it's same to App.AutoRouterWithPrefix.
|
||||||
|
// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
|
||||||
|
// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
|
||||||
|
func AutoPrefix(prefix string, c ControllerInterface) *App {
|
||||||
|
BeeApp.Handlers.AddAutoPrefix(prefix, c)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get used to register router for Get method
|
||||||
|
// usage:
|
||||||
|
// beego.Get("/", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func Get(rootpath string, f FilterFunc) *App {
|
||||||
|
BeeApp.Handlers.Get(rootpath, f)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post used to register router for Post method
|
||||||
|
// usage:
|
||||||
|
// beego.Post("/api", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func Post(rootpath string, f FilterFunc) *App {
|
||||||
|
BeeApp.Handlers.Post(rootpath, f)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete used to register router for Delete method
|
||||||
|
// usage:
|
||||||
|
// beego.Delete("/api", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func Delete(rootpath string, f FilterFunc) *App {
|
||||||
|
BeeApp.Handlers.Delete(rootpath, f)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put used to register router for Put method
|
||||||
|
// usage:
|
||||||
|
// beego.Put("/api", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func Put(rootpath string, f FilterFunc) *App {
|
||||||
|
BeeApp.Handlers.Put(rootpath, f)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head used to register router for Head method
|
||||||
|
// usage:
|
||||||
|
// beego.Head("/api", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func Head(rootpath string, f FilterFunc) *App {
|
||||||
|
BeeApp.Handlers.Head(rootpath, f)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options used to register router for Options method
|
||||||
|
// usage:
|
||||||
|
// beego.Options("/api", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func Options(rootpath string, f FilterFunc) *App {
|
||||||
|
BeeApp.Handlers.Options(rootpath, f)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch used to register router for Patch method
|
||||||
|
// usage:
|
||||||
|
// beego.Patch("/api", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func Patch(rootpath string, f FilterFunc) *App {
|
||||||
|
BeeApp.Handlers.Patch(rootpath, f)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any used to register router for all methods
|
||||||
|
// usage:
|
||||||
|
// beego.Any("/api", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func Any(rootpath string, f FilterFunc) *App {
|
||||||
|
BeeApp.Handlers.Any(rootpath, f)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler used to register a Handler router
|
||||||
|
// usage:
|
||||||
|
// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
||||||
|
// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
|
||||||
|
// }))
|
||||||
|
func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
|
||||||
|
BeeApp.Handlers.Handler(rootpath, h, options...)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertFilter adds a FilterFunc with pattern condition and action constant.
|
||||||
|
// The pos means action constant including
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 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 (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// VERSION represent beego web framework version.
|
||||||
|
VERSION = "1.9.2"
|
||||||
|
|
||||||
|
// DEV is for develop
|
||||||
|
DEV = "dev"
|
||||||
|
// PROD is for production
|
||||||
|
PROD = "prod"
|
||||||
|
)
|
||||||
|
|
||||||
|
//hook function to run
|
||||||
|
type hookfunc func() error
|
||||||
|
|
||||||
|
var (
|
||||||
|
hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddAPPStartHook is used to register the hookfunc
|
||||||
|
// The hookfuncs will run in beego.Run()
|
||||||
|
// such as initiating session , starting middleware , building template, starting admin control and so on.
|
||||||
|
func AddAPPStartHook(hf ...hookfunc) {
|
||||||
|
hooks = append(hooks, hf...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run beego application.
|
||||||
|
// beego.Run() default run on HttpPort
|
||||||
|
// beego.Run("localhost")
|
||||||
|
// beego.Run(":8089")
|
||||||
|
// beego.Run("127.0.0.1:8089")
|
||||||
|
func Run(params ...string) {
|
||||||
|
|
||||||
|
initBeforeHTTPRun()
|
||||||
|
|
||||||
|
if len(params) > 0 && params[0] != "" {
|
||||||
|
strs := strings.Split(params[0], ":")
|
||||||
|
if len(strs) > 0 && strs[0] != "" {
|
||||||
|
BConfig.Listen.HTTPAddr = strs[0]
|
||||||
|
}
|
||||||
|
if len(strs) > 1 && strs[1] != "" {
|
||||||
|
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BeeApp.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWithMiddleWares Run beego application with middlewares.
|
||||||
|
func RunWithMiddleWares(addr string, mws ...MiddleWare) {
|
||||||
|
initBeforeHTTPRun()
|
||||||
|
|
||||||
|
strs := strings.Split(addr, ":")
|
||||||
|
if len(strs) > 0 && strs[0] != "" {
|
||||||
|
BConfig.Listen.HTTPAddr = strs[0]
|
||||||
|
}
|
||||||
|
if len(strs) > 1 && strs[1] != "" {
|
||||||
|
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
BeeApp.Run(mws...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initBeforeHTTPRun() {
|
||||||
|
//init hooks
|
||||||
|
AddAPPStartHook(
|
||||||
|
registerMime,
|
||||||
|
registerDefaultErrorHandler,
|
||||||
|
registerSession,
|
||||||
|
registerTemplate,
|
||||||
|
registerAdmin,
|
||||||
|
registerGzip,
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, hk := range hooks {
|
||||||
|
if err := hk(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBeegoInit is for test package init
|
||||||
|
func TestBeegoInit(ap string) {
|
||||||
|
path := filepath.Join(ap, "conf", "app.conf")
|
||||||
|
os.Chdir(ap)
|
||||||
|
InitBeegoBeforeTest(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitBeegoBeforeTest is for test package init
|
||||||
|
func InitBeegoBeforeTest(appConfigPath string) {
|
||||||
|
if err := LoadAppConfig(appConfigProvider, appConfigPath); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
BConfig.RunMode = "test"
|
||||||
|
initBeforeHTTPRun()
|
||||||
|
}
|
|
@ -0,0 +1,497 @@
|
||||||
|
// 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"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/config"
|
||||||
|
"github.com/astaxie/beego/context"
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
"github.com/astaxie/beego/session"
|
||||||
|
"github.com/astaxie/beego/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is the main struct for BConfig
|
||||||
|
type Config struct {
|
||||||
|
AppName string //Application name
|
||||||
|
RunMode string //Running Mode: dev | prod
|
||||||
|
RouterCaseSensitive bool
|
||||||
|
ServerName string
|
||||||
|
RecoverPanic bool
|
||||||
|
RecoverFunc func(*context.Context)
|
||||||
|
CopyRequestBody bool
|
||||||
|
EnableGzip bool
|
||||||
|
MaxMemory int64
|
||||||
|
EnableErrorsShow bool
|
||||||
|
EnableErrorsRender bool
|
||||||
|
Listen Listen
|
||||||
|
WebConfig WebConfig
|
||||||
|
Log LogConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen holds for http and https related config
|
||||||
|
type Listen struct {
|
||||||
|
Graceful bool // Graceful means use graceful module to start the server
|
||||||
|
ServerTimeOut int64
|
||||||
|
ListenTCP4 bool
|
||||||
|
EnableHTTP bool
|
||||||
|
HTTPAddr string
|
||||||
|
HTTPPort int
|
||||||
|
EnableHTTPS bool
|
||||||
|
EnableMutualHTTPS bool
|
||||||
|
HTTPSAddr string
|
||||||
|
HTTPSPort int
|
||||||
|
HTTPSCertFile string
|
||||||
|
HTTPSKeyFile string
|
||||||
|
TrustCaFile string
|
||||||
|
EnableAdmin bool
|
||||||
|
AdminAddr string
|
||||||
|
AdminPort int
|
||||||
|
EnableFcgi bool
|
||||||
|
EnableStdIo bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebConfig holds web related config
|
||||||
|
type WebConfig struct {
|
||||||
|
AutoRender bool
|
||||||
|
EnableDocs bool
|
||||||
|
FlashName string
|
||||||
|
FlashSeparator string
|
||||||
|
DirectoryIndex bool
|
||||||
|
StaticDir map[string]string
|
||||||
|
StaticExtensionsToGzip []string
|
||||||
|
TemplateLeft string
|
||||||
|
TemplateRight string
|
||||||
|
ViewsPath string
|
||||||
|
EnableXSRF bool
|
||||||
|
XSRFKey string
|
||||||
|
XSRFExpire int
|
||||||
|
Session SessionConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionConfig holds session related config
|
||||||
|
type SessionConfig struct {
|
||||||
|
SessionOn bool
|
||||||
|
SessionProvider string
|
||||||
|
SessionName string
|
||||||
|
SessionGCMaxLifetime int64
|
||||||
|
SessionProviderConfig string
|
||||||
|
SessionCookieLifeTime int
|
||||||
|
SessionAutoSetCookie bool
|
||||||
|
SessionDomain string
|
||||||
|
SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies.
|
||||||
|
SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
|
||||||
|
SessionNameInHTTPHeader string
|
||||||
|
SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogConfig holds Log related config
|
||||||
|
type LogConfig struct {
|
||||||
|
AccessLogs bool
|
||||||
|
AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
|
||||||
|
FileLineNum bool
|
||||||
|
Outputs map[string]string // Store Adaptor : config
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// BConfig is the default config for Application
|
||||||
|
BConfig *Config
|
||||||
|
// AppConfig is the instance of Config, store the config information from file
|
||||||
|
AppConfig *beegoAppConfig
|
||||||
|
// AppPath is the absolute path to the app
|
||||||
|
AppPath string
|
||||||
|
// GlobalSessions is the instance for the session manager
|
||||||
|
GlobalSessions *session.Manager
|
||||||
|
|
||||||
|
// appConfigPath is the path to the config files
|
||||||
|
appConfigPath string
|
||||||
|
// appConfigProvider is the provider for the config, default is ini
|
||||||
|
appConfigProvider = "ini"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
BConfig = newBConfig()
|
||||||
|
var err error
|
||||||
|
if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
workPath, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var filename = "app.conf"
|
||||||
|
if os.Getenv("BEEGO_RUNMODE") != "" {
|
||||||
|
filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
|
||||||
|
}
|
||||||
|
appConfigPath = filepath.Join(workPath, "conf", filename)
|
||||||
|
if !utils.FileExists(appConfigPath) {
|
||||||
|
appConfigPath = filepath.Join(AppPath, "conf", filename)
|
||||||
|
if !utils.FileExists(appConfigPath) {
|
||||||
|
AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = parseConfig(appConfigPath); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func recoverPanic(ctx *context.Context) {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
if err == ErrAbort {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !BConfig.RecoverPanic {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if BConfig.EnableErrorsShow {
|
||||||
|
if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
|
||||||
|
exception(fmt.Sprint(err), ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var stack string
|
||||||
|
logs.Critical("the request url is ", ctx.Input.URL())
|
||||||
|
logs.Critical("Handler crashed with error", err)
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
_, file, line, ok := runtime.Caller(i)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
logs.Critical(fmt.Sprintf("%s:%d", file, line))
|
||||||
|
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
|
||||||
|
}
|
||||||
|
if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
|
||||||
|
showErr(err, ctx, stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
AppName: "beego",
|
||||||
|
RunMode: PROD,
|
||||||
|
RouterCaseSensitive: true,
|
||||||
|
ServerName: "beegoServer:" + VERSION,
|
||||||
|
RecoverPanic: true,
|
||||||
|
RecoverFunc: recoverPanic,
|
||||||
|
CopyRequestBody: false,
|
||||||
|
EnableGzip: false,
|
||||||
|
MaxMemory: 1 << 26, //64MB
|
||||||
|
EnableErrorsShow: true,
|
||||||
|
EnableErrorsRender: true,
|
||||||
|
Listen: Listen{
|
||||||
|
Graceful: false,
|
||||||
|
ServerTimeOut: 0,
|
||||||
|
ListenTCP4: false,
|
||||||
|
EnableHTTP: true,
|
||||||
|
HTTPAddr: "",
|
||||||
|
HTTPPort: 8080,
|
||||||
|
EnableHTTPS: false,
|
||||||
|
HTTPSAddr: "",
|
||||||
|
HTTPSPort: 10443,
|
||||||
|
HTTPSCertFile: "",
|
||||||
|
HTTPSKeyFile: "",
|
||||||
|
EnableAdmin: false,
|
||||||
|
AdminAddr: "",
|
||||||
|
AdminPort: 8088,
|
||||||
|
EnableFcgi: false,
|
||||||
|
EnableStdIo: false,
|
||||||
|
},
|
||||||
|
WebConfig: WebConfig{
|
||||||
|
AutoRender: true,
|
||||||
|
EnableDocs: false,
|
||||||
|
FlashName: "BEEGO_FLASH",
|
||||||
|
FlashSeparator: "BEEGOFLASH",
|
||||||
|
DirectoryIndex: false,
|
||||||
|
StaticDir: map[string]string{"/static": "static"},
|
||||||
|
StaticExtensionsToGzip: []string{".css", ".js"},
|
||||||
|
TemplateLeft: "{{",
|
||||||
|
TemplateRight: "}}",
|
||||||
|
ViewsPath: "views",
|
||||||
|
EnableXSRF: false,
|
||||||
|
XSRFKey: "beegoxsrf",
|
||||||
|
XSRFExpire: 0,
|
||||||
|
Session: SessionConfig{
|
||||||
|
SessionOn: false,
|
||||||
|
SessionProvider: "memory",
|
||||||
|
SessionName: "beegosessionID",
|
||||||
|
SessionGCMaxLifetime: 3600,
|
||||||
|
SessionProviderConfig: "",
|
||||||
|
SessionDisableHTTPOnly: false,
|
||||||
|
SessionCookieLifeTime: 0, //set cookie default is the browser life
|
||||||
|
SessionAutoSetCookie: true,
|
||||||
|
SessionDomain: "",
|
||||||
|
SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
|
||||||
|
SessionNameInHTTPHeader: "Beegosessionid",
|
||||||
|
SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Log: LogConfig{
|
||||||
|
AccessLogs: false,
|
||||||
|
AccessLogsFormat: "APACHE_FORMAT",
|
||||||
|
FileLineNum: true,
|
||||||
|
Outputs: map[string]string{"console": ""},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now only support ini, next will support json.
|
||||||
|
func parseConfig(appConfigPath string) (err error) {
|
||||||
|
AppConfig, err = newAppConfig(appConfigProvider, appConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return assignConfig(AppConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignConfig(ac config.Configer) error {
|
||||||
|
for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} {
|
||||||
|
assignSingleConfig(i, ac)
|
||||||
|
}
|
||||||
|
// set the run mode first
|
||||||
|
if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" {
|
||||||
|
BConfig.RunMode = envRunMode
|
||||||
|
} else if runMode := ac.String("RunMode"); runMode != "" {
|
||||||
|
BConfig.RunMode = runMode
|
||||||
|
}
|
||||||
|
|
||||||
|
if sd := ac.String("StaticDir"); sd != "" {
|
||||||
|
BConfig.WebConfig.StaticDir = map[string]string{}
|
||||||
|
sds := strings.Fields(sd)
|
||||||
|
for _, v := range sds {
|
||||||
|
if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
|
||||||
|
BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1]
|
||||||
|
} else {
|
||||||
|
BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sgz := ac.String("StaticExtensionsToGzip"); sgz != "" {
|
||||||
|
extensions := strings.Split(sgz, ",")
|
||||||
|
fileExts := []string{}
|
||||||
|
for _, ext := range extensions {
|
||||||
|
ext = strings.TrimSpace(ext)
|
||||||
|
if ext == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(ext, ".") {
|
||||||
|
ext = "." + ext
|
||||||
|
}
|
||||||
|
fileExts = append(fileExts, ext)
|
||||||
|
}
|
||||||
|
if len(fileExts) > 0 {
|
||||||
|
BConfig.WebConfig.StaticExtensionsToGzip = fileExts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo := ac.String("LogOutputs"); lo != "" {
|
||||||
|
// if lo is not nil or empty
|
||||||
|
// means user has set his own LogOutputs
|
||||||
|
// clear the default setting to BConfig.Log.Outputs
|
||||||
|
BConfig.Log.Outputs = make(map[string]string)
|
||||||
|
los := strings.Split(lo, ";")
|
||||||
|
for _, v := range los {
|
||||||
|
if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 {
|
||||||
|
BConfig.Log.Outputs[logType2Config[0]] = logType2Config[1]
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//init log
|
||||||
|
logs.Reset()
|
||||||
|
for adaptor, config := range BConfig.Log.Outputs {
|
||||||
|
err := logs.SetLogger(adaptor, config)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, config, err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logs.SetLogFuncCall(BConfig.Log.FileLineNum)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignSingleConfig(p interface{}, ac config.Configer) {
|
||||||
|
pt := reflect.TypeOf(p)
|
||||||
|
if pt.Kind() != reflect.Ptr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pt = pt.Elem()
|
||||||
|
if pt.Kind() != reflect.Struct {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pv := reflect.ValueOf(p).Elem()
|
||||||
|
|
||||||
|
for i := 0; i < pt.NumField(); i++ {
|
||||||
|
pf := pv.Field(i)
|
||||||
|
if !pf.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := pt.Field(i).Name
|
||||||
|
switch pf.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
pf.SetString(ac.DefaultString(name, pf.String()))
|
||||||
|
case reflect.Int, reflect.Int64:
|
||||||
|
pf.SetInt(ac.DefaultInt64(name, pf.Int()))
|
||||||
|
case reflect.Bool:
|
||||||
|
pf.SetBool(ac.DefaultBool(name, pf.Bool()))
|
||||||
|
case reflect.Struct:
|
||||||
|
default:
|
||||||
|
//do nothing here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadAppConfig allow developer to apply a config file
|
||||||
|
func LoadAppConfig(adapterName, configPath string) error {
|
||||||
|
absConfigPath, err := filepath.Abs(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.FileExists(absConfigPath) {
|
||||||
|
return fmt.Errorf("the target config file: %s don't exist", configPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
appConfigPath = absConfigPath
|
||||||
|
appConfigProvider = adapterName
|
||||||
|
|
||||||
|
return parseConfig(appConfigPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
type beegoAppConfig struct {
|
||||||
|
innerConfig config.Configer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) {
|
||||||
|
ac, err := config.NewConfig(appConfigProvider, appConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &beegoAppConfig{ac}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) Set(key, val string) error {
|
||||||
|
if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return b.innerConfig.Set(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) String(key string) string {
|
||||||
|
if v := b.innerConfig.String(BConfig.RunMode + "::" + key); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return b.innerConfig.String(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) Strings(key string) []string {
|
||||||
|
if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return b.innerConfig.Strings(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) Int(key string) (int, error) {
|
||||||
|
if v, err := b.innerConfig.Int(BConfig.RunMode + "::" + key); err == nil {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return b.innerConfig.Int(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) Int64(key string) (int64, error) {
|
||||||
|
if v, err := b.innerConfig.Int64(BConfig.RunMode + "::" + key); err == nil {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return b.innerConfig.Int64(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) Bool(key string) (bool, error) {
|
||||||
|
if v, err := b.innerConfig.Bool(BConfig.RunMode + "::" + key); err == nil {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return b.innerConfig.Bool(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) Float(key string) (float64, error) {
|
||||||
|
if v, err := b.innerConfig.Float(BConfig.RunMode + "::" + key); err == nil {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return b.innerConfig.Float(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string {
|
||||||
|
if v := b.String(key); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string {
|
||||||
|
if v := b.Strings(key); len(v) != 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int {
|
||||||
|
if v, err := b.Int(key); err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 {
|
||||||
|
if v, err := b.Int64(key); err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool {
|
||||||
|
if v, err := b.Bool(key); err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 {
|
||||||
|
if v, err := b.Float(key); 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)
|
||||||
|
}
|
|
@ -0,0 +1,661 @@
|
||||||
|
// 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"
|
||||||
|
"errors"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/context"
|
||||||
|
"github.com/astaxie/beego/context/param"
|
||||||
|
"github.com/astaxie/beego/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
//commonly used mime-types
|
||||||
|
const (
|
||||||
|
applicationJSON = "application/json"
|
||||||
|
applicationXML = "application/xml"
|
||||||
|
textXML = "text/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrAbort custom error when user stop request handler manually.
|
||||||
|
ErrAbort = errors.New("User stop run")
|
||||||
|
// GlobalControllerRouter store comments with controller. pkgpath+controller:comments
|
||||||
|
GlobalControllerRouter = make(map[string][]ControllerComments)
|
||||||
|
)
|
||||||
|
|
||||||
|
// ControllerComments store the comment for the controller method
|
||||||
|
type ControllerComments struct {
|
||||||
|
Method string
|
||||||
|
Router string
|
||||||
|
AllowHTTPMethods []string
|
||||||
|
Params []map[string]string
|
||||||
|
MethodParams []*param.MethodParam
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerCommentsSlice implements the sort interface
|
||||||
|
type ControllerCommentsSlice []ControllerComments
|
||||||
|
|
||||||
|
func (p ControllerCommentsSlice) Len() int { return len(p) }
|
||||||
|
func (p ControllerCommentsSlice) Less(i, j int) bool { return p[i].Router < p[j].Router }
|
||||||
|
func (p ControllerCommentsSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||||
|
|
||||||
|
// Controller defines some basic http request handler operations, such as
|
||||||
|
// http context, template and view, session and xsrf.
|
||||||
|
type Controller struct {
|
||||||
|
// context data
|
||||||
|
Ctx *context.Context
|
||||||
|
Data map[interface{}]interface{}
|
||||||
|
|
||||||
|
// route controller info
|
||||||
|
controllerName string
|
||||||
|
actionName string
|
||||||
|
methodMapping map[string]func() //method:routertree
|
||||||
|
gotofunc string
|
||||||
|
AppController interface{}
|
||||||
|
|
||||||
|
// template data
|
||||||
|
TplName string
|
||||||
|
ViewPath string
|
||||||
|
Layout string
|
||||||
|
LayoutSections map[string]string // the key is the section name and the value is the template name
|
||||||
|
TplPrefix string
|
||||||
|
TplExt string
|
||||||
|
EnableRender bool
|
||||||
|
|
||||||
|
// xsrf data
|
||||||
|
_xsrfToken string
|
||||||
|
XSRFExpire int
|
||||||
|
EnableXSRF bool
|
||||||
|
|
||||||
|
// session
|
||||||
|
CruSession session.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerInterface is an interface to uniform all controller handler.
|
||||||
|
type ControllerInterface interface {
|
||||||
|
Init(ct *context.Context, controllerName, actionName string, app interface{})
|
||||||
|
Prepare()
|
||||||
|
Get()
|
||||||
|
Post()
|
||||||
|
Delete()
|
||||||
|
Put()
|
||||||
|
Head()
|
||||||
|
Patch()
|
||||||
|
Options()
|
||||||
|
Finish()
|
||||||
|
Render() error
|
||||||
|
XSRFToken() string
|
||||||
|
CheckXSRFCookie() bool
|
||||||
|
HandlerFunc(fn string) bool
|
||||||
|
URLMapping()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init generates default values of controller operations.
|
||||||
|
func (c *Controller) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
|
||||||
|
c.Layout = ""
|
||||||
|
c.TplName = ""
|
||||||
|
c.controllerName = controllerName
|
||||||
|
c.actionName = actionName
|
||||||
|
c.Ctx = ctx
|
||||||
|
c.TplExt = "tpl"
|
||||||
|
c.AppController = app
|
||||||
|
c.EnableRender = true
|
||||||
|
c.EnableXSRF = true
|
||||||
|
c.Data = ctx.Input.Data()
|
||||||
|
c.methodMapping = make(map[string]func())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare runs after Init before request function execution.
|
||||||
|
func (c *Controller) Prepare() {}
|
||||||
|
|
||||||
|
// Finish runs after request function execution.
|
||||||
|
func (c *Controller) Finish() {}
|
||||||
|
|
||||||
|
// Get adds a request function to handle GET request.
|
||||||
|
func (c *Controller) Get() {
|
||||||
|
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post adds a request function to handle POST request.
|
||||||
|
func (c *Controller) Post() {
|
||||||
|
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete adds a request function to handle DELETE request.
|
||||||
|
func (c *Controller) Delete() {
|
||||||
|
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put adds a request function to handle PUT request.
|
||||||
|
func (c *Controller) Put() {
|
||||||
|
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head adds a request function to handle HEAD request.
|
||||||
|
func (c *Controller) Head() {
|
||||||
|
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch adds a request function to handle PATCH request.
|
||||||
|
func (c *Controller) Patch() {
|
||||||
|
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options adds a request function to handle OPTIONS request.
|
||||||
|
func (c *Controller) Options() {
|
||||||
|
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerFunc call function with the name
|
||||||
|
func (c *Controller) HandlerFunc(fnname string) bool {
|
||||||
|
if v, ok := c.methodMapping[fnname]; ok {
|
||||||
|
v()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLMapping register the internal Controller router.
|
||||||
|
func (c *Controller) URLMapping() {}
|
||||||
|
|
||||||
|
// Mapping the method to function
|
||||||
|
func (c *Controller) Mapping(method string, fn func()) {
|
||||||
|
c.methodMapping[method] = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render sends the response with rendered template bytes as text/html type.
|
||||||
|
func (c *Controller) Render() error {
|
||||||
|
if !c.EnableRender {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rb, err := c.RenderBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Ctx.ResponseWriter.Header().Get("Content-Type") == "" {
|
||||||
|
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Ctx.Output.Body(rb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderString returns the rendered template string. Do not send out response.
|
||||||
|
func (c *Controller) RenderString() (string, error) {
|
||||||
|
b, e := c.RenderBytes()
|
||||||
|
return string(b), e
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderBytes returns the bytes of rendered template string. Do not send out response.
|
||||||
|
func (c *Controller) RenderBytes() ([]byte, error) {
|
||||||
|
buf, err := c.renderTemplate()
|
||||||
|
//if the controller has set layout, then first get the tplName's content set the content to the layout
|
||||||
|
if err == nil && c.Layout != "" {
|
||||||
|
c.Data["LayoutContent"] = template.HTML(buf.String())
|
||||||
|
|
||||||
|
if c.LayoutSections != nil {
|
||||||
|
for sectionName, sectionTpl := range c.LayoutSections {
|
||||||
|
if sectionTpl == "" {
|
||||||
|
c.Data[sectionName] = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf.Reset()
|
||||||
|
err = ExecuteViewPathTemplate(&buf, sectionTpl, c.viewPath(), c.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.Data[sectionName] = template.HTML(buf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data)
|
||||||
|
}
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) renderTemplate() (bytes.Buffer, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if c.TplName == "" {
|
||||||
|
c.TplName = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
|
||||||
|
}
|
||||||
|
if c.TplPrefix != "" {
|
||||||
|
c.TplName = c.TplPrefix + c.TplName
|
||||||
|
}
|
||||||
|
if BConfig.RunMode == DEV {
|
||||||
|
buildFiles := []string{c.TplName}
|
||||||
|
if c.Layout != "" {
|
||||||
|
buildFiles = append(buildFiles, c.Layout)
|
||||||
|
if c.LayoutSections != nil {
|
||||||
|
for _, sectionTpl := range c.LayoutSections {
|
||||||
|
if sectionTpl == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buildFiles = append(buildFiles, sectionTpl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuildTemplate(c.viewPath(), buildFiles...)
|
||||||
|
}
|
||||||
|
return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) viewPath() string {
|
||||||
|
if c.ViewPath == "" {
|
||||||
|
return BConfig.WebConfig.ViewsPath
|
||||||
|
}
|
||||||
|
return c.ViewPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect sends the redirection response to url with status code.
|
||||||
|
func (c *Controller) Redirect(url string, code int) {
|
||||||
|
c.Ctx.Redirect(code, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abort 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 {
|
||||||
|
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) {
|
||||||
|
// first panic from ErrorMaps, it is user defined error functions.
|
||||||
|
if _, ok := ErrorMaps[body]; ok {
|
||||||
|
c.Ctx.Output.Status = status
|
||||||
|
panic(body)
|
||||||
|
}
|
||||||
|
// last panic user string
|
||||||
|
c.Ctx.ResponseWriter.WriteHeader(status)
|
||||||
|
c.Ctx.ResponseWriter.Write([]byte(body))
|
||||||
|
panic(ErrAbort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopRun makes panic of USERSTOPRUN error and go to recover function if defined.
|
||||||
|
func (c *Controller) StopRun() {
|
||||||
|
panic(ErrAbort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ...interface{}) string {
|
||||||
|
if len(endpoint) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if endpoint[0] == '.' {
|
||||||
|
return URLFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...)
|
||||||
|
}
|
||||||
|
return URLFor(endpoint, values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeJSON sends a json response with encoding charset.
|
||||||
|
func (c *Controller) ServeJSON(encoding ...bool) {
|
||||||
|
var (
|
||||||
|
hasIndent = true
|
||||||
|
hasEncoding = false
|
||||||
|
)
|
||||||
|
if BConfig.RunMode == PROD {
|
||||||
|
hasIndent = false
|
||||||
|
}
|
||||||
|
if len(encoding) > 0 && encoding[0] {
|
||||||
|
hasEncoding = true
|
||||||
|
}
|
||||||
|
c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeJSONP sends a jsonp response.
|
||||||
|
func (c *Controller) ServeJSONP() {
|
||||||
|
hasIndent := true
|
||||||
|
if BConfig.RunMode == PROD {
|
||||||
|
hasIndent = false
|
||||||
|
}
|
||||||
|
c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeXML sends xml response.
|
||||||
|
func (c *Controller) ServeXML() {
|
||||||
|
hasIndent := true
|
||||||
|
if BConfig.RunMode == PROD {
|
||||||
|
hasIndent = false
|
||||||
|
}
|
||||||
|
c.Ctx.Output.XML(c.Data["xml"], hasIndent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeFormatted serve Xml OR Json, depending on the value of the Accept header
|
||||||
|
func (c *Controller) ServeFormatted() {
|
||||||
|
accept := c.Ctx.Input.Header("Accept")
|
||||||
|
switch accept {
|
||||||
|
case applicationJSON:
|
||||||
|
c.ServeJSON()
|
||||||
|
case applicationXML, textXML:
|
||||||
|
c.ServeXML()
|
||||||
|
default:
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input returns the input data map from POST or PUT request body and query string.
|
||||||
|
func (c *Controller) Input() url.Values {
|
||||||
|
if c.Ctx.Request.Form == nil {
|
||||||
|
c.Ctx.Request.ParseForm()
|
||||||
|
}
|
||||||
|
return c.Ctx.Request.Form
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseForm maps input data map to obj struct.
|
||||||
|
func (c *Controller) ParseForm(obj interface{}) error {
|
||||||
|
return ParseForm(c.Input(), obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetString returns the input value by key string or the default value while it's present and input is blank
|
||||||
|
func (c *Controller) GetString(key string, def ...string) string {
|
||||||
|
if v := c.Ctx.Input.Query(key); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if len(def) > 0 {
|
||||||
|
return def[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, def ...[]string) []string {
|
||||||
|
var defv []string
|
||||||
|
if len(def) > 0 {
|
||||||
|
defv = def[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if f := c.Input(); f == nil {
|
||||||
|
return defv
|
||||||
|
} else if vs := f[key]; len(vs) > 0 {
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
return defv
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
return strconv.Atoi(strv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
i64, err := strconv.ParseInt(strv, 10, 8)
|
||||||
|
return int8(i64), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint8 return input as an uint8 or the default value while it's present and input is blank
|
||||||
|
func (c *Controller) GetUint8(key string, def ...uint8) (uint8, error) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
u64, err := strconv.ParseUint(strv, 10, 8)
|
||||||
|
return uint8(u64), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
i64, err := strconv.ParseInt(strv, 10, 16)
|
||||||
|
return int16(i64), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint16 returns input as an uint16 or the default value while it's present and input is blank
|
||||||
|
func (c *Controller) GetUint16(key string, def ...uint16) (uint16, error) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
u64, err := strconv.ParseUint(strv, 10, 16)
|
||||||
|
return uint16(u64), 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) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
i64, err := strconv.ParseInt(strv, 10, 32)
|
||||||
|
return int32(i64), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint32 returns input as an uint32 or the default value while it's present and input is blank
|
||||||
|
func (c *Controller) GetUint32(key string, def ...uint32) (uint32, error) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
u64, err := strconv.ParseUint(strv, 10, 32)
|
||||||
|
return uint32(u64), 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) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
return strconv.ParseInt(strv, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint64 returns input value as uint64 or the default value while it's present and input is blank.
|
||||||
|
func (c *Controller) GetUint64(key string, def ...uint64) (uint64, error) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
return strconv.ParseUint(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) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
strv := c.Ctx.Input.Query(key)
|
||||||
|
if len(strv) == 0 && len(def) > 0 {
|
||||||
|
return def[0], nil
|
||||||
|
}
|
||||||
|
return strconv.ParseFloat(strv, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFile returns the file data in file upload field named as key.
|
||||||
|
// it returns the first one of multi-uploaded files.
|
||||||
|
func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) {
|
||||||
|
return c.Ctx.Request.FormFile(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if files, ok := c.Ctx.Request.MultipartForm.File[key]; 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 {
|
||||||
|
file, _, err := c.Ctx.Request.FormFile(fromfile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
f, err := os.OpenFile(tofile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
io.Copy(f, file)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartSession starts session and load old session data info this controller.
|
||||||
|
func (c *Controller) StartSession() session.Store {
|
||||||
|
if c.CruSession == nil {
|
||||||
|
c.CruSession = c.Ctx.Input.CruSession
|
||||||
|
}
|
||||||
|
return c.CruSession
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSession puts value into session.
|
||||||
|
func (c *Controller) SetSession(name interface{}, value interface{}) {
|
||||||
|
if c.CruSession == nil {
|
||||||
|
c.StartSession()
|
||||||
|
}
|
||||||
|
c.CruSession.Set(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSession gets value from session.
|
||||||
|
func (c *Controller) GetSession(name interface{}) interface{} {
|
||||||
|
if c.CruSession == nil {
|
||||||
|
c.StartSession()
|
||||||
|
}
|
||||||
|
return c.CruSession.Get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelSession removes value from session.
|
||||||
|
func (c *Controller) DelSession(name interface{}) {
|
||||||
|
if c.CruSession == nil {
|
||||||
|
c.StartSession()
|
||||||
|
}
|
||||||
|
c.CruSession.Delete(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionRegenerateID regenerates session id for this session.
|
||||||
|
// the session data have no changes.
|
||||||
|
func (c *Controller) SessionRegenerateID() {
|
||||||
|
if c.CruSession != nil {
|
||||||
|
c.CruSession.SessionRelease(c.Ctx.ResponseWriter)
|
||||||
|
}
|
||||||
|
c.CruSession = GlobalSessions.SessionRegenerateID(c.Ctx.ResponseWriter, c.Ctx.Request)
|
||||||
|
c.Ctx.Input.CruSession = c.CruSession
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroySession cleans session data and session cookie.
|
||||||
|
func (c *Controller) DestroySession() {
|
||||||
|
c.Ctx.Input.CruSession.Flush()
|
||||||
|
c.Ctx.Input.CruSession = nil
|
||||||
|
GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAjax returns this request is ajax or not.
|
||||||
|
func (c *Controller) IsAjax() bool {
|
||||||
|
return c.Ctx.Input.IsAjax()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSecureCookie returns decoded cookie value from encoded browser cookie values.
|
||||||
|
func (c *Controller) GetSecureCookie(Secret, key string) (string, bool) {
|
||||||
|
return c.Ctx.GetSecureCookie(Secret, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSecureCookie puts value into cookie after encoded the value.
|
||||||
|
func (c *Controller) SetSecureCookie(Secret, name, value string, others ...interface{}) {
|
||||||
|
c.Ctx.SetSecureCookie(Secret, name, value, others...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XSRFToken creates a CSRF token string and returns.
|
||||||
|
func (c *Controller) XSRFToken() string {
|
||||||
|
if c._xsrfToken == "" {
|
||||||
|
expire := int64(BConfig.WebConfig.XSRFExpire)
|
||||||
|
if c.XSRFExpire > 0 {
|
||||||
|
expire = int64(c.XSRFExpire)
|
||||||
|
}
|
||||||
|
c._xsrfToken = c.Ctx.XSRFToken(BConfig.WebConfig.XSRFKey, expire)
|
||||||
|
}
|
||||||
|
return c._xsrfToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckXSRFCookie checks xsrf token in this request is valid or not.
|
||||||
|
// the token can provided in request header "X-Xsrftoken" and "X-CsrfToken"
|
||||||
|
// or in form field value named as "_xsrf".
|
||||||
|
func (c *Controller) CheckXSRFCookie() bool {
|
||||||
|
if !c.EnableXSRF {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return c.Ctx.CheckXSRFCookie()
|
||||||
|
}
|
||||||
|
|
||||||
|
// XSRFFormHTML writes an input field contains xsrf token value.
|
||||||
|
func (c *Controller) XSRFFormHTML() string {
|
||||||
|
return `<input type="hidden" name="_xsrf" value="` +
|
||||||
|
c.XSRFToken() + `" />`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetControllerAndAction gets the executing controller name and action name.
|
||||||
|
func (c *Controller) GetControllerAndAction() (string, string) {
|
||||||
|
return c.controllerName, c.actionName
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
Package beego provide a MVC framework
|
||||||
|
beego: an open-source, high-performance, modular, full-stack web framework
|
||||||
|
|
||||||
|
It is used for rapid development of RESTful APIs, web apps and backend services in Go.
|
||||||
|
beego is inspired by Tornado, Sinatra and Flask with the added benefit of some Go-specific features such as interfaces and struct embedding.
|
||||||
|
|
||||||
|
package main
|
||||||
|
import "github.com/astaxie/beego"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
beego.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
more information: http://beego.me
|
||||||
|
*/
|
||||||
|
package beego
|
|
@ -0,0 +1,476 @@
|
||||||
|
// 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 := map[string]string{
|
||||||
|
"AppError": fmt.Sprintf("%s:%v", BConfig.AppName, err),
|
||||||
|
"RequestMethod": ctx.Input.Method(),
|
||||||
|
"RequestURL": ctx.Input.URI(),
|
||||||
|
"RemoteAddr": ctx.Input.IP(),
|
||||||
|
"Stack": stack,
|
||||||
|
"BeegoVersion": VERSION,
|
||||||
|
"GoVersion": runtime.Version(),
|
||||||
|
}
|
||||||
|
if ctx.Output.Status != 0 {
|
||||||
|
ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorMaps holds map of http handlers for each error string.
|
||||||
|
// there is 10 kinds default error(40x and 50x)
|
||||||
|
var ErrorMaps = make(map[string]*errorInfo, 10)
|
||||||
|
|
||||||
|
// show 401 unauthorized error.
|
||||||
|
func unauthorized(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
401,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 402 Payment Required
|
||||||
|
func paymentRequired(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
402,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 403 forbidden error.
|
||||||
|
func forbidden(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
403,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 422 missing xsrf token
|
||||||
|
func missingxsrf(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
422,
|
||||||
|
"<br>The page you have requested is forbidden."+
|
||||||
|
"<br>Perhaps you are here because:"+
|
||||||
|
"<br><br><ul>"+
|
||||||
|
"<br>'_xsrf' argument missing from POST"+
|
||||||
|
"</ul>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 417 invalid xsrf token
|
||||||
|
func invalidxsrf(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
417,
|
||||||
|
"<br>The page you have requested is forbidden."+
|
||||||
|
"<br>Perhaps you are here because:"+
|
||||||
|
"<br><br><ul>"+
|
||||||
|
"<br>expected XSRF not found"+
|
||||||
|
"</ul>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 404 not found error.
|
||||||
|
func notFound(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
404,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 405 Method Not Allowed
|
||||||
|
func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
405,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 500 internal server error.
|
||||||
|
func internalServerError(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
500,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 501 Not Implemented.
|
||||||
|
func notImplemented(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
501,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 502 Bad Gateway.
|
||||||
|
func badGateway(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
502,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 503 service unavailable error.
|
||||||
|
func serviceUnavailable(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
503,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show 504 Gateway Timeout.
|
||||||
|
func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
responseError(rw, r,
|
||||||
|
504,
|
||||||
|
"<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>",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
|
||||||
|
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"Title": http.StatusText(errCode),
|
||||||
|
"BeegoVersion": VERSION,
|
||||||
|
"Content": template.HTML(errContent),
|
||||||
|
}
|
||||||
|
t.Execute(rw, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
ErrorMaps[code] = &errorInfo{
|
||||||
|
errorType: errorTypeHandler,
|
||||||
|
handler: h,
|
||||||
|
method: code,
|
||||||
|
}
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorController registers ControllerInterface to each http err code string.
|
||||||
|
// usage:
|
||||||
|
// beego.ErrorController(&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++ {
|
||||||
|
methodName := rt.Method(i).Name
|
||||||
|
if !utils.InSlice(methodName, exceptMethod) && strings.HasPrefix(methodName, "Error") {
|
||||||
|
errName := strings.TrimPrefix(methodName, "Error")
|
||||||
|
ErrorMaps[errName] = &errorInfo{
|
||||||
|
errorType: errorTypeController,
|
||||||
|
controllerType: ct,
|
||||||
|
method: methodName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exception Write HttpStatus with errCode and Exec error handler if exist.
|
||||||
|
func Exception(errCode uint64, ctx *context.Context) {
|
||||||
|
exception(strconv.FormatUint(errCode, 10), ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show error string as simple text message.
|
||||||
|
// if error string is empty, show 503 or 500 error as default.
|
||||||
|
func exception(errCode string, ctx *context.Context) {
|
||||||
|
atoi := func(code string) int {
|
||||||
|
v, err := strconv.Atoi(code)
|
||||||
|
if err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if ctx.Output.Status == 0 {
|
||||||
|
return 503
|
||||||
|
}
|
||||||
|
return ctx.Output.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ec := range []string{errCode, "503", "500"} {
|
||||||
|
if h, ok := ErrorMaps[ec]; ok {
|
||||||
|
executeError(h, ctx, atoi(ec))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if 50x error has been removed from errorMap
|
||||||
|
ctx.ResponseWriter.WriteHeader(atoi(errCode))
|
||||||
|
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()
|
||||||
|
|
||||||
|
method := vc.MethodByName(err.method)
|
||||||
|
method.Call([]reflect.Value{})
|
||||||
|
|
||||||
|
//render template
|
||||||
|
if BConfig.WebConfig.AutoRender {
|
||||||
|
if err := execController.Render(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish all runrouter. release resource
|
||||||
|
execController.Finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// 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 "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
|
||||||
|
resetParams bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, ctx *context.Context) bool {
|
||||||
|
isOk := f.tree.Match(url, ctx)
|
||||||
|
if isOk != nil {
|
||||||
|
if b, ok := isOk.(bool); ok {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2014 beego Author. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package beego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlashData is a tools to maintain data when using across request.
|
||||||
|
type FlashData struct {
|
||||||
|
Data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFlash return a new empty FlashData struct.
|
||||||
|
func NewFlash() *FlashData {
|
||||||
|
return &FlashData{
|
||||||
|
Data: make(map[string]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
fd.Data["notice"] = msg
|
||||||
|
} else {
|
||||||
|
fd.Data["notice"] = fmt.Sprintf(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning writes warning message to flash.
|
||||||
|
func (fd *FlashData) Warning(msg string, args ...interface{}) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
fd.Data["warning"] = msg
|
||||||
|
} else {
|
||||||
|
fd.Data["warning"] = fmt.Sprintf(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error writes error message to flash.
|
||||||
|
func (fd *FlashData) Error(msg string, args ...interface{}) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
fd.Data["error"] = msg
|
||||||
|
} else {
|
||||||
|
fd.Data["error"] = fmt.Sprintf(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store does the saving operation of flash data.
|
||||||
|
// the data are encoded and saved in cookie.
|
||||||
|
func (fd *FlashData) Store(c *Controller) {
|
||||||
|
c.Data["flash"] = fd.Data
|
||||||
|
var flashValue string
|
||||||
|
for key, value := range fd.Data {
|
||||||
|
flashValue += "\x00" + key + "\x23" + BConfig.WebConfig.FlashSeparator + "\x23" + value + "\x00"
|
||||||
|
}
|
||||||
|
c.Ctx.SetCookie(BConfig.WebConfig.FlashName, url.QueryEscape(flashValue), 0, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFromRequest parsed flash data from encoded values in cookie.
|
||||||
|
func ReadFromRequest(c *Controller) *FlashData {
|
||||||
|
flash := NewFlash()
|
||||||
|
if cookie, err := c.Ctx.Request.Cookie(BConfig.WebConfig.FlashName); err == nil {
|
||||||
|
v, _ := url.QueryUnescape(cookie.Value)
|
||||||
|
vals := strings.Split(v, "\x00")
|
||||||
|
for _, v := range vals {
|
||||||
|
if len(v) > 0 {
|
||||||
|
kv := strings.Split(v, "\x23"+BConfig.WebConfig.FlashSeparator+"\x23")
|
||||||
|
if len(kv) == 2 {
|
||||||
|
flash.Data[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//read one time then delete it
|
||||||
|
c.Ctx.SetCookie(BConfig.WebConfig.FlashName, "", -1, "/")
|
||||||
|
}
|
||||||
|
c.Data["flash"] = flash.Data
|
||||||
|
return flash
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
package beego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/context"
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
"github.com/astaxie/beego/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
func registerMime() error {
|
||||||
|
for k, v := range mimemaps {
|
||||||
|
mime.AddExtensionType(k, v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// register default error http handlers, 404,401,403,500 and 503.
|
||||||
|
func registerDefaultErrorHandler() error {
|
||||||
|
m := map[string]func(http.ResponseWriter, *http.Request){
|
||||||
|
"401": unauthorized,
|
||||||
|
"402": paymentRequired,
|
||||||
|
"403": forbidden,
|
||||||
|
"404": notFound,
|
||||||
|
"405": methodNotAllowed,
|
||||||
|
"500": internalServerError,
|
||||||
|
"501": notImplemented,
|
||||||
|
"502": badGateway,
|
||||||
|
"503": serviceUnavailable,
|
||||||
|
"504": gatewayTimeout,
|
||||||
|
"417": invalidxsrf,
|
||||||
|
"422": missingxsrf,
|
||||||
|
}
|
||||||
|
for e, h := range m {
|
||||||
|
if _, ok := ErrorMaps[e]; !ok {
|
||||||
|
ErrorHandler(e, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerSession() error {
|
||||||
|
if BConfig.WebConfig.Session.SessionOn {
|
||||||
|
var err error
|
||||||
|
sessionConfig := AppConfig.String("sessionConfig")
|
||||||
|
conf := new(session.ManagerConfig)
|
||||||
|
if sessionConfig == "" {
|
||||||
|
conf.CookieName = BConfig.WebConfig.Session.SessionName
|
||||||
|
conf.EnableSetCookie = BConfig.WebConfig.Session.SessionAutoSetCookie
|
||||||
|
conf.Gclifetime = BConfig.WebConfig.Session.SessionGCMaxLifetime
|
||||||
|
conf.Secure = BConfig.Listen.EnableHTTPS
|
||||||
|
conf.CookieLifeTime = BConfig.WebConfig.Session.SessionCookieLifeTime
|
||||||
|
conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig)
|
||||||
|
conf.DisableHTTPOnly = BConfig.WebConfig.Session.SessionDisableHTTPOnly
|
||||||
|
conf.Domain = BConfig.WebConfig.Session.SessionDomain
|
||||||
|
conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
|
||||||
|
conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
|
||||||
|
conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
|
||||||
|
} else {
|
||||||
|
if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if GlobalSessions, err = session.NewManager(BConfig.WebConfig.Session.SessionProvider, conf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go GlobalSessions.GC()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerTemplate() error {
|
||||||
|
defer lockViewPaths()
|
||||||
|
if err := AddViewPath(BConfig.WebConfig.ViewsPath); err != nil {
|
||||||
|
if BConfig.RunMode == DEV {
|
||||||
|
logs.Warn(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerAdmin() error {
|
||||||
|
if BConfig.Listen.EnableAdmin {
|
||||||
|
go beeAdminApp.Run()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerGzip() error {
|
||||||
|
if BConfig.EnableGzip {
|
||||||
|
context.InitGzip(
|
||||||
|
AppConfig.DefaultInt("gzipMinLength", -1),
|
||||||
|
AppConfig.DefaultInt("gzipCompressLevel", -1),
|
||||||
|
AppConfig.DefaultStrings("includedMethods", []string{"GET"}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright 2014 beego Author. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package beego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log levels to control the logging output.
|
||||||
|
const (
|
||||||
|
LevelEmergency = iota
|
||||||
|
LevelAlert
|
||||||
|
LevelCritical
|
||||||
|
LevelError
|
||||||
|
LevelWarning
|
||||||
|
LevelNotice
|
||||||
|
LevelInformational
|
||||||
|
LevelDebug
|
||||||
|
)
|
||||||
|
|
||||||
|
// BeeLogger references the used application logger.
|
||||||
|
var BeeLogger = logs.GetBeeLogger()
|
||||||
|
|
||||||
|
// SetLevel sets the global log level used by the simple logger.
|
||||||
|
func SetLevel(l int) {
|
||||||
|
logs.SetLevel(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLogFuncCall set the CallDepth, default is 3
|
||||||
|
func SetLogFuncCall(b bool) {
|
||||||
|
logs.SetLogFuncCall(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLogger sets a new logger.
|
||||||
|
func SetLogger(adaptername string, config string) error {
|
||||||
|
return logs.SetLogger(adaptername, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emergency logs a message at emergency level.
|
||||||
|
func Emergency(v ...interface{}) {
|
||||||
|
logs.Emergency(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert logs a message at alert level.
|
||||||
|
func Alert(v ...interface{}) {
|
||||||
|
logs.Alert(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Critical logs a message at critical level.
|
||||||
|
func Critical(v ...interface{}) {
|
||||||
|
logs.Critical(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs a message at error level.
|
||||||
|
func Error(v ...interface{}) {
|
||||||
|
logs.Error(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning logs a message at warning level.
|
||||||
|
func Warning(v ...interface{}) {
|
||||||
|
logs.Warning(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn compatibility alias for Warning()
|
||||||
|
func Warn(v ...interface{}) {
|
||||||
|
logs.Warn(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notice logs a message at notice level.
|
||||||
|
func Notice(v ...interface{}) {
|
||||||
|
logs.Notice(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Informational logs a message at info level.
|
||||||
|
func Informational(v ...interface{}) {
|
||||||
|
logs.Informational(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info compatibility alias for Warning()
|
||||||
|
func Info(v ...interface{}) {
|
||||||
|
logs.Info(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug logs a message at debug level.
|
||||||
|
func Debug(v ...interface{}) {
|
||||||
|
logs.Debug(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace logs a message at trace level.
|
||||||
|
// compatibility alias for Warning()
|
||||||
|
func Trace(v ...interface{}) {
|
||||||
|
logs.Trace(generateFmtStr(len(v)), v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateFmtStr(n int) string {
|
||||||
|
return strings.Repeat("%v ", n)
|
||||||
|
}
|
|
@ -0,0 +1,556 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
var mimemaps = map[string]string{
|
||||||
|
".3dm": "x-world/x-3dmf",
|
||||||
|
".3dmf": "x-world/x-3dmf",
|
||||||
|
".7z": "application/x-7z-compressed",
|
||||||
|
".a": "application/octet-stream",
|
||||||
|
".aab": "application/x-authorware-bin",
|
||||||
|
".aam": "application/x-authorware-map",
|
||||||
|
".aas": "application/x-authorware-seg",
|
||||||
|
".abc": "text/vndabc",
|
||||||
|
".ace": "application/x-ace-compressed",
|
||||||
|
".acgi": "text/html",
|
||||||
|
".afl": "video/animaflex",
|
||||||
|
".ai": "application/postscript",
|
||||||
|
".aif": "audio/aiff",
|
||||||
|
".aifc": "audio/aiff",
|
||||||
|
".aiff": "audio/aiff",
|
||||||
|
".aim": "application/x-aim",
|
||||||
|
".aip": "text/x-audiosoft-intra",
|
||||||
|
".alz": "application/x-alz-compressed",
|
||||||
|
".ani": "application/x-navi-animation",
|
||||||
|
".aos": "application/x-nokia-9000-communicator-add-on-software",
|
||||||
|
".aps": "application/mime",
|
||||||
|
".apk": "application/vnd.android.package-archive",
|
||||||
|
".arc": "application/x-arc-compressed",
|
||||||
|
".arj": "application/arj",
|
||||||
|
".art": "image/x-jg",
|
||||||
|
".asf": "video/x-ms-asf",
|
||||||
|
".asm": "text/x-asm",
|
||||||
|
".asp": "text/asp",
|
||||||
|
".asx": "application/x-mplayer2",
|
||||||
|
".au": "audio/basic",
|
||||||
|
".avi": "video/x-msvideo",
|
||||||
|
".avs": "video/avs-video",
|
||||||
|
".bcpio": "application/x-bcpio",
|
||||||
|
".bin": "application/mac-binary",
|
||||||
|
".bmp": "image/bmp",
|
||||||
|
".boo": "application/book",
|
||||||
|
".book": "application/book",
|
||||||
|
".boz": "application/x-bzip2",
|
||||||
|
".bsh": "application/x-bsh",
|
||||||
|
".bz2": "application/x-bzip2",
|
||||||
|
".bz": "application/x-bzip",
|
||||||
|
".c++": "text/plain",
|
||||||
|
".c": "text/x-c",
|
||||||
|
".cab": "application/vnd.ms-cab-compressed",
|
||||||
|
".cat": "application/vndms-pkiseccat",
|
||||||
|
".cc": "text/x-c",
|
||||||
|
".ccad": "application/clariscad",
|
||||||
|
".cco": "application/x-cocoa",
|
||||||
|
".cdf": "application/cdf",
|
||||||
|
".cer": "application/pkix-cert",
|
||||||
|
".cha": "application/x-chat",
|
||||||
|
".chat": "application/x-chat",
|
||||||
|
".chrt": "application/vnd.kde.kchart",
|
||||||
|
".class": "application/java",
|
||||||
|
".com": "text/plain",
|
||||||
|
".conf": "text/plain",
|
||||||
|
".cpio": "application/x-cpio",
|
||||||
|
".cpp": "text/x-c",
|
||||||
|
".cpt": "application/mac-compactpro",
|
||||||
|
".crl": "application/pkcs-crl",
|
||||||
|
".crt": "application/pkix-cert",
|
||||||
|
".crx": "application/x-chrome-extension",
|
||||||
|
".csh": "text/x-scriptcsh",
|
||||||
|
".css": "text/css",
|
||||||
|
".csv": "text/csv",
|
||||||
|
".cxx": "text/plain",
|
||||||
|
".dar": "application/x-dar",
|
||||||
|
".dcr": "application/x-director",
|
||||||
|
".deb": "application/x-debian-package",
|
||||||
|
".deepv": "application/x-deepv",
|
||||||
|
".def": "text/plain",
|
||||||
|
".der": "application/x-x509-ca-cert",
|
||||||
|
".dif": "video/x-dv",
|
||||||
|
".dir": "application/x-director",
|
||||||
|
".divx": "video/divx",
|
||||||
|
".dl": "video/dl",
|
||||||
|
".dmg": "application/x-apple-diskimage",
|
||||||
|
".doc": "application/msword",
|
||||||
|
".dot": "application/msword",
|
||||||
|
".dp": "application/commonground",
|
||||||
|
".drw": "application/drafting",
|
||||||
|
".dump": "application/octet-stream",
|
||||||
|
".dv": "video/x-dv",
|
||||||
|
".dvi": "application/x-dvi",
|
||||||
|
".dwf": "drawing/x-dwf=(old)",
|
||||||
|
".dwg": "application/acad",
|
||||||
|
".dxf": "application/dxf",
|
||||||
|
".dxr": "application/x-director",
|
||||||
|
".el": "text/x-scriptelisp",
|
||||||
|
".elc": "application/x-bytecodeelisp=(compiled=elisp)",
|
||||||
|
".eml": "message/rfc822",
|
||||||
|
".env": "application/x-envoy",
|
||||||
|
".eps": "application/postscript",
|
||||||
|
".es": "application/x-esrehber",
|
||||||
|
".etx": "text/x-setext",
|
||||||
|
".evy": "application/envoy",
|
||||||
|
".exe": "application/octet-stream",
|
||||||
|
".f77": "text/x-fortran",
|
||||||
|
".f90": "text/x-fortran",
|
||||||
|
".f": "text/x-fortran",
|
||||||
|
".fdf": "application/vndfdf",
|
||||||
|
".fif": "application/fractals",
|
||||||
|
".fli": "video/fli",
|
||||||
|
".flo": "image/florian",
|
||||||
|
".flv": "video/x-flv",
|
||||||
|
".flx": "text/vndfmiflexstor",
|
||||||
|
".fmf": "video/x-atomic3d-feature",
|
||||||
|
".for": "text/x-fortran",
|
||||||
|
".fpx": "image/vndfpx",
|
||||||
|
".frl": "application/freeloader",
|
||||||
|
".funk": "audio/make",
|
||||||
|
".g3": "image/g3fax",
|
||||||
|
".g": "text/plain",
|
||||||
|
".gif": "image/gif",
|
||||||
|
".gl": "video/gl",
|
||||||
|
".gsd": "audio/x-gsm",
|
||||||
|
".gsm": "audio/x-gsm",
|
||||||
|
".gsp": "application/x-gsp",
|
||||||
|
".gss": "application/x-gss",
|
||||||
|
".gtar": "application/x-gtar",
|
||||||
|
".gz": "application/x-compressed",
|
||||||
|
".gzip": "application/x-gzip",
|
||||||
|
".h": "text/x-h",
|
||||||
|
".hdf": "application/x-hdf",
|
||||||
|
".help": "application/x-helpfile",
|
||||||
|
".hgl": "application/vndhp-hpgl",
|
||||||
|
".hh": "text/x-h",
|
||||||
|
".hlb": "text/x-script",
|
||||||
|
".hlp": "application/hlp",
|
||||||
|
".hpg": "application/vndhp-hpgl",
|
||||||
|
".hpgl": "application/vndhp-hpgl",
|
||||||
|
".hqx": "application/binhex",
|
||||||
|
".hta": "application/hta",
|
||||||
|
".htc": "text/x-component",
|
||||||
|
".htm": "text/html",
|
||||||
|
".html": "text/html",
|
||||||
|
".htmls": "text/html",
|
||||||
|
".htt": "text/webviewhtml",
|
||||||
|
".htx": "text/html",
|
||||||
|
".ice": "x-conference/x-cooltalk",
|
||||||
|
".ico": "image/x-icon",
|
||||||
|
".ics": "text/calendar",
|
||||||
|
".icz": "text/calendar",
|
||||||
|
".idc": "text/plain",
|
||||||
|
".ief": "image/ief",
|
||||||
|
".iefs": "image/ief",
|
||||||
|
".iges": "application/iges",
|
||||||
|
".igs": "application/iges",
|
||||||
|
".ima": "application/x-ima",
|
||||||
|
".imap": "application/x-httpd-imap",
|
||||||
|
".inf": "application/inf",
|
||||||
|
".ins": "application/x-internett-signup",
|
||||||
|
".ip": "application/x-ip2",
|
||||||
|
".isu": "video/x-isvideo",
|
||||||
|
".it": "audio/it",
|
||||||
|
".iv": "application/x-inventor",
|
||||||
|
".ivr": "i-world/i-vrml",
|
||||||
|
".ivy": "application/x-livescreen",
|
||||||
|
".jam": "audio/x-jam",
|
||||||
|
".jav": "text/x-java-source",
|
||||||
|
".java": "text/x-java-source",
|
||||||
|
".jcm": "application/x-java-commerce",
|
||||||
|
".jfif-tbnl": "image/jpeg",
|
||||||
|
".jfif": "image/jpeg",
|
||||||
|
".jnlp": "application/x-java-jnlp-file",
|
||||||
|
".jpe": "image/jpeg",
|
||||||
|
".jpeg": "image/jpeg",
|
||||||
|
".jpg": "image/jpeg",
|
||||||
|
".jps": "image/x-jps",
|
||||||
|
".js": "application/javascript",
|
||||||
|
".json": "application/json",
|
||||||
|
".jut": "image/jutvision",
|
||||||
|
".kar": "audio/midi",
|
||||||
|
".karbon": "application/vnd.kde.karbon",
|
||||||
|
".kfo": "application/vnd.kde.kformula",
|
||||||
|
".flw": "application/vnd.kde.kivio",
|
||||||
|
".kml": "application/vnd.google-earth.kml+xml",
|
||||||
|
".kmz": "application/vnd.google-earth.kmz",
|
||||||
|
".kon": "application/vnd.kde.kontour",
|
||||||
|
".kpr": "application/vnd.kde.kpresenter",
|
||||||
|
".kpt": "application/vnd.kde.kpresenter",
|
||||||
|
".ksp": "application/vnd.kde.kspread",
|
||||||
|
".kwd": "application/vnd.kde.kword",
|
||||||
|
".kwt": "application/vnd.kde.kword",
|
||||||
|
".ksh": "text/x-scriptksh",
|
||||||
|
".la": "audio/nspaudio",
|
||||||
|
".lam": "audio/x-liveaudio",
|
||||||
|
".latex": "application/x-latex",
|
||||||
|
".lha": "application/lha",
|
||||||
|
".lhx": "application/octet-stream",
|
||||||
|
".list": "text/plain",
|
||||||
|
".lma": "audio/nspaudio",
|
||||||
|
".log": "text/plain",
|
||||||
|
".lsp": "text/x-scriptlisp",
|
||||||
|
".lst": "text/plain",
|
||||||
|
".lsx": "text/x-la-asf",
|
||||||
|
".ltx": "application/x-latex",
|
||||||
|
".lzh": "application/octet-stream",
|
||||||
|
".lzx": "application/lzx",
|
||||||
|
".m1v": "video/mpeg",
|
||||||
|
".m2a": "audio/mpeg",
|
||||||
|
".m2v": "video/mpeg",
|
||||||
|
".m3u": "audio/x-mpegurl",
|
||||||
|
".m": "text/x-m",
|
||||||
|
".man": "application/x-troff-man",
|
||||||
|
".manifest": "text/cache-manifest",
|
||||||
|
".map": "application/x-navimap",
|
||||||
|
".mar": "text/plain",
|
||||||
|
".mbd": "application/mbedlet",
|
||||||
|
".mc$": "application/x-magic-cap-package-10",
|
||||||
|
".mcd": "application/mcad",
|
||||||
|
".mcf": "text/mcf",
|
||||||
|
".mcp": "application/netmc",
|
||||||
|
".me": "application/x-troff-me",
|
||||||
|
".mht": "message/rfc822",
|
||||||
|
".mhtml": "message/rfc822",
|
||||||
|
".mid": "application/x-midi",
|
||||||
|
".midi": "application/x-midi",
|
||||||
|
".mif": "application/x-frame",
|
||||||
|
".mime": "message/rfc822",
|
||||||
|
".mjf": "audio/x-vndaudioexplosionmjuicemediafile",
|
||||||
|
".mjpg": "video/x-motion-jpeg",
|
||||||
|
".mm": "application/base64",
|
||||||
|
".mme": "application/base64",
|
||||||
|
".mod": "audio/mod",
|
||||||
|
".moov": "video/quicktime",
|
||||||
|
".mov": "video/quicktime",
|
||||||
|
".movie": "video/x-sgi-movie",
|
||||||
|
".mp2": "audio/mpeg",
|
||||||
|
".mp3": "audio/mpeg3",
|
||||||
|
".mp4": "video/mp4",
|
||||||
|
".mpa": "audio/mpeg",
|
||||||
|
".mpc": "application/x-project",
|
||||||
|
".mpe": "video/mpeg",
|
||||||
|
".mpeg": "video/mpeg",
|
||||||
|
".mpg": "video/mpeg",
|
||||||
|
".mpga": "audio/mpeg",
|
||||||
|
".mpp": "application/vndms-project",
|
||||||
|
".mpt": "application/x-project",
|
||||||
|
".mpv": "application/x-project",
|
||||||
|
".mpx": "application/x-project",
|
||||||
|
".mrc": "application/marc",
|
||||||
|
".ms": "application/x-troff-ms",
|
||||||
|
".mv": "video/x-sgi-movie",
|
||||||
|
".my": "audio/make",
|
||||||
|
".mzz": "application/x-vndaudioexplosionmzz",
|
||||||
|
".nap": "image/naplps",
|
||||||
|
".naplps": "image/naplps",
|
||||||
|
".nc": "application/x-netcdf",
|
||||||
|
".ncm": "application/vndnokiaconfiguration-message",
|
||||||
|
".nif": "image/x-niff",
|
||||||
|
".niff": "image/x-niff",
|
||||||
|
".nix": "application/x-mix-transfer",
|
||||||
|
".nsc": "application/x-conference",
|
||||||
|
".nvd": "application/x-navidoc",
|
||||||
|
".o": "application/octet-stream",
|
||||||
|
".oda": "application/oda",
|
||||||
|
".odb": "application/vnd.oasis.opendocument.database",
|
||||||
|
".odc": "application/vnd.oasis.opendocument.chart",
|
||||||
|
".odf": "application/vnd.oasis.opendocument.formula",
|
||||||
|
".odg": "application/vnd.oasis.opendocument.graphics",
|
||||||
|
".odi": "application/vnd.oasis.opendocument.image",
|
||||||
|
".odm": "application/vnd.oasis.opendocument.text-master",
|
||||||
|
".odp": "application/vnd.oasis.opendocument.presentation",
|
||||||
|
".ods": "application/vnd.oasis.opendocument.spreadsheet",
|
||||||
|
".odt": "application/vnd.oasis.opendocument.text",
|
||||||
|
".oga": "audio/ogg",
|
||||||
|
".ogg": "audio/ogg",
|
||||||
|
".ogv": "video/ogg",
|
||||||
|
".omc": "application/x-omc",
|
||||||
|
".omcd": "application/x-omcdatamaker",
|
||||||
|
".omcr": "application/x-omcregerator",
|
||||||
|
".otc": "application/vnd.oasis.opendocument.chart-template",
|
||||||
|
".otf": "application/vnd.oasis.opendocument.formula-template",
|
||||||
|
".otg": "application/vnd.oasis.opendocument.graphics-template",
|
||||||
|
".oth": "application/vnd.oasis.opendocument.text-web",
|
||||||
|
".oti": "application/vnd.oasis.opendocument.image-template",
|
||||||
|
".otm": "application/vnd.oasis.opendocument.text-master",
|
||||||
|
".otp": "application/vnd.oasis.opendocument.presentation-template",
|
||||||
|
".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
|
||||||
|
".ott": "application/vnd.oasis.opendocument.text-template",
|
||||||
|
".p10": "application/pkcs10",
|
||||||
|
".p12": "application/pkcs-12",
|
||||||
|
".p7a": "application/x-pkcs7-signature",
|
||||||
|
".p7c": "application/pkcs7-mime",
|
||||||
|
".p7m": "application/pkcs7-mime",
|
||||||
|
".p7r": "application/x-pkcs7-certreqresp",
|
||||||
|
".p7s": "application/pkcs7-signature",
|
||||||
|
".p": "text/x-pascal",
|
||||||
|
".part": "application/pro_eng",
|
||||||
|
".pas": "text/pascal",
|
||||||
|
".pbm": "image/x-portable-bitmap",
|
||||||
|
".pcl": "application/vndhp-pcl",
|
||||||
|
".pct": "image/x-pict",
|
||||||
|
".pcx": "image/x-pcx",
|
||||||
|
".pdb": "chemical/x-pdb",
|
||||||
|
".pdf": "application/pdf",
|
||||||
|
".pfunk": "audio/make",
|
||||||
|
".pgm": "image/x-portable-graymap",
|
||||||
|
".pic": "image/pict",
|
||||||
|
".pict": "image/pict",
|
||||||
|
".pkg": "application/x-newton-compatible-pkg",
|
||||||
|
".pko": "application/vndms-pkipko",
|
||||||
|
".pl": "text/x-scriptperl",
|
||||||
|
".plx": "application/x-pixclscript",
|
||||||
|
".pm4": "application/x-pagemaker",
|
||||||
|
".pm5": "application/x-pagemaker",
|
||||||
|
".pm": "text/x-scriptperl-module",
|
||||||
|
".png": "image/png",
|
||||||
|
".pnm": "application/x-portable-anymap",
|
||||||
|
".pot": "application/mspowerpoint",
|
||||||
|
".pov": "model/x-pov",
|
||||||
|
".ppa": "application/vndms-powerpoint",
|
||||||
|
".ppm": "image/x-portable-pixmap",
|
||||||
|
".pps": "application/mspowerpoint",
|
||||||
|
".ppt": "application/mspowerpoint",
|
||||||
|
".ppz": "application/mspowerpoint",
|
||||||
|
".pre": "application/x-freelance",
|
||||||
|
".prt": "application/pro_eng",
|
||||||
|
".ps": "application/postscript",
|
||||||
|
".psd": "application/octet-stream",
|
||||||
|
".pvu": "paleovu/x-pv",
|
||||||
|
".pwz": "application/vndms-powerpoint",
|
||||||
|
".py": "text/x-scriptphyton",
|
||||||
|
".pyc": "application/x-bytecodepython",
|
||||||
|
".qcp": "audio/vndqcelp",
|
||||||
|
".qd3": "x-world/x-3dmf",
|
||||||
|
".qd3d": "x-world/x-3dmf",
|
||||||
|
".qif": "image/x-quicktime",
|
||||||
|
".qt": "video/quicktime",
|
||||||
|
".qtc": "video/x-qtc",
|
||||||
|
".qti": "image/x-quicktime",
|
||||||
|
".qtif": "image/x-quicktime",
|
||||||
|
".ra": "audio/x-pn-realaudio",
|
||||||
|
".ram": "audio/x-pn-realaudio",
|
||||||
|
".rar": "application/x-rar-compressed",
|
||||||
|
".ras": "application/x-cmu-raster",
|
||||||
|
".rast": "image/cmu-raster",
|
||||||
|
".rexx": "text/x-scriptrexx",
|
||||||
|
".rf": "image/vndrn-realflash",
|
||||||
|
".rgb": "image/x-rgb",
|
||||||
|
".rm": "application/vndrn-realmedia",
|
||||||
|
".rmi": "audio/mid",
|
||||||
|
".rmm": "audio/x-pn-realaudio",
|
||||||
|
".rmp": "audio/x-pn-realaudio",
|
||||||
|
".rng": "application/ringing-tones",
|
||||||
|
".rnx": "application/vndrn-realplayer",
|
||||||
|
".roff": "application/x-troff",
|
||||||
|
".rp": "image/vndrn-realpix",
|
||||||
|
".rpm": "audio/x-pn-realaudio-plugin",
|
||||||
|
".rt": "text/vndrn-realtext",
|
||||||
|
".rtf": "text/richtext",
|
||||||
|
".rtx": "text/richtext",
|
||||||
|
".rv": "video/vndrn-realvideo",
|
||||||
|
".s": "text/x-asm",
|
||||||
|
".s3m": "audio/s3m",
|
||||||
|
".s7z": "application/x-7z-compressed",
|
||||||
|
".saveme": "application/octet-stream",
|
||||||
|
".sbk": "application/x-tbook",
|
||||||
|
".scm": "text/x-scriptscheme",
|
||||||
|
".sdml": "text/plain",
|
||||||
|
".sdp": "application/sdp",
|
||||||
|
".sdr": "application/sounder",
|
||||||
|
".sea": "application/sea",
|
||||||
|
".set": "application/set",
|
||||||
|
".sgm": "text/x-sgml",
|
||||||
|
".sgml": "text/x-sgml",
|
||||||
|
".sh": "text/x-scriptsh",
|
||||||
|
".shar": "application/x-bsh",
|
||||||
|
".shtml": "text/x-server-parsed-html",
|
||||||
|
".sid": "audio/x-psid",
|
||||||
|
".skd": "application/x-koan",
|
||||||
|
".skm": "application/x-koan",
|
||||||
|
".skp": "application/x-koan",
|
||||||
|
".skt": "application/x-koan",
|
||||||
|
".sit": "application/x-stuffit",
|
||||||
|
".sitx": "application/x-stuffitx",
|
||||||
|
".sl": "application/x-seelogo",
|
||||||
|
".smi": "application/smil",
|
||||||
|
".smil": "application/smil",
|
||||||
|
".snd": "audio/basic",
|
||||||
|
".sol": "application/solids",
|
||||||
|
".spc": "text/x-speech",
|
||||||
|
".spl": "application/futuresplash",
|
||||||
|
".spr": "application/x-sprite",
|
||||||
|
".sprite": "application/x-sprite",
|
||||||
|
".spx": "audio/ogg",
|
||||||
|
".src": "application/x-wais-source",
|
||||||
|
".ssi": "text/x-server-parsed-html",
|
||||||
|
".ssm": "application/streamingmedia",
|
||||||
|
".sst": "application/vndms-pkicertstore",
|
||||||
|
".step": "application/step",
|
||||||
|
".stl": "application/sla",
|
||||||
|
".stp": "application/step",
|
||||||
|
".sv4cpio": "application/x-sv4cpio",
|
||||||
|
".sv4crc": "application/x-sv4crc",
|
||||||
|
".svf": "image/vnddwg",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
|
".svr": "application/x-world",
|
||||||
|
".swf": "application/x-shockwave-flash",
|
||||||
|
".t": "application/x-troff",
|
||||||
|
".talk": "text/x-speech",
|
||||||
|
".tar": "application/x-tar",
|
||||||
|
".tbk": "application/toolbook",
|
||||||
|
".tcl": "text/x-scripttcl",
|
||||||
|
".tcsh": "text/x-scripttcsh",
|
||||||
|
".tex": "application/x-tex",
|
||||||
|
".texi": "application/x-texinfo",
|
||||||
|
".texinfo": "application/x-texinfo",
|
||||||
|
".text": "text/plain",
|
||||||
|
".tgz": "application/gnutar",
|
||||||
|
".tif": "image/tiff",
|
||||||
|
".tiff": "image/tiff",
|
||||||
|
".tr": "application/x-troff",
|
||||||
|
".tsi": "audio/tsp-audio",
|
||||||
|
".tsp": "application/dsptype",
|
||||||
|
".tsv": "text/tab-separated-values",
|
||||||
|
".turbot": "image/florian",
|
||||||
|
".txt": "text/plain",
|
||||||
|
".uil": "text/x-uil",
|
||||||
|
".uni": "text/uri-list",
|
||||||
|
".unis": "text/uri-list",
|
||||||
|
".unv": "application/i-deas",
|
||||||
|
".uri": "text/uri-list",
|
||||||
|
".uris": "text/uri-list",
|
||||||
|
".ustar": "application/x-ustar",
|
||||||
|
".uu": "text/x-uuencode",
|
||||||
|
".uue": "text/x-uuencode",
|
||||||
|
".vcd": "application/x-cdlink",
|
||||||
|
".vcf": "text/x-vcard",
|
||||||
|
".vcard": "text/x-vcard",
|
||||||
|
".vcs": "text/x-vcalendar",
|
||||||
|
".vda": "application/vda",
|
||||||
|
".vdo": "video/vdo",
|
||||||
|
".vew": "application/groupwise",
|
||||||
|
".viv": "video/vivo",
|
||||||
|
".vivo": "video/vivo",
|
||||||
|
".vmd": "application/vocaltec-media-desc",
|
||||||
|
".vmf": "application/vocaltec-media-file",
|
||||||
|
".voc": "audio/voc",
|
||||||
|
".vos": "video/vosaic",
|
||||||
|
".vox": "audio/voxware",
|
||||||
|
".vqe": "audio/x-twinvq-plugin",
|
||||||
|
".vqf": "audio/x-twinvq",
|
||||||
|
".vql": "audio/x-twinvq-plugin",
|
||||||
|
".vrml": "application/x-vrml",
|
||||||
|
".vrt": "x-world/x-vrt",
|
||||||
|
".vsd": "application/x-visio",
|
||||||
|
".vst": "application/x-visio",
|
||||||
|
".vsw": "application/x-visio",
|
||||||
|
".w60": "application/wordperfect60",
|
||||||
|
".w61": "application/wordperfect61",
|
||||||
|
".w6w": "application/msword",
|
||||||
|
".wav": "audio/wav",
|
||||||
|
".wb1": "application/x-qpro",
|
||||||
|
".wbmp": "image/vnd.wap.wbmp",
|
||||||
|
".web": "application/vndxara",
|
||||||
|
".wiz": "application/msword",
|
||||||
|
".wk1": "application/x-123",
|
||||||
|
".wmf": "windows/metafile",
|
||||||
|
".wml": "text/vnd.wap.wml",
|
||||||
|
".wmlc": "application/vnd.wap.wmlc",
|
||||||
|
".wmls": "text/vnd.wap.wmlscript",
|
||||||
|
".wmlsc": "application/vnd.wap.wmlscriptc",
|
||||||
|
".word": "application/msword",
|
||||||
|
".wp5": "application/wordperfect",
|
||||||
|
".wp6": "application/wordperfect",
|
||||||
|
".wp": "application/wordperfect",
|
||||||
|
".wpd": "application/wordperfect",
|
||||||
|
".wq1": "application/x-lotus",
|
||||||
|
".wri": "application/mswrite",
|
||||||
|
".wrl": "application/x-world",
|
||||||
|
".wrz": "model/vrml",
|
||||||
|
".wsc": "text/scriplet",
|
||||||
|
".wsrc": "application/x-wais-source",
|
||||||
|
".wtk": "application/x-wintalk",
|
||||||
|
".x-png": "image/png",
|
||||||
|
".xbm": "image/x-xbitmap",
|
||||||
|
".xdr": "video/x-amt-demorun",
|
||||||
|
".xgz": "xgl/drawing",
|
||||||
|
".xif": "image/vndxiff",
|
||||||
|
".xl": "application/excel",
|
||||||
|
".xla": "application/excel",
|
||||||
|
".xlb": "application/excel",
|
||||||
|
".xlc": "application/excel",
|
||||||
|
".xld": "application/excel",
|
||||||
|
".xlk": "application/excel",
|
||||||
|
".xll": "application/excel",
|
||||||
|
".xlm": "application/excel",
|
||||||
|
".xls": "application/excel",
|
||||||
|
".xlt": "application/excel",
|
||||||
|
".xlv": "application/excel",
|
||||||
|
".xlw": "application/excel",
|
||||||
|
".xm": "audio/xm",
|
||||||
|
".xml": "text/xml",
|
||||||
|
".xmz": "xgl/movie",
|
||||||
|
".xpix": "application/x-vndls-xpix",
|
||||||
|
".xpm": "image/x-xpixmap",
|
||||||
|
".xsr": "video/x-amt-showrun",
|
||||||
|
".xwd": "image/x-xwd",
|
||||||
|
".xyz": "chemical/x-pdb",
|
||||||
|
".z": "application/x-compress",
|
||||||
|
".zip": "application/zip",
|
||||||
|
".zoo": "application/octet-stream",
|
||||||
|
".zsh": "text/x-scriptzsh",
|
||||||
|
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
".docm": "application/vnd.ms-word.document.macroEnabled.12",
|
||||||
|
".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
||||||
|
".dotm": "application/vnd.ms-word.template.macroEnabled.12",
|
||||||
|
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
".xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12",
|
||||||
|
".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||||
|
".xltm": "application/vnd.ms-excel.template.macroEnabled.12",
|
||||||
|
".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
||||||
|
".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
|
||||||
|
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||||
|
".pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||||
|
".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
||||||
|
".ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
|
||||||
|
".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
|
||||||
|
".potm": "application/vnd.ms-powerpoint.template.macroEnabled.12",
|
||||||
|
".ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12",
|
||||||
|
".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
|
||||||
|
".sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12",
|
||||||
|
".thmx": "application/vnd.ms-officetheme",
|
||||||
|
".onetoc": "application/onenote",
|
||||||
|
".onetoc2": "application/onenote",
|
||||||
|
".onetmp": "application/onenote",
|
||||||
|
".onepkg": "application/onenote",
|
||||||
|
".key": "application/x-iwork-keynote-sffkey",
|
||||||
|
".kth": "application/x-iwork-keynote-sffkth",
|
||||||
|
".nmbtemplate": "application/x-iwork-numbers-sfftemplate",
|
||||||
|
".numbers": "application/x-iwork-numbers-sffnumbers",
|
||||||
|
".pages": "application/x-iwork-pages-sffpages",
|
||||||
|
".template": "application/x-iwork-pages-sfftemplate",
|
||||||
|
".xpi": "application/x-xpinstall",
|
||||||
|
".oex": "application/x-opera-extension",
|
||||||
|
".mustache": "text/html",
|
||||||
|
}
|
|
@ -0,0 +1,396 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type namespaceCond func(*beecontext.Context) bool
|
||||||
|
|
||||||
|
// LinkNamespace used as link action
|
||||||
|
type LinkNamespace func(*Namespace)
|
||||||
|
|
||||||
|
// Namespace is store all the info
|
||||||
|
type Namespace struct {
|
||||||
|
prefix string
|
||||||
|
handlers *ControllerRegister
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNamespace get new Namespace
|
||||||
|
func NewNamespace(prefix string, params ...LinkNamespace) *Namespace {
|
||||||
|
ns := &Namespace{
|
||||||
|
prefix: prefix,
|
||||||
|
handlers: NewControllerRegister(),
|
||||||
|
}
|
||||||
|
for _, p := range params {
|
||||||
|
p(ns)
|
||||||
|
}
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cond set condition function
|
||||||
|
// if cond return true can run this namespace, else can't
|
||||||
|
// usage:
|
||||||
|
// ns.Cond(func (ctx *context.Context) bool{
|
||||||
|
// if ctx.Input.Domain() == "api.beego.me" {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// return false
|
||||||
|
// })
|
||||||
|
// Cond as the first filter
|
||||||
|
func (n *Namespace) Cond(cond namespaceCond) *Namespace {
|
||||||
|
fn := func(ctx *beecontext.Context) {
|
||||||
|
if !cond(ctx) {
|
||||||
|
exception("405", ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v := n.handlers.filters[BeforeRouter]; len(v) > 0 {
|
||||||
|
mr := new(FilterRouter)
|
||||||
|
mr.tree = NewTree()
|
||||||
|
mr.pattern = "*"
|
||||||
|
mr.filterFunc = fn
|
||||||
|
mr.tree.AddRouter("*", true)
|
||||||
|
n.handlers.filters[BeforeRouter] = append([]*FilterRouter{mr}, v...)
|
||||||
|
} else {
|
||||||
|
n.handlers.InsertFilter("*", BeforeRouter, fn)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter add filter in the Namespace
|
||||||
|
// action has before & after
|
||||||
|
// FilterFunc
|
||||||
|
// usage:
|
||||||
|
// Filter("before", func (ctx *context.Context){
|
||||||
|
// _, ok := ctx.Input.Session("uid").(int)
|
||||||
|
// if !ok && ctx.Request.RequestURI != "/login" {
|
||||||
|
// ctx.Redirect(302, "/login")
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace {
|
||||||
|
var a int
|
||||||
|
if action == "before" {
|
||||||
|
a = BeforeRouter
|
||||||
|
} else if action == "after" {
|
||||||
|
a = FinishRouter
|
||||||
|
}
|
||||||
|
for _, f := range filter {
|
||||||
|
n.handlers.InsertFilter("*", a, f)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Router same as beego.Rourer
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Router
|
||||||
|
func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace {
|
||||||
|
n.handlers.Add(rootpath, c, mappingMethods...)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoRouter same as beego.AutoRouter
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#AutoRouter
|
||||||
|
func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace {
|
||||||
|
n.handlers.AddAuto(c)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoPrefix same as beego.AutoPrefix
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix
|
||||||
|
func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace {
|
||||||
|
n.handlers.AddAutoPrefix(prefix, c)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get same as beego.Get
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Get
|
||||||
|
func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace {
|
||||||
|
n.handlers.Get(rootpath, f)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post same as beego.Post
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Post
|
||||||
|
func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace {
|
||||||
|
n.handlers.Post(rootpath, f)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete same as beego.Delete
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Delete
|
||||||
|
func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace {
|
||||||
|
n.handlers.Delete(rootpath, f)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put same as beego.Put
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Put
|
||||||
|
func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace {
|
||||||
|
n.handlers.Put(rootpath, f)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head same as beego.Head
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Head
|
||||||
|
func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace {
|
||||||
|
n.handlers.Head(rootpath, f)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options same as beego.Options
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Options
|
||||||
|
func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace {
|
||||||
|
n.handlers.Options(rootpath, f)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch same as beego.Patch
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Patch
|
||||||
|
func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace {
|
||||||
|
n.handlers.Patch(rootpath, f)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any same as beego.Any
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Any
|
||||||
|
func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace {
|
||||||
|
n.handlers.Any(rootpath, f)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler same as beego.Handler
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Handler
|
||||||
|
func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace {
|
||||||
|
n.handlers.Handler(rootpath, h)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include add include class
|
||||||
|
// refer: https://godoc.org/github.com/astaxie/beego#Include
|
||||||
|
func (n *Namespace) Include(cList ...ControllerInterface) *Namespace {
|
||||||
|
n.handlers.Include(cList...)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace add nest Namespace
|
||||||
|
// usage:
|
||||||
|
//ns := beego.NewNamespace(“/v1”).
|
||||||
|
//Namespace(
|
||||||
|
// beego.NewNamespace("/shop").
|
||||||
|
// Get("/:id", func(ctx *context.Context) {
|
||||||
|
// ctx.Output.Body([]byte("shopinfo"))
|
||||||
|
// }),
|
||||||
|
// beego.NewNamespace("/order").
|
||||||
|
// Get("/:id", func(ctx *context.Context) {
|
||||||
|
// ctx.Output.Body([]byte("orderinfo"))
|
||||||
|
// }),
|
||||||
|
// beego.NewNamespace("/crm").
|
||||||
|
// Get("/:id", func(ctx *context.Context) {
|
||||||
|
// ctx.Output.Body([]byte("crminfo"))
|
||||||
|
// }),
|
||||||
|
//)
|
||||||
|
func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
|
||||||
|
for _, ni := range ns {
|
||||||
|
for k, v := range ni.handlers.routers {
|
||||||
|
if t, ok := n.handlers.routers[k]; ok {
|
||||||
|
addPrefix(v, ni.prefix)
|
||||||
|
n.handlers.routers[k].AddTree(ni.prefix, v)
|
||||||
|
} else {
|
||||||
|
t = NewTree()
|
||||||
|
t.AddTree(ni.prefix, v)
|
||||||
|
addPrefix(t, ni.prefix)
|
||||||
|
n.handlers.routers[k] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.handlers.enableFilter {
|
||||||
|
for pos, filterList := range ni.handlers.filters {
|
||||||
|
for _, mr := range filterList {
|
||||||
|
t := NewTree()
|
||||||
|
t.AddTree(ni.prefix, mr.tree)
|
||||||
|
mr.tree = t
|
||||||
|
n.handlers.insertFilterRouter(pos, mr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNamespace register Namespace into beego.Handler
|
||||||
|
// support multi Namespace
|
||||||
|
func AddNamespace(nl ...*Namespace) {
|
||||||
|
for _, n := range nl {
|
||||||
|
for k, v := range n.handlers.routers {
|
||||||
|
if t, ok := BeeApp.Handlers.routers[k]; ok {
|
||||||
|
addPrefix(v, n.prefix)
|
||||||
|
BeeApp.Handlers.routers[k].AddTree(n.prefix, v)
|
||||||
|
} else {
|
||||||
|
t = NewTree()
|
||||||
|
t.AddTree(n.prefix, v)
|
||||||
|
addPrefix(t, n.prefix)
|
||||||
|
BeeApp.Handlers.routers[k] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n.handlers.enableFilter {
|
||||||
|
for pos, filterList := range n.handlers.filters {
|
||||||
|
for _, mr := range filterList {
|
||||||
|
t := NewTree()
|
||||||
|
t.AddTree(n.prefix, mr.tree)
|
||||||
|
mr.tree = t
|
||||||
|
BeeApp.Handlers.insertFilterRouter(pos, mr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPrefix(t *Tree, prefix string) {
|
||||||
|
for _, v := range t.fixrouters {
|
||||||
|
addPrefix(v, prefix)
|
||||||
|
}
|
||||||
|
if t.wildcard != nil {
|
||||||
|
addPrefix(t.wildcard, prefix)
|
||||||
|
}
|
||||||
|
for _, l := range t.leaves {
|
||||||
|
if c, ok := l.runObject.(*ControllerInfo); ok {
|
||||||
|
if !strings.HasPrefix(c.pattern, prefix) {
|
||||||
|
c.pattern = prefix + c.pattern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSCond is Namespace Condition
|
||||||
|
func NSCond(cond namespaceCond) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Cond(cond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSBefore Namespace BeforeRouter filter
|
||||||
|
func NSBefore(filterList ...FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Filter("before", filterList...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSAfter add Namespace FinishRouter filter
|
||||||
|
func NSAfter(filterList ...FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Filter("after", filterList...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSInclude Namespace Include ControllerInterface
|
||||||
|
func NSInclude(cList ...ControllerInterface) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Include(cList...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSRouter call Namespace Router
|
||||||
|
func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Router(rootpath, c, mappingMethods...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSGet call Namespace Get
|
||||||
|
func NSGet(rootpath string, f FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Get(rootpath, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSPost call Namespace Post
|
||||||
|
func NSPost(rootpath string, f FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Post(rootpath, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSHead call Namespace Head
|
||||||
|
func NSHead(rootpath string, f FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Head(rootpath, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSPut call Namespace Put
|
||||||
|
func NSPut(rootpath string, f FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Put(rootpath, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSDelete call Namespace Delete
|
||||||
|
func NSDelete(rootpath string, f FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Delete(rootpath, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSAny call Namespace Any
|
||||||
|
func NSAny(rootpath string, f FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Any(rootpath, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSOptions call Namespace Options
|
||||||
|
func NSOptions(rootpath string, f FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Options(rootpath, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSPatch call Namespace Patch
|
||||||
|
func NSPatch(rootpath string, f FilterFunc) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Patch(rootpath, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSAutoRouter call Namespace AutoRouter
|
||||||
|
func NSAutoRouter(c ControllerInterface) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.AutoRouter(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSAutoPrefix call Namespace AutoPrefix
|
||||||
|
func NSAutoPrefix(prefix string, c ControllerInterface) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.AutoPrefix(prefix, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSNamespace add sub Namespace
|
||||||
|
func NSNamespace(prefix string, params ...LinkNamespace) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
n := NewNamespace(prefix, params...)
|
||||||
|
ns.Namespace(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSHandler add handler
|
||||||
|
func NSHandler(rootpath string, h http.Handler) LinkNamespace {
|
||||||
|
return func(ns *Namespace) {
|
||||||
|
ns.Handler(rootpath, h)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,403 @@
|
||||||
|
// Copyright 2014 beego Author. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package beego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/context/param"
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
"github.com/astaxie/beego/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalRouterTemplate = `package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/astaxie/beego/context/param"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
{{.globalinfo}}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
lastupdateFilename = "lastupdate.tmp"
|
||||||
|
commentFilename string
|
||||||
|
pkgLastupdate map[string]int64
|
||||||
|
genInfoList map[string][]ControllerComments
|
||||||
|
)
|
||||||
|
|
||||||
|
const commentPrefix = "commentsRouter_"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pkgLastupdate = make(map[string]int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parserPkg(pkgRealpath, pkgpath string) error {
|
||||||
|
rep := strings.NewReplacer("\\", "_", "/", "_", ".", "_")
|
||||||
|
commentFilename, _ = filepath.Rel(AppPath, pkgRealpath)
|
||||||
|
commentFilename = commentPrefix + rep.Replace(commentFilename) + ".go"
|
||||||
|
if !compareFile(pkgRealpath) {
|
||||||
|
logs.Info(pkgRealpath + " no changed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
genInfoList = make(map[string][]ControllerComments)
|
||||||
|
fileSet := token.NewFileSet()
|
||||||
|
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
|
||||||
|
name := info.Name()
|
||||||
|
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
|
||||||
|
}, parser.ParseComments)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, pkg := range astPkgs {
|
||||||
|
for _, fl := range pkg.Files {
|
||||||
|
for _, d := range fl.Decls {
|
||||||
|
switch specDecl := d.(type) {
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
if specDecl.Recv != nil {
|
||||||
|
exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser
|
||||||
|
if ok {
|
||||||
|
parserComments(specDecl, fmt.Sprint(exp.X), pkgpath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genRouterCode(pkgRealpath)
|
||||||
|
savetoFile(pkgRealpath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type parsedComment struct {
|
||||||
|
routerPath string
|
||||||
|
methods []string
|
||||||
|
params map[string]parsedParam
|
||||||
|
}
|
||||||
|
|
||||||
|
type parsedParam struct {
|
||||||
|
name string
|
||||||
|
datatype string
|
||||||
|
location string
|
||||||
|
defValue string
|
||||||
|
required bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
||||||
|
if f.Doc != nil {
|
||||||
|
parsedComments, err := parseComment(f.Doc.List)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, parsedComment := range parsedComments {
|
||||||
|
if parsedComment.routerPath != "" {
|
||||||
|
key := pkgpath + ":" + controllerName
|
||||||
|
cc := ControllerComments{}
|
||||||
|
cc.Method = f.Name.String()
|
||||||
|
cc.Router = parsedComment.routerPath
|
||||||
|
cc.AllowHTTPMethods = parsedComment.methods
|
||||||
|
cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
|
||||||
|
genInfoList[key] = append(genInfoList[key], cc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam {
|
||||||
|
result := make([]*param.MethodParam, 0, len(funcParams))
|
||||||
|
for _, fparam := range funcParams {
|
||||||
|
for _, pName := range fparam.Names {
|
||||||
|
methodParam := buildMethodParam(fparam, pName.Name, pc)
|
||||||
|
result = append(result, methodParam)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildMethodParam(fparam *ast.Field, name string, pc *parsedComment) *param.MethodParam {
|
||||||
|
options := []param.MethodParamOption{}
|
||||||
|
if cparam, ok := pc.params[name]; ok {
|
||||||
|
//Build param from comment info
|
||||||
|
name = cparam.name
|
||||||
|
if cparam.required {
|
||||||
|
options = append(options, param.IsRequired)
|
||||||
|
}
|
||||||
|
switch cparam.location {
|
||||||
|
case "body":
|
||||||
|
options = append(options, param.InBody)
|
||||||
|
case "header":
|
||||||
|
options = append(options, param.InHeader)
|
||||||
|
case "path":
|
||||||
|
options = append(options, param.InPath)
|
||||||
|
}
|
||||||
|
if cparam.defValue != "" {
|
||||||
|
options = append(options, param.Default(cparam.defValue))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if paramInPath(name, pc.routerPath) {
|
||||||
|
options = append(options, param.InPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return param.New(name, options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func paramInPath(name, route string) bool {
|
||||||
|
return strings.HasSuffix(route, ":"+name) ||
|
||||||
|
strings.Contains(route, ":"+name+"/")
|
||||||
|
}
|
||||||
|
|
||||||
|
var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`)
|
||||||
|
|
||||||
|
func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) {
|
||||||
|
pcs = []*parsedComment{}
|
||||||
|
params := map[string]parsedParam{}
|
||||||
|
|
||||||
|
for _, c := range lines {
|
||||||
|
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||||
|
if strings.HasPrefix(t, "@Param") {
|
||||||
|
pv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Param")))
|
||||||
|
if len(pv) < 4 {
|
||||||
|
logs.Error("Invalid @Param format. Needs at least 4 parameters")
|
||||||
|
}
|
||||||
|
p := parsedParam{}
|
||||||
|
names := strings.SplitN(pv[0], "=>", 2)
|
||||||
|
p.name = names[0]
|
||||||
|
funcParamName := p.name
|
||||||
|
if len(names) > 1 {
|
||||||
|
funcParamName = names[1]
|
||||||
|
}
|
||||||
|
p.location = pv[1]
|
||||||
|
p.datatype = pv[2]
|
||||||
|
switch len(pv) {
|
||||||
|
case 5:
|
||||||
|
p.required, _ = strconv.ParseBool(pv[3])
|
||||||
|
case 6:
|
||||||
|
p.defValue = pv[3]
|
||||||
|
p.required, _ = strconv.ParseBool(pv[4])
|
||||||
|
}
|
||||||
|
params[funcParamName] = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range lines {
|
||||||
|
var pc = &parsedComment{}
|
||||||
|
pc.params = params
|
||||||
|
|
||||||
|
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||||
|
if strings.HasPrefix(t, "@router") {
|
||||||
|
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||||
|
matches := routeRegex.FindStringSubmatch(t)
|
||||||
|
if len(matches) == 3 {
|
||||||
|
pc.routerPath = matches[1]
|
||||||
|
methods := matches[2]
|
||||||
|
if methods == "" {
|
||||||
|
pc.methods = []string{"get"}
|
||||||
|
//pc.hasGet = true
|
||||||
|
} else {
|
||||||
|
pc.methods = strings.Split(methods, ",")
|
||||||
|
//pc.hasGet = strings.Contains(methods, "get")
|
||||||
|
}
|
||||||
|
pcs = append(pcs, pc)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("Router information is missing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// direct copy from bee\g_docs.go
|
||||||
|
// analysis params return []string
|
||||||
|
// @Param query form string true "The email for login"
|
||||||
|
// [query form string true "The email for login"]
|
||||||
|
func getparams(str string) []string {
|
||||||
|
var s []rune
|
||||||
|
var j int
|
||||||
|
var start bool
|
||||||
|
var r []string
|
||||||
|
var quoted int8
|
||||||
|
for _, c := range str {
|
||||||
|
if unicode.IsSpace(c) && quoted == 0 {
|
||||||
|
if !start {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
start = false
|
||||||
|
j++
|
||||||
|
r = append(r, string(s))
|
||||||
|
s = make([]rune, 0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = true
|
||||||
|
if c == '"' {
|
||||||
|
quoted ^= 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s = append(s, c)
|
||||||
|
}
|
||||||
|
if len(s) > 0 {
|
||||||
|
r = append(r, string(s))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func genRouterCode(pkgRealpath string) {
|
||||||
|
os.Mkdir(getRouterDir(pkgRealpath), 0755)
|
||||||
|
logs.Info("generate router from comments")
|
||||||
|
var (
|
||||||
|
globalinfo string
|
||||||
|
sortKey []string
|
||||||
|
)
|
||||||
|
for k := range genInfoList {
|
||||||
|
sortKey = append(sortKey, k)
|
||||||
|
}
|
||||||
|
sort.Strings(sortKey)
|
||||||
|
for _, k := range sortKey {
|
||||||
|
cList := genInfoList[k]
|
||||||
|
sort.Sort(ControllerCommentsSlice(cList))
|
||||||
|
for _, c := range cList {
|
||||||
|
allmethod := "nil"
|
||||||
|
if len(c.AllowHTTPMethods) > 0 {
|
||||||
|
allmethod = "[]string{"
|
||||||
|
for _, m := range c.AllowHTTPMethods {
|
||||||
|
allmethod += `"` + m + `",`
|
||||||
|
}
|
||||||
|
allmethod = strings.TrimRight(allmethod, ",") + "}"
|
||||||
|
}
|
||||||
|
params := "nil"
|
||||||
|
if len(c.Params) > 0 {
|
||||||
|
params = "[]map[string]string{"
|
||||||
|
for _, p := range c.Params {
|
||||||
|
for k, v := range p {
|
||||||
|
params = params + `map[string]string{` + k + `:"` + v + `"},`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params = strings.TrimRight(params, ",") + "}"
|
||||||
|
}
|
||||||
|
methodParams := "param.Make("
|
||||||
|
if len(c.MethodParams) > 0 {
|
||||||
|
lines := make([]string, 0, len(c.MethodParams))
|
||||||
|
for _, m := range c.MethodParams {
|
||||||
|
lines = append(lines, fmt.Sprint(m))
|
||||||
|
}
|
||||||
|
methodParams += "\n " +
|
||||||
|
strings.Join(lines, ",\n ") +
|
||||||
|
",\n "
|
||||||
|
}
|
||||||
|
methodParams += ")"
|
||||||
|
globalinfo = globalinfo + `
|
||||||
|
beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
|
||||||
|
beego.ControllerComments{
|
||||||
|
Method: "` + strings.TrimSpace(c.Method) + `",
|
||||||
|
` + "Router: `" + c.Router + "`" + `,
|
||||||
|
AllowHTTPMethods: ` + allmethod + `,
|
||||||
|
MethodParams: ` + methodParams + `,
|
||||||
|
Params: ` + params + `})
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if globalinfo != "" {
|
||||||
|
f, err := os.Create(filepath.Join(getRouterDir(pkgRealpath), commentFilename))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
f.WriteString(strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareFile(pkgRealpath string) bool {
|
||||||
|
if !utils.FileExists(filepath.Join(getRouterDir(pkgRealpath), commentFilename)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if utils.FileExists(lastupdateFilename) {
|
||||||
|
content, err := ioutil.ReadFile(lastupdateFilename)
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
json.Unmarshal(content, &pkgLastupdate)
|
||||||
|
lastupdate, err := getpathTime(pkgRealpath)
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if v, ok := pkgLastupdate[pkgRealpath]; ok {
|
||||||
|
if lastupdate <= v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func savetoFile(pkgRealpath string) {
|
||||||
|
lastupdate, err := getpathTime(pkgRealpath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pkgLastupdate[pkgRealpath] = lastupdate
|
||||||
|
d, err := json.Marshal(pkgLastupdate)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ioutil.WriteFile(lastupdateFilename, d, os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getpathTime(pkgRealpath string) (lastupdate int64, err error) {
|
||||||
|
fl, err := ioutil.ReadDir(pkgRealpath)
|
||||||
|
if err != nil {
|
||||||
|
return lastupdate, err
|
||||||
|
}
|
||||||
|
for _, f := range fl {
|
||||||
|
if lastupdate < f.ModTime().UnixNano() {
|
||||||
|
lastupdate = f.ModTime().UnixNano()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastupdate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRouterDir(pkgRealpath string) string {
|
||||||
|
dir := filepath.Dir(pkgRealpath)
|
||||||
|
for {
|
||||||
|
d := filepath.Join(dir, "routers")
|
||||||
|
if utils.FileExists(d) {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
if r, _ := filepath.Rel(dir, AppPath); r == "." {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
// Parent dir.
|
||||||
|
dir = filepath.Dir(dir)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright 2016 beego authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package beego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PolicyFunc defines a policy function which is invoked before the controller handler is executed.
|
||||||
|
type PolicyFunc func(*context.Context)
|
||||||
|
|
||||||
|
// FindPolicy Find Router info for URL
|
||||||
|
func (p *ControllerRegister) FindPolicy(cont *context.Context) []PolicyFunc {
|
||||||
|
var urlPath = cont.Input.URL()
|
||||||
|
if !BConfig.RouterCaseSensitive {
|
||||||
|
urlPath = strings.ToLower(urlPath)
|
||||||
|
}
|
||||||
|
httpMethod := cont.Input.Method()
|
||||||
|
isWildcard := false
|
||||||
|
// Find policy for current method
|
||||||
|
t, ok := p.policies[httpMethod]
|
||||||
|
// If not found - find policy for whole controller
|
||||||
|
if !ok {
|
||||||
|
t, ok = p.policies["*"]
|
||||||
|
isWildcard = true
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
runObjects := t.Match(urlPath, cont)
|
||||||
|
if r, ok := runObjects.([]PolicyFunc); ok {
|
||||||
|
return r
|
||||||
|
} else if !isWildcard {
|
||||||
|
// If no policies found and we checked not for "*" method - try to find it
|
||||||
|
t, ok = p.policies["*"]
|
||||||
|
if ok {
|
||||||
|
runObjects = t.Match(urlPath, cont)
|
||||||
|
if r, ok = runObjects.([]PolicyFunc); ok {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ControllerRegister) addToPolicy(method, pattern string, r ...PolicyFunc) {
|
||||||
|
method = strings.ToUpper(method)
|
||||||
|
p.enablePolicy = true
|
||||||
|
if !BConfig.RouterCaseSensitive {
|
||||||
|
pattern = strings.ToLower(pattern)
|
||||||
|
}
|
||||||
|
if t, ok := p.policies[method]; ok {
|
||||||
|
t.AddRouter(pattern, r)
|
||||||
|
} else {
|
||||||
|
t := NewTree()
|
||||||
|
t.AddRouter(pattern, r)
|
||||||
|
p.policies[method] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Policy Register new policy in beego
|
||||||
|
func Policy(pattern, method string, policy ...PolicyFunc) {
|
||||||
|
BeeApp.Handlers.addToPolicy(method, pattern, policy...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find policies and execute if were found
|
||||||
|
func (p *ControllerRegister) execPolicy(cont *context.Context, urlPath string) (started bool) {
|
||||||
|
if !p.enablePolicy {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Find Policy for method
|
||||||
|
policyList := p.FindPolicy(cont)
|
||||||
|
if len(policyList) > 0 {
|
||||||
|
// Run policies
|
||||||
|
for _, runPolicy := range policyList {
|
||||||
|
runPolicy(cont)
|
||||||
|
if cont.ResponseWriter.Started {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,993 @@
|
||||||
|
// Copyright 2014 beego Author. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package beego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
beecontext "github.com/astaxie/beego/context"
|
||||||
|
"github.com/astaxie/beego/context/param"
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
"github.com/astaxie/beego/toolbox"
|
||||||
|
"github.com/astaxie/beego/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// default filter execution points
|
||||||
|
const (
|
||||||
|
BeforeStatic = iota
|
||||||
|
BeforeRouter
|
||||||
|
BeforeExec
|
||||||
|
AfterExec
|
||||||
|
FinishRouter
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
routerTypeBeego = iota
|
||||||
|
routerTypeRESTFul
|
||||||
|
routerTypeHandler
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// HTTPMETHOD list the supported http methods.
|
||||||
|
HTTPMETHOD = map[string]bool{
|
||||||
|
"GET": true,
|
||||||
|
"POST": true,
|
||||||
|
"PUT": true,
|
||||||
|
"DELETE": true,
|
||||||
|
"PATCH": true,
|
||||||
|
"OPTIONS": true,
|
||||||
|
"HEAD": true,
|
||||||
|
"TRACE": true,
|
||||||
|
"CONNECT": true,
|
||||||
|
"MKCOL": true,
|
||||||
|
"COPY": true,
|
||||||
|
"MOVE": true,
|
||||||
|
"PROPFIND": true,
|
||||||
|
"PROPPATCH": true,
|
||||||
|
"LOCK": true,
|
||||||
|
"UNLOCK": true,
|
||||||
|
}
|
||||||
|
// these beego.Controller's methods shouldn't reflect to AutoRouter
|
||||||
|
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
|
||||||
|
"RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJSON", "ServeJSONP",
|
||||||
|
"ServeXML", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool",
|
||||||
|
"GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession",
|
||||||
|
"DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie",
|
||||||
|
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
|
||||||
|
"GetControllerAndAction", "ServeFormatted"}
|
||||||
|
|
||||||
|
urlPlaceholder = "{{placeholder}}"
|
||||||
|
// DefaultAccessLogFilter will skip the accesslog if return true
|
||||||
|
DefaultAccessLogFilter FilterHandler = &logFilter{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilterHandler is an interface for
|
||||||
|
type FilterHandler interface {
|
||||||
|
Filter(*beecontext.Context) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// default log filter static file will not show
|
||||||
|
type logFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logFilter) Filter(ctx *beecontext.Context) bool {
|
||||||
|
requestPath := path.Clean(ctx.Request.URL.Path)
|
||||||
|
if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for prefix := range BConfig.WebConfig.StaticDir {
|
||||||
|
if strings.HasPrefix(requestPath, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExceptMethodAppend to append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter
|
||||||
|
func ExceptMethodAppend(action string) {
|
||||||
|
exceptMethod = append(exceptMethod, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerInfo holds information about the controller.
|
||||||
|
type ControllerInfo struct {
|
||||||
|
pattern string
|
||||||
|
controllerType reflect.Type
|
||||||
|
methods map[string]string
|
||||||
|
handler http.Handler
|
||||||
|
runFunction FilterFunc
|
||||||
|
routerType int
|
||||||
|
initialize func() ControllerInterface
|
||||||
|
methodParams []*param.MethodParam
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerRegister containers registered router rules, controller handlers and filters.
|
||||||
|
type ControllerRegister struct {
|
||||||
|
routers map[string]*Tree
|
||||||
|
enablePolicy bool
|
||||||
|
policies map[string]*Tree
|
||||||
|
enableFilter bool
|
||||||
|
filters [FinishRouter + 1][]*FilterRouter
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewControllerRegister returns a new ControllerRegister.
|
||||||
|
func NewControllerRegister() *ControllerRegister {
|
||||||
|
cr := &ControllerRegister{
|
||||||
|
routers: make(map[string]*Tree),
|
||||||
|
policies: make(map[string]*Tree),
|
||||||
|
}
|
||||||
|
cr.pool.New = func() interface{} {
|
||||||
|
return beecontext.NewContext()
|
||||||
|
}
|
||||||
|
return cr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add controller handler and pattern rules to ControllerRegister.
|
||||||
|
// usage:
|
||||||
|
// default methods is the same name as method
|
||||||
|
// Add("/user",&UserController{})
|
||||||
|
// Add("/api/list",&RestController{},"*:ListFood")
|
||||||
|
// Add("/api/create",&RestController{},"post:CreateFood")
|
||||||
|
// Add("/api/update",&RestController{},"put:UpdateFood")
|
||||||
|
// Add("/api/delete",&RestController{},"delete:DeleteFood")
|
||||||
|
// Add("/api",&RestController{},"get,post:ApiFunc"
|
||||||
|
// Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc")
|
||||||
|
func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) {
|
||||||
|
p.addWithMethodParams(pattern, c, nil, mappingMethods...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, mappingMethods ...string) {
|
||||||
|
reflectVal := reflect.ValueOf(c)
|
||||||
|
t := reflect.Indirect(reflectVal).Type()
|
||||||
|
methods := make(map[string]string)
|
||||||
|
if len(mappingMethods) > 0 {
|
||||||
|
semi := strings.Split(mappingMethods[0], ";")
|
||||||
|
for _, v := range semi {
|
||||||
|
colon := strings.Split(v, ":")
|
||||||
|
if len(colon) != 2 {
|
||||||
|
panic("method mapping format is invalid")
|
||||||
|
}
|
||||||
|
comma := strings.Split(colon[0], ",")
|
||||||
|
for _, m := range comma {
|
||||||
|
if m == "*" || HTTPMETHOD[strings.ToUpper(m)] {
|
||||||
|
if val := reflectVal.MethodByName(colon[1]); val.IsValid() {
|
||||||
|
methods[strings.ToUpper(m)] = colon[1]
|
||||||
|
} else {
|
||||||
|
panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic(v + " is an invalid method mapping. Method doesn't exist " + m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
route := &ControllerInfo{}
|
||||||
|
route.pattern = pattern
|
||||||
|
route.methods = methods
|
||||||
|
route.routerType = routerTypeBeego
|
||||||
|
route.controllerType = t
|
||||||
|
route.initialize = func() ControllerInterface {
|
||||||
|
vc := reflect.New(route.controllerType)
|
||||||
|
execController, ok := vc.Interface().(ControllerInterface)
|
||||||
|
if !ok {
|
||||||
|
panic("controller is not ControllerInterface")
|
||||||
|
}
|
||||||
|
|
||||||
|
elemVal := reflect.ValueOf(c).Elem()
|
||||||
|
elemType := reflect.TypeOf(c).Elem()
|
||||||
|
execElem := reflect.ValueOf(execController).Elem()
|
||||||
|
|
||||||
|
numOfFields := elemVal.NumField()
|
||||||
|
for i := 0; i < numOfFields; i++ {
|
||||||
|
fieldType := elemType.Field(i)
|
||||||
|
elemField := execElem.FieldByName(fieldType.Name)
|
||||||
|
if elemField.CanSet() {
|
||||||
|
fieldVal := elemVal.Field(i)
|
||||||
|
elemField.Set(fieldVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return execController
|
||||||
|
}
|
||||||
|
|
||||||
|
route.methodParams = methodParams
|
||||||
|
if len(methods) == 0 {
|
||||||
|
for m := range HTTPMETHOD {
|
||||||
|
p.addToRouter(m, pattern, route)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for k := range methods {
|
||||||
|
if k == "*" {
|
||||||
|
for m := range HTTPMETHOD {
|
||||||
|
p.addToRouter(m, pattern, route)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.addToRouter(k, pattern, route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) {
|
||||||
|
if !BConfig.RouterCaseSensitive {
|
||||||
|
pattern = strings.ToLower(pattern)
|
||||||
|
}
|
||||||
|
if t, ok := p.routers[method]; ok {
|
||||||
|
t.AddRouter(pattern, r)
|
||||||
|
} else {
|
||||||
|
t := NewTree()
|
||||||
|
t.AddRouter(pattern, r)
|
||||||
|
p.routers[method] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include only when the Runmode is dev will generate router file in the router/auto.go from the controller
|
||||||
|
// Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
|
||||||
|
func (p *ControllerRegister) Include(cList ...ControllerInterface) {
|
||||||
|
if BConfig.RunMode == DEV {
|
||||||
|
skip := make(map[string]bool, 10)
|
||||||
|
for _, c := range cList {
|
||||||
|
reflectVal := reflect.ValueOf(c)
|
||||||
|
t := reflect.Indirect(reflectVal).Type()
|
||||||
|
wgopath := utils.GetGOPATHs()
|
||||||
|
if len(wgopath) == 0 {
|
||||||
|
panic("you are in dev mode. So please set gopath")
|
||||||
|
}
|
||||||
|
pkgpath := ""
|
||||||
|
for _, wg := range wgopath {
|
||||||
|
wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath()))
|
||||||
|
if utils.FileExists(wg) {
|
||||||
|
pkgpath = wg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pkgpath != "" {
|
||||||
|
if _, ok := skip[pkgpath]; !ok {
|
||||||
|
skip[pkgpath] = true
|
||||||
|
parserPkg(pkgpath, t.PkgPath())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, c := range cList {
|
||||||
|
reflectVal := reflect.ValueOf(c)
|
||||||
|
t := reflect.Indirect(reflectVal).Type()
|
||||||
|
key := t.PkgPath() + ":" + t.Name()
|
||||||
|
if comm, ok := GlobalControllerRouter[key]; ok {
|
||||||
|
for _, a := range comm {
|
||||||
|
p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get add get method
|
||||||
|
// usage:
|
||||||
|
// Get("/", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) Get(pattern string, f FilterFunc) {
|
||||||
|
p.AddMethod("get", pattern, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post add post method
|
||||||
|
// usage:
|
||||||
|
// Post("/api", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) Post(pattern string, f FilterFunc) {
|
||||||
|
p.AddMethod("post", pattern, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put add put method
|
||||||
|
// usage:
|
||||||
|
// Put("/api/:id", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) Put(pattern string, f FilterFunc) {
|
||||||
|
p.AddMethod("put", pattern, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete add delete method
|
||||||
|
// usage:
|
||||||
|
// Delete("/api/:id", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) Delete(pattern string, f FilterFunc) {
|
||||||
|
p.AddMethod("delete", pattern, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head add head method
|
||||||
|
// usage:
|
||||||
|
// Head("/api/:id", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) Head(pattern string, f FilterFunc) {
|
||||||
|
p.AddMethod("head", pattern, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch add patch method
|
||||||
|
// usage:
|
||||||
|
// Patch("/api/:id", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) Patch(pattern string, f FilterFunc) {
|
||||||
|
p.AddMethod("patch", pattern, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options add options method
|
||||||
|
// usage:
|
||||||
|
// Options("/api/:id", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) Options(pattern string, f FilterFunc) {
|
||||||
|
p.AddMethod("options", pattern, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any add all method
|
||||||
|
// usage:
|
||||||
|
// Any("/api/:id", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) Any(pattern string, f FilterFunc) {
|
||||||
|
p.AddMethod("*", pattern, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMethod add http method router
|
||||||
|
// usage:
|
||||||
|
// AddMethod("get","/api/:id", func(ctx *context.Context){
|
||||||
|
// ctx.Output.Body("hello world")
|
||||||
|
// })
|
||||||
|
func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) {
|
||||||
|
method = strings.ToUpper(method)
|
||||||
|
if method != "*" && !HTTPMETHOD[method] {
|
||||||
|
panic("not support http method: " + method)
|
||||||
|
}
|
||||||
|
route := &ControllerInfo{}
|
||||||
|
route.pattern = pattern
|
||||||
|
route.routerType = routerTypeRESTFul
|
||||||
|
route.runFunction = f
|
||||||
|
methods := make(map[string]string)
|
||||||
|
if method == "*" {
|
||||||
|
for val := range HTTPMETHOD {
|
||||||
|
methods[val] = val
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
methods[method] = method
|
||||||
|
}
|
||||||
|
route.methods = methods
|
||||||
|
for k := range methods {
|
||||||
|
if k == "*" {
|
||||||
|
for m := range HTTPMETHOD {
|
||||||
|
p.addToRouter(m, pattern, route)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.addToRouter(k, pattern, route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler add user defined Handler
|
||||||
|
func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) {
|
||||||
|
route := &ControllerInfo{}
|
||||||
|
route.pattern = pattern
|
||||||
|
route.routerType = routerTypeHandler
|
||||||
|
route.handler = h
|
||||||
|
if len(options) > 0 {
|
||||||
|
if _, ok := options[0].(bool); ok {
|
||||||
|
pattern = path.Join(pattern, "?:all(.*)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for m := range HTTPMETHOD {
|
||||||
|
p.addToRouter(m, pattern, route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAuto router to ControllerRegister.
|
||||||
|
// example beego.AddAuto(&MainContorlller{}),
|
||||||
|
// MainController has method List and Page.
|
||||||
|
// visit the url /main/list to execute List function
|
||||||
|
// /main/page to execute Page function.
|
||||||
|
func (p *ControllerRegister) AddAuto(c ControllerInterface) {
|
||||||
|
p.AddAutoPrefix("/", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAutoPrefix Add auto router to ControllerRegister with prefix.
|
||||||
|
// example beego.AddAutoPrefix("/admin",&MainContorlller{}),
|
||||||
|
// MainController has method List and Page.
|
||||||
|
// visit the url /admin/main/list to execute List function
|
||||||
|
// /admin/main/page to execute Page function.
|
||||||
|
func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) {
|
||||||
|
reflectVal := reflect.ValueOf(c)
|
||||||
|
rt := reflectVal.Type()
|
||||||
|
ct := reflect.Indirect(reflectVal).Type()
|
||||||
|
controllerName := strings.TrimSuffix(ct.Name(), "Controller")
|
||||||
|
for i := 0; i < rt.NumMethod(); i++ {
|
||||||
|
if !utils.InSlice(rt.Method(i).Name, exceptMethod) {
|
||||||
|
route := &ControllerInfo{}
|
||||||
|
route.routerType = routerTypeBeego
|
||||||
|
route.methods = map[string]string{"*": rt.Method(i).Name}
|
||||||
|
route.controllerType = ct
|
||||||
|
pattern := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name), "*")
|
||||||
|
patternInit := path.Join(prefix, controllerName, rt.Method(i).Name, "*")
|
||||||
|
patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name))
|
||||||
|
patternFixInit := path.Join(prefix, controllerName, rt.Method(i).Name)
|
||||||
|
route.pattern = pattern
|
||||||
|
for m := range HTTPMETHOD {
|
||||||
|
p.addToRouter(m, pattern, route)
|
||||||
|
p.addToRouter(m, patternInit, route)
|
||||||
|
p.addToRouter(m, patternFix, route)
|
||||||
|
p.addToRouter(m, patternFixInit, route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertFilter Add a FilterFunc with pattern rule and action constant.
|
||||||
|
// params is for:
|
||||||
|
// 1. setting the returnOnOutput value (false allows multiple filters to execute)
|
||||||
|
// 2. determining whether or not params need to be reset.
|
||||||
|
func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error {
|
||||||
|
mr := &FilterRouter{
|
||||||
|
tree: NewTree(),
|
||||||
|
pattern: pattern,
|
||||||
|
filterFunc: filter,
|
||||||
|
returnOnOutput: true,
|
||||||
|
}
|
||||||
|
if !BConfig.RouterCaseSensitive {
|
||||||
|
mr.pattern = strings.ToLower(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
paramsLen := len(params)
|
||||||
|
if paramsLen > 0 {
|
||||||
|
mr.returnOnOutput = params[0]
|
||||||
|
}
|
||||||
|
if paramsLen > 1 {
|
||||||
|
mr.resetParams = params[1]
|
||||||
|
}
|
||||||
|
mr.tree.AddRouter(pattern, true)
|
||||||
|
return p.insertFilterRouter(pos, mr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add Filter into
|
||||||
|
func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) {
|
||||||
|
if pos < BeforeStatic || pos > FinishRouter {
|
||||||
|
err = fmt.Errorf("can not find your filter position")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.enableFilter = true
|
||||||
|
p.filters[pos] = append(p.filters[pos], mr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLFor does another controller handler in this request function.
|
||||||
|
// it can access any controller method.
|
||||||
|
func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) string {
|
||||||
|
paths := strings.Split(endpoint, ".")
|
||||||
|
if len(paths) <= 1 {
|
||||||
|
logs.Warn("urlfor endpoint must like path.controller.method")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(values)%2 != 0 {
|
||||||
|
logs.Warn("urlfor params must key-value pair")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
params := make(map[string]string)
|
||||||
|
if len(values) > 0 {
|
||||||
|
key := ""
|
||||||
|
for k, v := range values {
|
||||||
|
if k%2 == 0 {
|
||||||
|
key = fmt.Sprint(v)
|
||||||
|
} else {
|
||||||
|
params[key] = fmt.Sprint(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controllName := strings.Join(paths[:len(paths)-1], "/")
|
||||||
|
methodName := paths[len(paths)-1]
|
||||||
|
for m, t := range p.routers {
|
||||||
|
ok, url := p.geturl(t, "/", controllName, methodName, params, m)
|
||||||
|
if ok {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
|
||||||
|
for _, subtree := range t.fixrouters {
|
||||||
|
u := path.Join(url, subtree.prefix)
|
||||||
|
ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
|
||||||
|
if ok {
|
||||||
|
return ok, u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.wildcard != nil {
|
||||||
|
u := path.Join(url, urlPlaceholder)
|
||||||
|
ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
|
||||||
|
if ok {
|
||||||
|
return ok, u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, l := range t.leaves {
|
||||||
|
if c, ok := l.runObject.(*ControllerInfo); ok {
|
||||||
|
if c.routerType == routerTypeBeego &&
|
||||||
|
strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) {
|
||||||
|
find := false
|
||||||
|
if HTTPMETHOD[strings.ToUpper(methodName)] {
|
||||||
|
if len(c.methods) == 0 {
|
||||||
|
find = true
|
||||||
|
} else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) {
|
||||||
|
find = true
|
||||||
|
} else if m, ok = c.methods["*"]; ok && m == methodName {
|
||||||
|
find = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !find {
|
||||||
|
for m, md := range c.methods {
|
||||||
|
if (m == "*" || m == httpMethod) && md == methodName {
|
||||||
|
find = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if find {
|
||||||
|
if l.regexps == nil {
|
||||||
|
if len(l.wildcards) == 0 {
|
||||||
|
return true, strings.Replace(url, "/"+urlPlaceholder, "", 1) + toURL(params)
|
||||||
|
}
|
||||||
|
if len(l.wildcards) == 1 {
|
||||||
|
if v, ok := params[l.wildcards[0]]; ok {
|
||||||
|
delete(params, l.wildcards[0])
|
||||||
|
return true, strings.Replace(url, urlPlaceholder, v, 1) + toURL(params)
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
if len(l.wildcards) == 3 && l.wildcards[0] == "." {
|
||||||
|
if p, ok := params[":path"]; ok {
|
||||||
|
if e, isok := params[":ext"]; isok {
|
||||||
|
delete(params, ":path")
|
||||||
|
delete(params, ":ext")
|
||||||
|
return true, strings.Replace(url, urlPlaceholder, p+"."+e, -1) + toURL(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canskip := false
|
||||||
|
for _, v := range l.wildcards {
|
||||||
|
if v == ":" {
|
||||||
|
canskip = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if u, ok := params[v]; ok {
|
||||||
|
delete(params, v)
|
||||||
|
url = strings.Replace(url, urlPlaceholder, u, 1)
|
||||||
|
} else {
|
||||||
|
if canskip {
|
||||||
|
canskip = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, url + toURL(params)
|
||||||
|
}
|
||||||
|
var i int
|
||||||
|
var startreg bool
|
||||||
|
regurl := ""
|
||||||
|
for _, v := range strings.Trim(l.regexps.String(), "^$") {
|
||||||
|
if v == '(' {
|
||||||
|
startreg = true
|
||||||
|
continue
|
||||||
|
} else if v == ')' {
|
||||||
|
startreg = false
|
||||||
|
if v, ok := params[l.wildcards[i]]; ok {
|
||||||
|
delete(params, l.wildcards[i])
|
||||||
|
regurl = regurl + v
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if !startreg {
|
||||||
|
regurl = string(append([]rune(regurl), v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l.regexps.MatchString(regurl) {
|
||||||
|
ps := strings.Split(regurl, "/")
|
||||||
|
for _, p := range ps {
|
||||||
|
url = strings.Replace(url, urlPlaceholder, p, 1)
|
||||||
|
}
|
||||||
|
return true, url + toURL(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath string, pos int) (started bool) {
|
||||||
|
var preFilterParams map[string]string
|
||||||
|
for _, filterR := range p.filters[pos] {
|
||||||
|
if filterR.returnOnOutput && context.ResponseWriter.Started {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if filterR.resetParams {
|
||||||
|
preFilterParams = context.Input.Params()
|
||||||
|
}
|
||||||
|
if ok := filterR.ValidRouter(urlPath, context); ok {
|
||||||
|
filterR.filterFunc(context)
|
||||||
|
if filterR.resetParams {
|
||||||
|
context.Input.ResetParams()
|
||||||
|
for k, v := range preFilterParams {
|
||||||
|
context.Input.SetParam(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if filterR.returnOnOutput && context.ResponseWriter.Started {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement http.Handler interface.
|
||||||
|
func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
startTime := time.Now()
|
||||||
|
var (
|
||||||
|
runRouter reflect.Type
|
||||||
|
findRouter bool
|
||||||
|
runMethod string
|
||||||
|
methodParams []*param.MethodParam
|
||||||
|
routerInfo *ControllerInfo
|
||||||
|
isRunnable bool
|
||||||
|
)
|
||||||
|
context := p.pool.Get().(*beecontext.Context)
|
||||||
|
context.Reset(rw, r)
|
||||||
|
|
||||||
|
defer p.pool.Put(context)
|
||||||
|
if BConfig.RecoverFunc != nil {
|
||||||
|
defer BConfig.RecoverFunc(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Output.EnableGzip = BConfig.EnableGzip
|
||||||
|
|
||||||
|
if BConfig.RunMode == DEV {
|
||||||
|
context.Output.Header("Server", BConfig.ServerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlPath = r.URL.Path
|
||||||
|
|
||||||
|
if !BConfig.RouterCaseSensitive {
|
||||||
|
urlPath = strings.ToLower(urlPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter wrong http method
|
||||||
|
if !HTTPMETHOD[r.Method] {
|
||||||
|
http.Error(rw, "Method Not Allowed", 405)
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter for static file
|
||||||
|
if len(p.filters[BeforeStatic]) > 0 && p.execFilter(context, urlPath, BeforeStatic) {
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
|
||||||
|
serverStaticRouter(context)
|
||||||
|
|
||||||
|
if context.ResponseWriter.Started {
|
||||||
|
findRouter = true
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||||
|
if BConfig.CopyRequestBody && !context.Input.IsUpload() {
|
||||||
|
context.Input.CopyBody(BConfig.MaxMemory)
|
||||||
|
}
|
||||||
|
context.Input.ParseFormOrMulitForm(BConfig.MaxMemory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// session init
|
||||||
|
if BConfig.WebConfig.Session.SessionOn {
|
||||||
|
var err error
|
||||||
|
context.Input.CruSession, err = GlobalSessions.SessionStart(rw, r)
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err)
|
||||||
|
exception("503", context)
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if context.Input.CruSession != nil {
|
||||||
|
context.Input.CruSession.SessionRelease(rw)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if len(p.filters[BeforeRouter]) > 0 && p.execFilter(context, urlPath, BeforeRouter) {
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
// User can define RunController and RunMethod in filter
|
||||||
|
if context.Input.RunController != nil && context.Input.RunMethod != "" {
|
||||||
|
findRouter = true
|
||||||
|
runMethod = context.Input.RunMethod
|
||||||
|
runRouter = context.Input.RunController
|
||||||
|
} else {
|
||||||
|
routerInfo, findRouter = p.FindRouter(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
//if no matches to url, throw a not found exception
|
||||||
|
if !findRouter {
|
||||||
|
exception("404", context)
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
if splat := context.Input.Param(":splat"); splat != "" {
|
||||||
|
for k, v := range strings.Split(splat, "/") {
|
||||||
|
context.Input.SetParam(strconv.Itoa(k), v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//execute middleware filters
|
||||||
|
if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) {
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
|
||||||
|
//check policies
|
||||||
|
if p.execPolicy(context, urlPath) {
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
|
||||||
|
if routerInfo != nil {
|
||||||
|
//store router pattern into context
|
||||||
|
context.Input.SetData("RouterPattern", routerInfo.pattern)
|
||||||
|
if routerInfo.routerType == routerTypeRESTFul {
|
||||||
|
if _, ok := routerInfo.methods[r.Method]; ok {
|
||||||
|
isRunnable = true
|
||||||
|
routerInfo.runFunction(context)
|
||||||
|
} else {
|
||||||
|
exception("405", context)
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
} else if routerInfo.routerType == routerTypeHandler {
|
||||||
|
isRunnable = true
|
||||||
|
routerInfo.handler.ServeHTTP(rw, r)
|
||||||
|
} else {
|
||||||
|
runRouter = routerInfo.controllerType
|
||||||
|
methodParams = routerInfo.methodParams
|
||||||
|
method := r.Method
|
||||||
|
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost {
|
||||||
|
method = http.MethodPut
|
||||||
|
}
|
||||||
|
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete {
|
||||||
|
method = http.MethodDelete
|
||||||
|
}
|
||||||
|
if m, ok := routerInfo.methods[method]; ok {
|
||||||
|
runMethod = m
|
||||||
|
} else if m, ok = routerInfo.methods["*"]; ok {
|
||||||
|
runMethod = m
|
||||||
|
} else {
|
||||||
|
runMethod = method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// also defined runRouter & runMethod from filter
|
||||||
|
if !isRunnable {
|
||||||
|
//Invoke the request handler
|
||||||
|
var execController ControllerInterface
|
||||||
|
if routerInfo.initialize != nil {
|
||||||
|
execController = routerInfo.initialize()
|
||||||
|
} else {
|
||||||
|
vc := reflect.New(runRouter)
|
||||||
|
var ok bool
|
||||||
|
execController, ok = vc.Interface().(ControllerInterface)
|
||||||
|
if !ok {
|
||||||
|
panic("controller is not ControllerInterface")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the controller init function
|
||||||
|
execController.Init(context, runRouter.Name(), runMethod, execController)
|
||||||
|
|
||||||
|
//call prepare function
|
||||||
|
execController.Prepare()
|
||||||
|
|
||||||
|
//if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf
|
||||||
|
if BConfig.WebConfig.EnableXSRF {
|
||||||
|
execController.XSRFToken()
|
||||||
|
if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut ||
|
||||||
|
(r.Method == http.MethodPost && (context.Input.Query("_method") == http.MethodDelete || context.Input.Query("_method") == http.MethodPut)) {
|
||||||
|
execController.CheckXSRFCookie()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
execController.URLMapping()
|
||||||
|
|
||||||
|
if !context.ResponseWriter.Started {
|
||||||
|
//exec main logic
|
||||||
|
switch runMethod {
|
||||||
|
case http.MethodGet:
|
||||||
|
execController.Get()
|
||||||
|
case http.MethodPost:
|
||||||
|
execController.Post()
|
||||||
|
case http.MethodDelete:
|
||||||
|
execController.Delete()
|
||||||
|
case http.MethodPut:
|
||||||
|
execController.Put()
|
||||||
|
case http.MethodHead:
|
||||||
|
execController.Head()
|
||||||
|
case http.MethodPatch:
|
||||||
|
execController.Patch()
|
||||||
|
case http.MethodOptions:
|
||||||
|
execController.Options()
|
||||||
|
default:
|
||||||
|
if !execController.HandlerFunc(runMethod) {
|
||||||
|
vc := reflect.ValueOf(execController)
|
||||||
|
method := vc.MethodByName(runMethod)
|
||||||
|
in := param.ConvertParams(methodParams, method.Type(), context)
|
||||||
|
out := method.Call(in)
|
||||||
|
|
||||||
|
//For backward compatibility we only handle response if we had incoming methodParams
|
||||||
|
if methodParams != nil {
|
||||||
|
p.handleParamResponse(context, execController, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//render template
|
||||||
|
if !context.ResponseWriter.Started && context.Output.Status == 0 {
|
||||||
|
if BConfig.WebConfig.AutoRender {
|
||||||
|
if err := execController.Render(); err != nil {
|
||||||
|
logs.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish all runRouter. release resource
|
||||||
|
execController.Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
//execute middleware filters
|
||||||
|
if len(p.filters[AfterExec]) > 0 && p.execFilter(context, urlPath, AfterExec) {
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.filters[FinishRouter]) > 0 && p.execFilter(context, urlPath, FinishRouter) {
|
||||||
|
goto Admin
|
||||||
|
}
|
||||||
|
|
||||||
|
Admin:
|
||||||
|
//admin module record QPS
|
||||||
|
|
||||||
|
statusCode := context.ResponseWriter.Status
|
||||||
|
if statusCode == 0 {
|
||||||
|
statusCode = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
if BConfig.Listen.EnableAdmin {
|
||||||
|
timeDur := time.Since(startTime)
|
||||||
|
pattern := ""
|
||||||
|
if routerInfo != nil {
|
||||||
|
pattern = routerInfo.pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) {
|
||||||
|
if runRouter != nil {
|
||||||
|
go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur)
|
||||||
|
} else {
|
||||||
|
go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, "", timeDur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if BConfig.RunMode == DEV || BConfig.Log.AccessLogs {
|
||||||
|
timeDur := time.Since(startTime)
|
||||||
|
var devInfo string
|
||||||
|
|
||||||
|
iswin := (runtime.GOOS == "windows")
|
||||||
|
statusColor := logs.ColorByStatus(iswin, statusCode)
|
||||||
|
methodColor := logs.ColorByMethod(iswin, r.Method)
|
||||||
|
resetColor := logs.ColorByMethod(iswin, "")
|
||||||
|
if BConfig.Log.AccessLogsFormat != "" {
|
||||||
|
record := &logs.AccessLogRecord{
|
||||||
|
RemoteAddr: context.Input.IP(),
|
||||||
|
RequestTime: startTime,
|
||||||
|
RequestMethod: r.Method,
|
||||||
|
Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
|
||||||
|
ServerProtocol: r.Proto,
|
||||||
|
Host: r.Host,
|
||||||
|
Status: statusCode,
|
||||||
|
ElapsedTime: timeDur,
|
||||||
|
HTTPReferrer: r.Header.Get("Referer"),
|
||||||
|
HTTPUserAgent: r.Header.Get("User-Agent"),
|
||||||
|
RemoteUser: r.Header.Get("Remote-User"),
|
||||||
|
BodyBytesSent: 0, //@todo this one is missing!
|
||||||
|
}
|
||||||
|
logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
|
||||||
|
} else {
|
||||||
|
if findRouter {
|
||||||
|
if routerInfo != nil {
|
||||||
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode,
|
||||||
|
resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
|
||||||
|
routerInfo.pattern)
|
||||||
|
} else {
|
||||||
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
||||||
|
timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
||||||
|
timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
|
||||||
|
}
|
||||||
|
if iswin {
|
||||||
|
logs.W32Debug(devInfo)
|
||||||
|
} else {
|
||||||
|
logs.Debug(devInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Call WriteHeader if status code has been set changed
|
||||||
|
if context.Output.Status != 0 {
|
||||||
|
context.ResponseWriter.WriteHeader(context.Output.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, execController ControllerInterface, results []reflect.Value) {
|
||||||
|
//looping in reverse order for the case when both error and value are returned and error sets the response status code
|
||||||
|
for i := len(results) - 1; i >= 0; i-- {
|
||||||
|
result := results[i]
|
||||||
|
if result.Kind() != reflect.Interface || !result.IsNil() {
|
||||||
|
resultValue := result.Interface()
|
||||||
|
context.RenderMethodResult(resultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !context.ResponseWriter.Started && context.Output.Status == 0 {
|
||||||
|
context.Output.SetStatus(200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindRouter Find Router info for URL
|
||||||
|
func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *ControllerInfo, isFind bool) {
|
||||||
|
var urlPath = context.Input.URL()
|
||||||
|
if !BConfig.RouterCaseSensitive {
|
||||||
|
urlPath = strings.ToLower(urlPath)
|
||||||
|
}
|
||||||
|
httpMethod := context.Input.Method()
|
||||||
|
if t, ok := p.routers[httpMethod]; ok {
|
||||||
|
runObject := t.Match(urlPath, context)
|
||||||
|
if r, ok := runObject.(*ControllerInfo); ok {
|
||||||
|
return r, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func toURL(params map[string]string) string {
|
||||||
|
if len(params) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
u := "?"
|
||||||
|
for k, v := range params {
|
||||||
|
u += k + "=" + v + "&"
|
||||||
|
}
|
||||||
|
return strings.TrimRight(u, "&")
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
// 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"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/context"
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNotStaticRequest = errors.New("request not a static file request")
|
||||||
|
|
||||||
|
func serverStaticRouter(ctx *context.Context) {
|
||||||
|
if ctx.Input.Method() != "GET" && ctx.Input.Method() != "HEAD" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forbidden, filePath, fileInfo, err := lookupFile(ctx)
|
||||||
|
if err == errNotStaticRequest {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if forbidden {
|
||||||
|
exception("403", ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if filePath == "" || fileInfo == nil {
|
||||||
|
if BConfig.RunMode == DEV {
|
||||||
|
logs.Warn("Can't find/open the file:", filePath, err)
|
||||||
|
}
|
||||||
|
http.NotFound(ctx.ResponseWriter, ctx.Request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if fileInfo.IsDir() {
|
||||||
|
requestURL := ctx.Input.URL()
|
||||||
|
if requestURL[len(requestURL)-1] != '/' {
|
||||||
|
redirectURL := requestURL + "/"
|
||||||
|
if ctx.Request.URL.RawQuery != "" {
|
||||||
|
redirectURL = redirectURL + "?" + ctx.Request.URL.RawQuery
|
||||||
|
}
|
||||||
|
ctx.Redirect(302, redirectURL)
|
||||||
|
} else {
|
||||||
|
//serveFile will list dir
|
||||||
|
http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var enableCompress = BConfig.EnableGzip && isStaticCompress(filePath)
|
||||||
|
var acceptEncoding string
|
||||||
|
if enableCompress {
|
||||||
|
acceptEncoding = context.ParseEncoding(ctx.Request)
|
||||||
|
}
|
||||||
|
b, n, sch, err := openFile(filePath, fileInfo, acceptEncoding)
|
||||||
|
if err != nil {
|
||||||
|
if BConfig.RunMode == DEV {
|
||||||
|
logs.Warn("Can't compress the file:", filePath, err)
|
||||||
|
}
|
||||||
|
http.NotFound(ctx.ResponseWriter, ctx.Request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b {
|
||||||
|
ctx.Output.Header("Content-Encoding", n)
|
||||||
|
} else {
|
||||||
|
ctx.Output.Header("Content-Length", strconv.FormatInt(sch.size, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch)
|
||||||
|
}
|
||||||
|
|
||||||
|
type serveContentHolder struct {
|
||||||
|
*bytes.Reader
|
||||||
|
modTime time.Time
|
||||||
|
size int64
|
||||||
|
encoding string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
staticFileMap = make(map[string]*serveContentHolder)
|
||||||
|
mapLock sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, error) {
|
||||||
|
mapKey := acceptEncoding + ":" + filePath
|
||||||
|
mapLock.RLock()
|
||||||
|
mapFile := staticFileMap[mapKey]
|
||||||
|
mapLock.RUnlock()
|
||||||
|
if isOk(mapFile, fi) {
|
||||||
|
return mapFile.encoding != "", mapFile.encoding, mapFile, nil
|
||||||
|
}
|
||||||
|
mapLock.Lock()
|
||||||
|
defer mapLock.Unlock()
|
||||||
|
if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
var bufferWriter bytes.Buffer
|
||||||
|
_, n, err := context.WriteFile(acceptEncoding, &bufferWriter, file)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", nil, err
|
||||||
|
}
|
||||||
|
mapFile = &serveContentHolder{Reader: bytes.NewReader(bufferWriter.Bytes()), modTime: fi.ModTime(), size: int64(bufferWriter.Len()), encoding: n}
|
||||||
|
staticFileMap[mapKey] = mapFile
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapFile.encoding != "", mapFile.encoding, mapFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOk(s *serveContentHolder, fi os.FileInfo) bool {
|
||||||
|
if s == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s.modTime == fi.ModTime() && s.size == fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isStaticCompress detect static files
|
||||||
|
func isStaticCompress(filePath string) bool {
|
||||||
|
for _, statExtension := range BConfig.WebConfig.StaticExtensionsToGzip {
|
||||||
|
if strings.HasSuffix(strings.ToLower(filePath), strings.ToLower(statExtension)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// searchFile search the file by url path
|
||||||
|
// if none the static file prefix matches ,return notStaticRequestErr
|
||||||
|
func searchFile(ctx *context.Context) (string, os.FileInfo, error) {
|
||||||
|
requestPath := filepath.ToSlash(filepath.Clean(ctx.Request.URL.Path))
|
||||||
|
// special processing : favicon.ico/robots.txt can be in any static dir
|
||||||
|
if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
|
||||||
|
file := path.Join(".", requestPath)
|
||||||
|
if fi, _ := os.Stat(file); fi != nil {
|
||||||
|
return file, fi, nil
|
||||||
|
}
|
||||||
|
for _, staticDir := range BConfig.WebConfig.StaticDir {
|
||||||
|
filePath := path.Join(staticDir, requestPath)
|
||||||
|
if fi, _ := os.Stat(filePath); fi != nil {
|
||||||
|
return filePath, fi, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil, errNotStaticRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
for prefix, staticDir := range BConfig.WebConfig.StaticDir {
|
||||||
|
if !strings.Contains(requestPath, prefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filePath := path.Join(staticDir, requestPath[len(prefix):])
|
||||||
|
if fi, err := os.Stat(filePath); fi != nil {
|
||||||
|
return filePath, fi, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil, errNotStaticRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookupFile find the file to serve
|
||||||
|
// if the file is dir ,search the index.html as default file( MUST NOT A DIR also)
|
||||||
|
// if the index.html not exist or is a dir, give a forbidden response depending on DirectoryIndex
|
||||||
|
func lookupFile(ctx *context.Context) (bool, string, os.FileInfo, error) {
|
||||||
|
fp, fi, err := searchFile(ctx)
|
||||||
|
if fp == "" || fi == nil {
|
||||||
|
return false, "", nil, err
|
||||||
|
}
|
||||||
|
if !fi.IsDir() {
|
||||||
|
return false, fp, fi, err
|
||||||
|
}
|
||||||
|
if requestURL := ctx.Input.URL(); requestURL[len(requestURL)-1] == '/' {
|
||||||
|
ifp := filepath.Join(fp, "index.html")
|
||||||
|
if ifi, _ := os.Stat(ifp); ifi != nil && ifi.Mode().IsRegular() {
|
||||||
|
return false, ifp, ifi, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !BConfig.WebConfig.DirectoryIndex, fp, fi, err
|
||||||
|
}
|
|
@ -0,0 +1,374 @@
|
||||||
|
// 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"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
"github.com/astaxie/beego/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
beegoTplFuncMap = make(template.FuncMap)
|
||||||
|
beeViewPathTemplateLocked = false
|
||||||
|
// beeViewPathTemplates caching map and supported template file extensions per view
|
||||||
|
beeViewPathTemplates = make(map[string]map[string]*template.Template)
|
||||||
|
templatesLock sync.RWMutex
|
||||||
|
// beeTemplateExt stores the template extension which will build
|
||||||
|
beeTemplateExt = []string{"tpl", "html"}
|
||||||
|
// beeTemplatePreprocessors stores associations of extension -> preprocessor handler
|
||||||
|
beeTemplateEngines = map[string]templatePreProcessor{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecuteTemplate applies the template with name to the specified data object,
|
||||||
|
// writing the output to wr.
|
||||||
|
// A template will be executed safely in parallel.
|
||||||
|
func ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
|
||||||
|
return ExecuteViewPathTemplate(wr, name, BConfig.WebConfig.ViewsPath, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteViewPathTemplate applies the template with name and from specific viewPath to the specified data object,
|
||||||
|
// writing the output to wr.
|
||||||
|
// A template will be executed safely in parallel.
|
||||||
|
func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data interface{}) error {
|
||||||
|
if BConfig.RunMode == DEV {
|
||||||
|
templatesLock.RLock()
|
||||||
|
defer templatesLock.RUnlock()
|
||||||
|
}
|
||||||
|
if beeTemplates, ok := beeViewPathTemplates[viewPath]; ok {
|
||||||
|
if t, ok := beeTemplates[name]; ok {
|
||||||
|
var err error
|
||||||
|
if t.Lookup(name) != nil {
|
||||||
|
err = t.ExecuteTemplate(wr, name, data)
|
||||||
|
} else {
|
||||||
|
err = t.Execute(wr, data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logs.Trace("template Execute err:", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
panic("can't find templatefile in the path:" + viewPath + "/" + name)
|
||||||
|
}
|
||||||
|
panic("Unknown view path:" + viewPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
beegoTplFuncMap["dateformat"] = DateFormat
|
||||||
|
beegoTplFuncMap["date"] = Date
|
||||||
|
beegoTplFuncMap["compare"] = Compare
|
||||||
|
beegoTplFuncMap["compare_not"] = CompareNot
|
||||||
|
beegoTplFuncMap["not_nil"] = NotNil
|
||||||
|
beegoTplFuncMap["not_null"] = NotNil
|
||||||
|
beegoTplFuncMap["substr"] = Substr
|
||||||
|
beegoTplFuncMap["html2str"] = HTML2str
|
||||||
|
beegoTplFuncMap["str2html"] = Str2html
|
||||||
|
beegoTplFuncMap["htmlquote"] = Htmlquote
|
||||||
|
beegoTplFuncMap["htmlunquote"] = Htmlunquote
|
||||||
|
beegoTplFuncMap["renderform"] = RenderForm
|
||||||
|
beegoTplFuncMap["assets_js"] = AssetsJs
|
||||||
|
beegoTplFuncMap["assets_css"] = AssetsCSS
|
||||||
|
beegoTplFuncMap["config"] = GetConfig
|
||||||
|
beegoTplFuncMap["map_get"] = MapGet
|
||||||
|
|
||||||
|
// Comparisons
|
||||||
|
beegoTplFuncMap["eq"] = eq // ==
|
||||||
|
beegoTplFuncMap["ge"] = ge // >=
|
||||||
|
beegoTplFuncMap["gt"] = gt // >
|
||||||
|
beegoTplFuncMap["le"] = le // <=
|
||||||
|
beegoTplFuncMap["lt"] = lt // <
|
||||||
|
beegoTplFuncMap["ne"] = ne // !=
|
||||||
|
|
||||||
|
beegoTplFuncMap["urlfor"] = URLFor // build a URL to match a Controller and it's method
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFuncMap let user to register a func in the template.
|
||||||
|
func AddFuncMap(key string, fn interface{}) error {
|
||||||
|
beegoTplFuncMap[key] = fn
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type templatePreProcessor func(root, path string, funcs template.FuncMap) (*template.Template, error)
|
||||||
|
|
||||||
|
type templateFile struct {
|
||||||
|
root string
|
||||||
|
files map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// visit will make the paths into two part,the first is subDir (without tf.root),the second is full path(without tf.root).
|
||||||
|
// if tf.root="views" and
|
||||||
|
// paths is "views/errors/404.html",the subDir will be "errors",the file will be "errors/404.html"
|
||||||
|
// paths is "views/admin/errors/404.html",the subDir will be "admin/errors",the file will be "admin/errors/404.html"
|
||||||
|
func (tf *templateFile) visit(paths string, f os.FileInfo, err error) error {
|
||||||
|
if f == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f.IsDir() || (f.Mode()&os.ModeSymlink) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !HasTemplateExt(paths) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
replace := strings.NewReplacer("\\", "/")
|
||||||
|
file := strings.TrimLeft(replace.Replace(paths[len(tf.root):]), "/")
|
||||||
|
subDir := filepath.Dir(file)
|
||||||
|
|
||||||
|
tf.files[subDir] = append(tf.files[subDir], file)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasTemplateExt return this path contains supported template extension of beego or not.
|
||||||
|
func HasTemplateExt(paths string) bool {
|
||||||
|
for _, v := range beeTemplateExt {
|
||||||
|
if strings.HasSuffix(paths, "."+v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTemplateExt add new extension for template.
|
||||||
|
func AddTemplateExt(ext string) {
|
||||||
|
for _, v := range beeTemplateExt {
|
||||||
|
if v == ext {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
beeTemplateExt = append(beeTemplateExt, ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddViewPath adds a new path to the supported view paths.
|
||||||
|
//Can later be used by setting a controller ViewPath to this folder
|
||||||
|
//will panic if called after beego.Run()
|
||||||
|
func AddViewPath(viewPath string) error {
|
||||||
|
if beeViewPathTemplateLocked {
|
||||||
|
if _, exist := beeViewPathTemplates[viewPath]; exist {
|
||||||
|
return nil //Ignore if viewpath already exists
|
||||||
|
}
|
||||||
|
panic("Can not add new view paths after beego.Run()")
|
||||||
|
}
|
||||||
|
beeViewPathTemplates[viewPath] = make(map[string]*template.Template)
|
||||||
|
return BuildTemplate(viewPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lockViewPaths() {
|
||||||
|
beeViewPathTemplateLocked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildTemplate will build all template files in a directory.
|
||||||
|
// it makes beego can render any template file in view directory.
|
||||||
|
func BuildTemplate(dir string, files ...string) error {
|
||||||
|
if _, err := os.Stat(dir); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("dir open err")
|
||||||
|
}
|
||||||
|
beeTemplates, ok := beeViewPathTemplates[dir]
|
||||||
|
if !ok {
|
||||||
|
panic("Unknown view path: " + dir)
|
||||||
|
}
|
||||||
|
self := &templateFile{
|
||||||
|
root: dir,
|
||||||
|
files: make(map[string][]string),
|
||||||
|
}
|
||||||
|
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
||||||
|
return self.visit(path, f, err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("filepath.Walk() returned %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buildAllFiles := len(files) == 0
|
||||||
|
for _, v := range self.files {
|
||||||
|
for _, file := range v {
|
||||||
|
if buildAllFiles || utils.InSlice(file, files) {
|
||||||
|
templatesLock.Lock()
|
||||||
|
ext := filepath.Ext(file)
|
||||||
|
var t *template.Template
|
||||||
|
if len(ext) == 0 {
|
||||||
|
t, err = getTemplate(self.root, file, v...)
|
||||||
|
} else if fn, ok := beeTemplateEngines[ext[1:]]; ok {
|
||||||
|
t, err = fn(self.root, file, beegoTplFuncMap)
|
||||||
|
} else {
|
||||||
|
t, err = getTemplate(self.root, file, v...)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logs.Error("parse template err:", file, err)
|
||||||
|
templatesLock.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
beeTemplates[file] = t
|
||||||
|
templatesLock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTplDeep(root, file, parent string, t *template.Template) (*template.Template, [][]string, error) {
|
||||||
|
var fileAbsPath string
|
||||||
|
var rParent string
|
||||||
|
if filepath.HasPrefix(file, "../") {
|
||||||
|
rParent = filepath.Join(filepath.Dir(parent), file)
|
||||||
|
fileAbsPath = filepath.Join(root, filepath.Dir(parent), file)
|
||||||
|
} else {
|
||||||
|
rParent = file
|
||||||
|
fileAbsPath = filepath.Join(root, file)
|
||||||
|
}
|
||||||
|
if e := utils.FileExists(fileAbsPath); !e {
|
||||||
|
panic("can't find template file:" + file)
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadFile(fileAbsPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, [][]string{}, err
|
||||||
|
}
|
||||||
|
t, err = t.New(file).Parse(string(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, [][]string{}, err
|
||||||
|
}
|
||||||
|
reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*template[ ]+\"([^\"]+)\"")
|
||||||
|
allSub := reg.FindAllStringSubmatch(string(data), -1)
|
||||||
|
for _, m := range allSub {
|
||||||
|
if len(m) == 2 {
|
||||||
|
tl := t.Lookup(m[1])
|
||||||
|
if tl != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !HasTemplateExt(m[1]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, _, err = getTplDeep(root, m[1], rParent, t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, [][]string{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t, allSub, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTemplate(root, file string, others ...string) (t *template.Template, err error) {
|
||||||
|
t = template.New(file).Delims(BConfig.WebConfig.TemplateLeft, BConfig.WebConfig.TemplateRight).Funcs(beegoTplFuncMap)
|
||||||
|
var subMods [][]string
|
||||||
|
t, subMods, err = getTplDeep(root, file, "", t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t, err = _getTemplate(t, root, subMods, others...)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getTemplate(t0 *template.Template, root string, subMods [][]string, others ...string) (t *template.Template, err error) {
|
||||||
|
t = t0
|
||||||
|
for _, m := range subMods {
|
||||||
|
if len(m) == 2 {
|
||||||
|
tpl := t.Lookup(m[1])
|
||||||
|
if tpl != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//first check filename
|
||||||
|
for _, otherFile := range others {
|
||||||
|
if otherFile == m[1] {
|
||||||
|
var subMods1 [][]string
|
||||||
|
t, subMods1, err = getTplDeep(root, otherFile, "", t)
|
||||||
|
if err != nil {
|
||||||
|
logs.Trace("template parse file err:", err)
|
||||||
|
} else if len(subMods1) > 0 {
|
||||||
|
t, err = _getTemplate(t, root, subMods1, others...)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//second check define
|
||||||
|
for _, otherFile := range others {
|
||||||
|
var data []byte
|
||||||
|
fileAbsPath := filepath.Join(root, otherFile)
|
||||||
|
data, err = ioutil.ReadFile(fileAbsPath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*define[ ]+\"([^\"]+)\"")
|
||||||
|
allSub := reg.FindAllStringSubmatch(string(data), -1)
|
||||||
|
for _, sub := range allSub {
|
||||||
|
if len(sub) == 2 && sub[1] == m[1] {
|
||||||
|
var subMods1 [][]string
|
||||||
|
t, subMods1, err = getTplDeep(root, otherFile, "", t)
|
||||||
|
if err != nil {
|
||||||
|
logs.Trace("template parse file err:", err)
|
||||||
|
} else if len(subMods1) > 0 {
|
||||||
|
t, err = _getTemplate(t, root, subMods1, others...)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetViewsPath sets view directory path in beego application.
|
||||||
|
func SetViewsPath(path string) *App {
|
||||||
|
BConfig.WebConfig.ViewsPath = path
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStaticPath sets static directory path and proper url pattern in beego application.
|
||||||
|
// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public".
|
||||||
|
func SetStaticPath(url string, path string) *App {
|
||||||
|
if !strings.HasPrefix(url, "/") {
|
||||||
|
url = "/" + url
|
||||||
|
}
|
||||||
|
if url != "/" {
|
||||||
|
url = strings.TrimRight(url, "/")
|
||||||
|
}
|
||||||
|
BConfig.WebConfig.StaticDir[url] = path
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelStaticPath removes the static folder setting in this url pattern in beego application.
|
||||||
|
func DelStaticPath(url string) *App {
|
||||||
|
if !strings.HasPrefix(url, "/") {
|
||||||
|
url = "/" + url
|
||||||
|
}
|
||||||
|
if url != "/" {
|
||||||
|
url = strings.TrimRight(url, "/")
|
||||||
|
}
|
||||||
|
delete(BConfig.WebConfig.StaticDir, url)
|
||||||
|
return BeeApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTemplateEngine add a new templatePreProcessor which support extension
|
||||||
|
func AddTemplateEngine(extension string, fn templatePreProcessor) *App {
|
||||||
|
AddTemplateExt(extension)
|
||||||
|
beeTemplateEngines[extension] = fn
|
||||||
|
return BeeApp
|
||||||
|
}
|
|
@ -0,0 +1,766 @@
|
||||||
|
// 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"
|
||||||
|
"html/template"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
formatTime = "15:04:05"
|
||||||
|
formatDate = "2006-01-02"
|
||||||
|
formatDateTime = "2006-01-02 15:04:05"
|
||||||
|
formatDateTimeT = "2006-01-02T15:04:05"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Substr returns the substr from start to length.
|
||||||
|
func Substr(s string, start, length int) string {
|
||||||
|
bt := []rune(s)
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
if start > len(bt) {
|
||||||
|
start = start % len(bt)
|
||||||
|
}
|
||||||
|
var end int
|
||||||
|
if (start + length) > (len(bt) - 1) {
|
||||||
|
end = len(bt)
|
||||||
|
} else {
|
||||||
|
end = start + length
|
||||||
|
}
|
||||||
|
return string(bt[start:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML2str returns escaping text convert from html.
|
||||||
|
func HTML2str(html string) string {
|
||||||
|
|
||||||
|
re, _ := regexp.Compile(`\<[\S\s]+?\>`)
|
||||||
|
html = re.ReplaceAllStringFunc(html, strings.ToLower)
|
||||||
|
|
||||||
|
//remove STYLE
|
||||||
|
re, _ = regexp.Compile(`\<style[\S\s]+?\</style\>`)
|
||||||
|
html = re.ReplaceAllString(html, "")
|
||||||
|
|
||||||
|
//remove SCRIPT
|
||||||
|
re, _ = regexp.Compile(`\<script[\S\s]+?\</script\>`)
|
||||||
|
html = re.ReplaceAllString(html, "")
|
||||||
|
|
||||||
|
re, _ = regexp.Compile(`\<[\S\s]+?\>`)
|
||||||
|
html = re.ReplaceAllString(html, "\n")
|
||||||
|
|
||||||
|
re, _ = regexp.Compile(`\s{2,}`)
|
||||||
|
html = re.ReplaceAllString(html, "\n")
|
||||||
|
|
||||||
|
return strings.TrimSpace(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat"
|
||||||
|
func DateFormat(t time.Time, layout string) (datestring string) {
|
||||||
|
datestring = t.Format(layout)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DateFormat pattern rules.
|
||||||
|
var datePatterns = []string{
|
||||||
|
// year
|
||||||
|
"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
|
||||||
|
"y", "06", //A two digit representation of a year Examples: 99 or 03
|
||||||
|
|
||||||
|
// month
|
||||||
|
"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
|
||||||
|
"n", "1", // Numeric representation of a month, without leading zeros 1 through 12
|
||||||
|
"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
|
||||||
|
"F", "January", // A full textual representation of a month, such as January or March January through December
|
||||||
|
|
||||||
|
// day
|
||||||
|
"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
|
||||||
|
"j", "2", // Day of the month without leading zeros 1 to 31
|
||||||
|
|
||||||
|
// week
|
||||||
|
"D", "Mon", // A textual representation of a day, three letters Mon through Sun
|
||||||
|
"l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
|
||||||
|
|
||||||
|
// time
|
||||||
|
"g", "3", // 12-hour format of an hour without leading zeros 1 through 12
|
||||||
|
"G", "15", // 24-hour format of an hour without leading zeros 0 through 23
|
||||||
|
"h", "03", // 12-hour format of an hour with leading zeros 01 through 12
|
||||||
|
"H", "15", // 24-hour format of an hour with leading zeros 00 through 23
|
||||||
|
|
||||||
|
"a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
|
||||||
|
"A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
|
||||||
|
|
||||||
|
"i", "04", // Minutes with leading zeros 00 to 59
|
||||||
|
"s", "05", // Seconds, with leading zeros 00 through 59
|
||||||
|
|
||||||
|
// time zone
|
||||||
|
"T", "MST",
|
||||||
|
"P", "-07:00",
|
||||||
|
"O", "-0700",
|
||||||
|
|
||||||
|
// RFC 2822
|
||||||
|
"r", time.RFC1123Z,
|
||||||
|
}
|
||||||
|
|
||||||
|
// DateParse Parse Date use PHP time format.
|
||||||
|
func DateParse(dateString, format string) (time.Time, error) {
|
||||||
|
replacer := strings.NewReplacer(datePatterns...)
|
||||||
|
format = replacer.Replace(format)
|
||||||
|
return time.ParseInLocation(format, dateString, time.Local)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Date takes a PHP like date func to Go's time format.
|
||||||
|
func Date(t time.Time, format string) string {
|
||||||
|
replacer := strings.NewReplacer(datePatterns...)
|
||||||
|
format = replacer.Replace(format)
|
||||||
|
return t.Format(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare is a quick and dirty comparison function. It will convert whatever you give it to strings and see if the two values are equal.
|
||||||
|
// Whitespace is trimmed. Used by the template parser as "eq".
|
||||||
|
func Compare(a, b interface{}) (equal bool) {
|
||||||
|
equal = false
|
||||||
|
if strings.TrimSpace(fmt.Sprintf("%v", a)) == strings.TrimSpace(fmt.Sprintf("%v", b)) {
|
||||||
|
equal = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareNot !Compare
|
||||||
|
func CompareNot(a, b interface{}) (equal bool) {
|
||||||
|
return !Compare(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotNil the same as CompareNot
|
||||||
|
func NotNil(a interface{}) (isNil bool) {
|
||||||
|
return CompareNot(a, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfig get the Appconfig
|
||||||
|
func GetConfig(returnType, key string, defaultVal interface{}) (value interface{}, err error) {
|
||||||
|
switch returnType {
|
||||||
|
case "String":
|
||||||
|
value = AppConfig.String(key)
|
||||||
|
case "Bool":
|
||||||
|
value, err = AppConfig.Bool(key)
|
||||||
|
case "Int":
|
||||||
|
value, err = AppConfig.Int(key)
|
||||||
|
case "Int64":
|
||||||
|
value, err = AppConfig.Int64(key)
|
||||||
|
case "Float":
|
||||||
|
value, err = AppConfig.Float(key)
|
||||||
|
case "DIY":
|
||||||
|
value, err = AppConfig.DIY(key)
|
||||||
|
default:
|
||||||
|
err = errors.New("Config keys must be of type String, Bool, Int, Int64, Float, or DIY")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if reflect.TypeOf(returnType) != reflect.TypeOf(defaultVal) {
|
||||||
|
err = errors.New("defaultVal type does not match returnType")
|
||||||
|
} else {
|
||||||
|
value, err = defaultVal, nil
|
||||||
|
}
|
||||||
|
} else if reflect.TypeOf(value).Kind() == reflect.String {
|
||||||
|
if value == "" {
|
||||||
|
if reflect.TypeOf(defaultVal).Kind() != reflect.String {
|
||||||
|
err = errors.New("defaultVal type must be a String if the returnType is a String")
|
||||||
|
} else {
|
||||||
|
value = defaultVal.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Str2html Convert string to template.HTML type.
|
||||||
|
func Str2html(raw string) template.HTML {
|
||||||
|
return template.HTML(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Htmlquote returns quoted html string.
|
||||||
|
func Htmlquote(text string) string {
|
||||||
|
//HTML编码为实体符号
|
||||||
|
/*
|
||||||
|
Encodes `text` for raw use in HTML.
|
||||||
|
>>> htmlquote("<'&\\">")
|
||||||
|
'<'&">'
|
||||||
|
*/
|
||||||
|
|
||||||
|
text = html.EscapeString(text)
|
||||||
|
text = strings.NewReplacer(
|
||||||
|
`“`, "“",
|
||||||
|
`”`, "”",
|
||||||
|
` `, " ",
|
||||||
|
).Replace(text)
|
||||||
|
|
||||||
|
return strings.TrimSpace(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Htmlunquote returns unquoted html string.
|
||||||
|
func Htmlunquote(text string) string {
|
||||||
|
//实体符号解释为HTML
|
||||||
|
/*
|
||||||
|
Decodes `text` that's HTML quoted.
|
||||||
|
>>> htmlunquote('<'&">')
|
||||||
|
'<\\'&">'
|
||||||
|
*/
|
||||||
|
|
||||||
|
text = html.UnescapeString(text)
|
||||||
|
|
||||||
|
return strings.TrimSpace(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLFor returns url string with another registered controller handler with params.
|
||||||
|
// usage:
|
||||||
|
//
|
||||||
|
// URLFor(".index")
|
||||||
|
// print URLFor("index")
|
||||||
|
// router /login
|
||||||
|
// print URLFor("login")
|
||||||
|
// print URLFor("login", "next","/"")
|
||||||
|
// router /profile/:username
|
||||||
|
// print UrlFor("profile", ":username","John Doe")
|
||||||
|
// result:
|
||||||
|
// /
|
||||||
|
// /login
|
||||||
|
// /login?next=/
|
||||||
|
// /user/John%20Doe
|
||||||
|
//
|
||||||
|
// more detail http://beego.me/docs/mvc/controller/urlbuilding.md
|
||||||
|
func URLFor(endpoint string, values ...interface{}) string {
|
||||||
|
return BeeApp.Handlers.URLFor(endpoint, values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetsJs returns script tag with src string.
|
||||||
|
func AssetsJs(text string) template.HTML {
|
||||||
|
|
||||||
|
text = "<script src=\"" + text + "\"></script>"
|
||||||
|
|
||||||
|
return template.HTML(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetsCSS returns stylesheet link tag with src string.
|
||||||
|
func AssetsCSS(text string) template.HTML {
|
||||||
|
|
||||||
|
text = "<link href=\"" + text + "\" rel=\"stylesheet\" />"
|
||||||
|
|
||||||
|
return template.HTML(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseForm will parse form values to struct via tag.
|
||||||
|
// Support for anonymous struct.
|
||||||
|
func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) error {
|
||||||
|
for i := 0; i < objT.NumField(); i++ {
|
||||||
|
fieldV := objV.Field(i)
|
||||||
|
if !fieldV.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldT := objT.Field(i)
|
||||||
|
if fieldT.Anonymous && fieldT.Type.Kind() == reflect.Struct {
|
||||||
|
err := parseFormToStruct(form, fieldT.Type, fieldV)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := strings.Split(fieldT.Tag.Get("form"), ",")
|
||||||
|
var tag string
|
||||||
|
if len(tags) == 0 || len(tags[0]) == 0 {
|
||||||
|
tag = fieldT.Name
|
||||||
|
} else if tags[0] == "-" {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
tag = tags[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
value := form.Get(tag)
|
||||||
|
if len(value) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fieldT.Type.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" {
|
||||||
|
fieldV.SetBool(true)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" {
|
||||||
|
fieldV.SetBool(false)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b, err := strconv.ParseBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fieldV.SetBool(b)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
x, err := strconv.ParseInt(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fieldV.SetInt(x)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
x, err := strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fieldV.SetUint(x)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
x, err := strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fieldV.SetFloat(x)
|
||||||
|
case reflect.Interface:
|
||||||
|
fieldV.Set(reflect.ValueOf(value))
|
||||||
|
case reflect.String:
|
||||||
|
fieldV.SetString(value)
|
||||||
|
case reflect.Struct:
|
||||||
|
switch fieldT.Type.String() {
|
||||||
|
case "time.Time":
|
||||||
|
var (
|
||||||
|
t time.Time
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if len(value) >= 25 {
|
||||||
|
value = value[:25]
|
||||||
|
t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
|
||||||
|
} else if len(value) >= 19 {
|
||||||
|
if strings.Contains(value, "T") {
|
||||||
|
value = value[:19]
|
||||||
|
t, err = time.ParseInLocation(formatDateTimeT, value, time.Local)
|
||||||
|
} else {
|
||||||
|
value = value[:19]
|
||||||
|
t, err = time.ParseInLocation(formatDateTime, value, time.Local)
|
||||||
|
}
|
||||||
|
} else if len(value) >= 10 {
|
||||||
|
if len(value) > 10 {
|
||||||
|
value = value[:10]
|
||||||
|
}
|
||||||
|
t, err = time.ParseInLocation(formatDate, value, time.Local)
|
||||||
|
} else if len(value) >= 8 {
|
||||||
|
if len(value) > 8 {
|
||||||
|
value = value[:8]
|
||||||
|
}
|
||||||
|
t, err = time.ParseInLocation(formatTime, value, time.Local)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fieldV.Set(reflect.ValueOf(t))
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
if fieldT.Type == sliceOfInts {
|
||||||
|
formVals := form[tag]
|
||||||
|
fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals)))
|
||||||
|
for i := 0; i < len(formVals); i++ {
|
||||||
|
val, err := strconv.Atoi(formVals[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fieldV.Index(i).SetInt(int64(val))
|
||||||
|
}
|
||||||
|
} else if fieldT.Type == sliceOfStrings {
|
||||||
|
formVals := form[tag]
|
||||||
|
fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals)))
|
||||||
|
for i := 0; i < len(formVals); i++ {
|
||||||
|
fieldV.Index(i).SetString(formVals[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseForm will parse form values to struct via tag.
|
||||||
|
func ParseForm(form url.Values, obj interface{}) error {
|
||||||
|
objT := reflect.TypeOf(obj)
|
||||||
|
objV := reflect.ValueOf(obj)
|
||||||
|
if !isStructPtr(objT) {
|
||||||
|
return fmt.Errorf("%v must be a struct pointer", obj)
|
||||||
|
}
|
||||||
|
objT = objT.Elem()
|
||||||
|
objV = objV.Elem()
|
||||||
|
|
||||||
|
return parseFormToStruct(form, objT, objV)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sliceOfInts = reflect.TypeOf([]int(nil))
|
||||||
|
var sliceOfStrings = reflect.TypeOf([]string(nil))
|
||||||
|
|
||||||
|
var unKind = map[reflect.Kind]bool{
|
||||||
|
reflect.Uintptr: true,
|
||||||
|
reflect.Complex64: true,
|
||||||
|
reflect.Complex128: true,
|
||||||
|
reflect.Array: true,
|
||||||
|
reflect.Chan: true,
|
||||||
|
reflect.Func: true,
|
||||||
|
reflect.Map: true,
|
||||||
|
reflect.Ptr: true,
|
||||||
|
reflect.Slice: true,
|
||||||
|
reflect.Struct: true,
|
||||||
|
reflect.UnsafePointer: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderForm will render object to form html.
|
||||||
|
// obj must be a struct pointer.
|
||||||
|
func RenderForm(obj interface{}) template.HTML {
|
||||||
|
objT := reflect.TypeOf(obj)
|
||||||
|
objV := reflect.ValueOf(obj)
|
||||||
|
if !isStructPtr(objT) {
|
||||||
|
return template.HTML("")
|
||||||
|
}
|
||||||
|
objT = objT.Elem()
|
||||||
|
objV = objV.Elem()
|
||||||
|
|
||||||
|
var raw []string
|
||||||
|
for i := 0; i < objT.NumField(); i++ {
|
||||||
|
fieldV := objV.Field(i)
|
||||||
|
if !fieldV.CanSet() || unKind[fieldV.Kind()] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldT := objT.Field(i)
|
||||||
|
|
||||||
|
label, name, fType, id, class, ignored, required := parseFormTag(fieldT)
|
||||||
|
if ignored {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
raw = append(raw, renderFormField(label, name, fType, fieldV.Interface(), id, class, required))
|
||||||
|
}
|
||||||
|
return template.HTML(strings.Join(raw, "</br>"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderFormField returns a string containing HTML of a single form field.
|
||||||
|
func renderFormField(label, name, fType string, value interface{}, id string, class string, required bool) string {
|
||||||
|
if id != "" {
|
||||||
|
id = " id=\"" + id + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
if class != "" {
|
||||||
|
class = " class=\"" + class + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredString := ""
|
||||||
|
if required {
|
||||||
|
requiredString = " required"
|
||||||
|
}
|
||||||
|
|
||||||
|
if isValidForInput(fType) {
|
||||||
|
return fmt.Sprintf(`%v<input%v%v name="%v" type="%v" value="%v"%v>`, label, id, class, name, fType, value, requiredString)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(`%v<%v%v%v name="%v"%v>%v</%v>`, label, fType, id, class, name, requiredString, value, fType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidForInput checks if fType is a valid value for the `type` property of an HTML input element.
|
||||||
|
func isValidForInput(fType string) bool {
|
||||||
|
validInputTypes := strings.Fields("text password checkbox radio submit reset hidden image file button search email url tel number range date month week time datetime datetime-local color")
|
||||||
|
for _, validType := range validInputTypes {
|
||||||
|
if fType == validType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFormTag takes the stuct-tag of a StructField and parses the `form` value.
|
||||||
|
// returned are the form label, name-property, type and wether the field should be ignored.
|
||||||
|
func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool, required bool) {
|
||||||
|
tags := strings.Split(fieldT.Tag.Get("form"), ",")
|
||||||
|
label = fieldT.Name + ": "
|
||||||
|
name = fieldT.Name
|
||||||
|
fType = "text"
|
||||||
|
ignored = false
|
||||||
|
id = fieldT.Tag.Get("id")
|
||||||
|
class = fieldT.Tag.Get("class")
|
||||||
|
|
||||||
|
required = false
|
||||||
|
requiredField := fieldT.Tag.Get("required")
|
||||||
|
if requiredField != "-" && requiredField != "" {
|
||||||
|
required, _ = strconv.ParseBool(requiredField)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(tags) {
|
||||||
|
case 1:
|
||||||
|
if tags[0] == "-" {
|
||||||
|
ignored = true
|
||||||
|
}
|
||||||
|
if len(tags[0]) > 0 {
|
||||||
|
name = tags[0]
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if len(tags[0]) > 0 {
|
||||||
|
name = tags[0]
|
||||||
|
}
|
||||||
|
if len(tags[1]) > 0 {
|
||||||
|
fType = tags[1]
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if len(tags[0]) > 0 {
|
||||||
|
name = tags[0]
|
||||||
|
}
|
||||||
|
if len(tags[1]) > 0 {
|
||||||
|
fType = tags[1]
|
||||||
|
}
|
||||||
|
if len(tags[2]) > 0 {
|
||||||
|
label = tags[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isStructPtr(t reflect.Type) bool {
|
||||||
|
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
|
||||||
|
}
|
||||||
|
|
||||||
|
// go1.2 added template funcs. begin
|
||||||
|
var (
|
||||||
|
errBadComparisonType = errors.New("invalid type for comparison")
|
||||||
|
errBadComparison = errors.New("incompatible types for comparison")
|
||||||
|
errNoComparison = errors.New("missing argument for comparison")
|
||||||
|
)
|
||||||
|
|
||||||
|
type kind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
invalidKind kind = iota
|
||||||
|
boolKind
|
||||||
|
complexKind
|
||||||
|
intKind
|
||||||
|
floatKind
|
||||||
|
stringKind
|
||||||
|
uintKind
|
||||||
|
)
|
||||||
|
|
||||||
|
func basicKind(v reflect.Value) (kind, error) {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return boolKind, nil
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return intKind, nil
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return uintKind, nil
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return floatKind, nil
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
return complexKind, nil
|
||||||
|
case reflect.String:
|
||||||
|
return stringKind, nil
|
||||||
|
}
|
||||||
|
return invalidKind, errBadComparisonType
|
||||||
|
}
|
||||||
|
|
||||||
|
// eq evaluates the comparison a == b || a == c || ...
|
||||||
|
func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
|
||||||
|
v1 := reflect.ValueOf(arg1)
|
||||||
|
k1, err := basicKind(v1)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if len(arg2) == 0 {
|
||||||
|
return false, errNoComparison
|
||||||
|
}
|
||||||
|
for _, arg := range arg2 {
|
||||||
|
v2 := reflect.ValueOf(arg)
|
||||||
|
k2, err := basicKind(v2)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if k1 != k2 {
|
||||||
|
return false, errBadComparison
|
||||||
|
}
|
||||||
|
truth := false
|
||||||
|
switch k1 {
|
||||||
|
case boolKind:
|
||||||
|
truth = v1.Bool() == v2.Bool()
|
||||||
|
case complexKind:
|
||||||
|
truth = v1.Complex() == v2.Complex()
|
||||||
|
case floatKind:
|
||||||
|
truth = v1.Float() == v2.Float()
|
||||||
|
case intKind:
|
||||||
|
truth = v1.Int() == v2.Int()
|
||||||
|
case stringKind:
|
||||||
|
truth = v1.String() == v2.String()
|
||||||
|
case uintKind:
|
||||||
|
truth = v1.Uint() == v2.Uint()
|
||||||
|
default:
|
||||||
|
panic("invalid kind")
|
||||||
|
}
|
||||||
|
if truth {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ne evaluates the comparison a != b.
|
||||||
|
func ne(arg1, arg2 interface{}) (bool, error) {
|
||||||
|
// != is the inverse of ==.
|
||||||
|
equal, err := eq(arg1, arg2)
|
||||||
|
return !equal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// lt evaluates the comparison a < b.
|
||||||
|
func lt(arg1, arg2 interface{}) (bool, error) {
|
||||||
|
v1 := reflect.ValueOf(arg1)
|
||||||
|
k1, err := basicKind(v1)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
v2 := reflect.ValueOf(arg2)
|
||||||
|
k2, err := basicKind(v2)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if k1 != k2 {
|
||||||
|
return false, errBadComparison
|
||||||
|
}
|
||||||
|
truth := false
|
||||||
|
switch k1 {
|
||||||
|
case boolKind, complexKind:
|
||||||
|
return false, errBadComparisonType
|
||||||
|
case floatKind:
|
||||||
|
truth = v1.Float() < v2.Float()
|
||||||
|
case intKind:
|
||||||
|
truth = v1.Int() < v2.Int()
|
||||||
|
case stringKind:
|
||||||
|
truth = v1.String() < v2.String()
|
||||||
|
case uintKind:
|
||||||
|
truth = v1.Uint() < v2.Uint()
|
||||||
|
default:
|
||||||
|
panic("invalid kind")
|
||||||
|
}
|
||||||
|
return truth, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// le evaluates the comparison <= b.
|
||||||
|
func le(arg1, arg2 interface{}) (bool, error) {
|
||||||
|
// <= is < or ==.
|
||||||
|
lessThan, err := lt(arg1, arg2)
|
||||||
|
if lessThan || err != nil {
|
||||||
|
return lessThan, err
|
||||||
|
}
|
||||||
|
return eq(arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gt evaluates the comparison a > b.
|
||||||
|
func gt(arg1, arg2 interface{}) (bool, error) {
|
||||||
|
// > is the inverse of <=.
|
||||||
|
lessOrEqual, err := le(arg1, arg2)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return !lessOrEqual, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ge evaluates the comparison a >= b.
|
||||||
|
func ge(arg1, arg2 interface{}) (bool, error) {
|
||||||
|
// >= is the inverse of <.
|
||||||
|
lessThan, err := lt(arg1, arg2)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return !lessThan, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapGet getting value from map by keys
|
||||||
|
// usage:
|
||||||
|
// Data["m"] = map[string]interface{} {
|
||||||
|
// "a": 1,
|
||||||
|
// "1": map[string]float64{
|
||||||
|
// "c": 4,
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {{ map_get m "a" }} // return 1
|
||||||
|
// {{ map_get m 1 "c" }} // return 4
|
||||||
|
func MapGet(arg1 interface{}, arg2 ...interface{}) (interface{}, error) {
|
||||||
|
arg1Type := reflect.TypeOf(arg1)
|
||||||
|
arg1Val := reflect.ValueOf(arg1)
|
||||||
|
|
||||||
|
if arg1Type.Kind() == reflect.Map && len(arg2) > 0 {
|
||||||
|
// check whether arg2[0] type equals to arg1 key type
|
||||||
|
// if they are different, make conversion
|
||||||
|
arg2Val := reflect.ValueOf(arg2[0])
|
||||||
|
arg2Type := reflect.TypeOf(arg2[0])
|
||||||
|
if arg2Type.Kind() != arg1Type.Key().Kind() {
|
||||||
|
// convert arg2Value to string
|
||||||
|
var arg2ConvertedVal interface{}
|
||||||
|
arg2String := fmt.Sprintf("%v", arg2[0])
|
||||||
|
|
||||||
|
// convert string representation to any other type
|
||||||
|
switch arg1Type.Key().Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
arg2ConvertedVal, _ = strconv.ParseBool(arg2String)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
arg2ConvertedVal, _ = strconv.ParseInt(arg2String, 0, 64)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
arg2ConvertedVal, _ = strconv.ParseUint(arg2String, 0, 64)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
arg2ConvertedVal, _ = strconv.ParseFloat(arg2String, 64)
|
||||||
|
case reflect.String:
|
||||||
|
arg2ConvertedVal = arg2String
|
||||||
|
default:
|
||||||
|
arg2ConvertedVal = arg2Val.Interface()
|
||||||
|
}
|
||||||
|
arg2Val = reflect.ValueOf(arg2ConvertedVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
storedVal := arg1Val.MapIndex(arg2Val)
|
||||||
|
|
||||||
|
if storedVal.IsValid() {
|
||||||
|
var result interface{}
|
||||||
|
|
||||||
|
switch arg1Type.Elem().Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
result = storedVal.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
result = storedVal.Int()
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
result = storedVal.Uint()
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
result = storedVal.Float()
|
||||||
|
case reflect.String:
|
||||||
|
result = storedVal.String()
|
||||||
|
default:
|
||||||
|
result = storedVal.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is more keys, handle this recursively
|
||||||
|
if len(arg2) > 1 {
|
||||||
|
return MapGet(result, arg2[1:]...)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
|
@ -0,0 +1,585 @@
|
||||||
|
// 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 (
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/context"
|
||||||
|
"github.com/astaxie/beego/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
allowSuffixExt = []string{".json", ".xml", ".html"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tree has three elements: FixRouter/wildcard/leaves
|
||||||
|
// fixRouter stores Fixed Router
|
||||||
|
// wildcard stores params
|
||||||
|
// leaves store the endpoint information
|
||||||
|
type Tree struct {
|
||||||
|
//prefix set for static router
|
||||||
|
prefix string
|
||||||
|
//search fix route first
|
||||||
|
fixrouters []*Tree
|
||||||
|
//if set, failure to match fixrouters search then search wildcard
|
||||||
|
wildcard *Tree
|
||||||
|
//if set, failure to match wildcard search
|
||||||
|
leaves []*leafInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTree return a new Tree
|
||||||
|
func NewTree() *Tree {
|
||||||
|
return &Tree{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTree will add tree to the exist Tree
|
||||||
|
// prefix should has no params
|
||||||
|
func (t *Tree) AddTree(prefix string, tree *Tree) {
|
||||||
|
t.addtree(splitPath(prefix), tree, nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg string) {
|
||||||
|
if len(segments) == 0 {
|
||||||
|
panic("prefix should has path")
|
||||||
|
}
|
||||||
|
seg := segments[0]
|
||||||
|
iswild, params, regexpStr := splitSegment(seg)
|
||||||
|
// if it's ? meaning can igone this, so add one more rule for it
|
||||||
|
if len(params) > 0 && params[0] == ":" {
|
||||||
|
params = params[1:]
|
||||||
|
if len(segments[1:]) > 0 {
|
||||||
|
t.addtree(segments[1:], tree, append(wildcards, params...), reg)
|
||||||
|
} else {
|
||||||
|
filterTreeWithPrefix(tree, wildcards, reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Rule: /login/*/access match /login/2009/11/access
|
||||||
|
//if already has *, and when loop the access, should as a regexpStr
|
||||||
|
if !iswild && utils.InSlice(":splat", wildcards) {
|
||||||
|
iswild = true
|
||||||
|
regexpStr = seg
|
||||||
|
}
|
||||||
|
//Rule: /user/:id/*
|
||||||
|
if seg == "*" && len(wildcards) > 0 && reg == "" {
|
||||||
|
regexpStr = "(.+)"
|
||||||
|
}
|
||||||
|
if len(segments) == 1 {
|
||||||
|
if iswild {
|
||||||
|
if regexpStr != "" {
|
||||||
|
if reg == "" {
|
||||||
|
rr := ""
|
||||||
|
for _, w := range wildcards {
|
||||||
|
if w == ":splat" {
|
||||||
|
rr = rr + "(.+)/"
|
||||||
|
} else {
|
||||||
|
rr = rr + "([^/]+)/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
regexpStr = rr + regexpStr
|
||||||
|
} else {
|
||||||
|
regexpStr = "/" + regexpStr
|
||||||
|
}
|
||||||
|
} else if reg != "" {
|
||||||
|
if seg == "*.*" {
|
||||||
|
regexpStr = "([^.]+).(.+)"
|
||||||
|
} else {
|
||||||
|
for _, w := range params {
|
||||||
|
if w == "." || w == ":" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
regexpStr = "([^/]+)/" + regexpStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reg = strings.Trim(reg+"/"+regexpStr, "/")
|
||||||
|
filterTreeWithPrefix(tree, append(wildcards, params...), reg)
|
||||||
|
t.wildcard = tree
|
||||||
|
} else {
|
||||||
|
reg = strings.Trim(reg+"/"+regexpStr, "/")
|
||||||
|
filterTreeWithPrefix(tree, append(wildcards, params...), reg)
|
||||||
|
tree.prefix = seg
|
||||||
|
t.fixrouters = append(t.fixrouters, tree)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if iswild {
|
||||||
|
if t.wildcard == nil {
|
||||||
|
t.wildcard = NewTree()
|
||||||
|
}
|
||||||
|
if regexpStr != "" {
|
||||||
|
if reg == "" {
|
||||||
|
rr := ""
|
||||||
|
for _, w := range wildcards {
|
||||||
|
if w == ":splat" {
|
||||||
|
rr = rr + "(.+)/"
|
||||||
|
} else {
|
||||||
|
rr = rr + "([^/]+)/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
regexpStr = rr + regexpStr
|
||||||
|
} else {
|
||||||
|
regexpStr = "/" + regexpStr
|
||||||
|
}
|
||||||
|
} else if reg != "" {
|
||||||
|
if seg == "*.*" {
|
||||||
|
regexpStr = "([^.]+).(.+)"
|
||||||
|
params = params[1:]
|
||||||
|
} else {
|
||||||
|
for range params {
|
||||||
|
regexpStr = "([^/]+)/" + regexpStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if seg == "*.*" {
|
||||||
|
params = params[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/")
|
||||||
|
t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg)
|
||||||
|
} else {
|
||||||
|
subTree := NewTree()
|
||||||
|
subTree.prefix = seg
|
||||||
|
t.fixrouters = append(t.fixrouters, subTree)
|
||||||
|
subTree.addtree(segments[1:], tree, append(wildcards, params...), reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) {
|
||||||
|
for _, v := range t.fixrouters {
|
||||||
|
filterTreeWithPrefix(v, wildcards, reg)
|
||||||
|
}
|
||||||
|
if t.wildcard != nil {
|
||||||
|
filterTreeWithPrefix(t.wildcard, wildcards, reg)
|
||||||
|
}
|
||||||
|
for _, l := range t.leaves {
|
||||||
|
if reg != "" {
|
||||||
|
if l.regexps != nil {
|
||||||
|
l.wildcards = append(wildcards, l.wildcards...)
|
||||||
|
l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$")
|
||||||
|
} else {
|
||||||
|
for _, v := range l.wildcards {
|
||||||
|
if v == ":splat" {
|
||||||
|
reg = reg + "/(.+)"
|
||||||
|
} else {
|
||||||
|
reg = reg + "/([^/]+)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.regexps = regexp.MustCompile("^" + reg + "$")
|
||||||
|
l.wildcards = append(wildcards, l.wildcards...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.wildcards = append(wildcards, l.wildcards...)
|
||||||
|
if l.regexps != nil {
|
||||||
|
for _, w := range wildcards {
|
||||||
|
if w == ":splat" {
|
||||||
|
reg = "(.+)/" + reg
|
||||||
|
} else {
|
||||||
|
reg = "([^/]+)/" + reg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRouter call addseg function
|
||||||
|
func (t *Tree) AddRouter(pattern string, runObject interface{}) {
|
||||||
|
t.addseg(splitPath(pattern), runObject, nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// "/"
|
||||||
|
// "admin" ->
|
||||||
|
func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) {
|
||||||
|
if len(segments) == 0 {
|
||||||
|
if reg != "" {
|
||||||
|
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")})
|
||||||
|
} else {
|
||||||
|
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seg := segments[0]
|
||||||
|
iswild, params, regexpStr := splitSegment(seg)
|
||||||
|
// if it's ? meaning can igone this, so add one more rule for it
|
||||||
|
if len(params) > 0 && params[0] == ":" {
|
||||||
|
t.addseg(segments[1:], route, wildcards, reg)
|
||||||
|
params = params[1:]
|
||||||
|
}
|
||||||
|
//Rule: /login/*/access match /login/2009/11/access
|
||||||
|
//if already has *, and when loop the access, should as a regexpStr
|
||||||
|
if !iswild && utils.InSlice(":splat", wildcards) {
|
||||||
|
iswild = true
|
||||||
|
regexpStr = seg
|
||||||
|
}
|
||||||
|
//Rule: /user/:id/*
|
||||||
|
if seg == "*" && len(wildcards) > 0 && reg == "" {
|
||||||
|
regexpStr = "(.+)"
|
||||||
|
}
|
||||||
|
if iswild {
|
||||||
|
if t.wildcard == nil {
|
||||||
|
t.wildcard = NewTree()
|
||||||
|
}
|
||||||
|
if regexpStr != "" {
|
||||||
|
if reg == "" {
|
||||||
|
rr := ""
|
||||||
|
for _, w := range wildcards {
|
||||||
|
if w == ":splat" {
|
||||||
|
rr = rr + "(.+)/"
|
||||||
|
} else {
|
||||||
|
rr = rr + "([^/]+)/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
regexpStr = rr + regexpStr
|
||||||
|
} else {
|
||||||
|
regexpStr = "/" + regexpStr
|
||||||
|
}
|
||||||
|
} else if reg != "" {
|
||||||
|
if seg == "*.*" {
|
||||||
|
regexpStr = "/([^.]+).(.+)"
|
||||||
|
params = params[1:]
|
||||||
|
} else {
|
||||||
|
for range params {
|
||||||
|
regexpStr = "/([^/]+)" + regexpStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if seg == "*.*" {
|
||||||
|
params = params[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr)
|
||||||
|
} else {
|
||||||
|
var subTree *Tree
|
||||||
|
for _, sub := range t.fixrouters {
|
||||||
|
if sub.prefix == seg {
|
||||||
|
subTree = sub
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if subTree == nil {
|
||||||
|
subTree = NewTree()
|
||||||
|
subTree.prefix = seg
|
||||||
|
t.fixrouters = append(t.fixrouters, subTree)
|
||||||
|
}
|
||||||
|
subTree.addseg(segments[1:], route, wildcards, reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match router to runObject & params
|
||||||
|
func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{}) {
|
||||||
|
if len(pattern) == 0 || pattern[0] != '/' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w := make([]string, 0, 20)
|
||||||
|
return t.match(pattern[1:], pattern, w, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tree) match(treePattern string, pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) {
|
||||||
|
if len(pattern) > 0 {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(pattern) && pattern[i] == '/'; i++ {
|
||||||
|
}
|
||||||
|
pattern = pattern[i:]
|
||||||
|
}
|
||||||
|
// Handle leaf nodes:
|
||||||
|
if len(pattern) == 0 {
|
||||||
|
for _, l := range t.leaves {
|
||||||
|
if ok := l.match(treePattern, wildcardValues, ctx); ok {
|
||||||
|
return l.runObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.wildcard != nil {
|
||||||
|
for _, l := range t.wildcard.leaves {
|
||||||
|
if ok := l.match(treePattern, wildcardValues, ctx); ok {
|
||||||
|
return l.runObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var seg string
|
||||||
|
i, l := 0, len(pattern)
|
||||||
|
for ; i < l && pattern[i] != '/'; i++ {
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
seg = pattern
|
||||||
|
pattern = ""
|
||||||
|
} else {
|
||||||
|
seg = pattern[:i]
|
||||||
|
pattern = pattern[i:]
|
||||||
|
}
|
||||||
|
for _, subTree := range t.fixrouters {
|
||||||
|
if subTree.prefix == seg {
|
||||||
|
if len(pattern) != 0 && pattern[0] == '/' {
|
||||||
|
treePattern = pattern[1:]
|
||||||
|
} else {
|
||||||
|
treePattern = pattern
|
||||||
|
}
|
||||||
|
runObject = subTree.match(treePattern, pattern, wildcardValues, ctx)
|
||||||
|
if runObject != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runObject == nil && len(t.fixrouters) > 0 {
|
||||||
|
// Filter the .json .xml .html extension
|
||||||
|
for _, str := range allowSuffixExt {
|
||||||
|
if strings.HasSuffix(seg, str) {
|
||||||
|
for _, subTree := range t.fixrouters {
|
||||||
|
if subTree.prefix == seg[:len(seg)-len(str)] {
|
||||||
|
runObject = subTree.match(treePattern, pattern, wildcardValues, ctx)
|
||||||
|
if runObject != nil {
|
||||||
|
ctx.Input.SetParam(":ext", str[1:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runObject == nil && t.wildcard != nil {
|
||||||
|
runObject = t.wildcard.match(treePattern, pattern, append(wildcardValues, seg), ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if runObject == nil && len(t.leaves) > 0 {
|
||||||
|
wildcardValues = append(wildcardValues, seg)
|
||||||
|
start, i := 0, 0
|
||||||
|
for ; i < len(pattern); i++ {
|
||||||
|
if pattern[i] == '/' {
|
||||||
|
if i != 0 && start < len(pattern) {
|
||||||
|
wildcardValues = append(wildcardValues, pattern[start:i])
|
||||||
|
}
|
||||||
|
start = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start > 0 {
|
||||||
|
wildcardValues = append(wildcardValues, pattern[start:i])
|
||||||
|
}
|
||||||
|
for _, l := range t.leaves {
|
||||||
|
if ok := l.match(treePattern, wildcardValues, ctx); ok {
|
||||||
|
return l.runObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return runObject
|
||||||
|
}
|
||||||
|
|
||||||
|
type leafInfo struct {
|
||||||
|
// names of wildcards that lead to this leaf. eg, ["id" "name"] for the wildcard ":id" and ":name"
|
||||||
|
wildcards []string
|
||||||
|
|
||||||
|
// if the leaf is regexp
|
||||||
|
regexps *regexp.Regexp
|
||||||
|
|
||||||
|
runObject interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (leaf *leafInfo) match(treePattern string, wildcardValues []string, ctx *context.Context) (ok bool) {
|
||||||
|
//fmt.Println("Leaf:", wildcardValues, leaf.wildcards, leaf.regexps)
|
||||||
|
if leaf.regexps == nil {
|
||||||
|
if len(wildcardValues) == 0 && len(leaf.wildcards) == 0 { // static path
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// match *
|
||||||
|
if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
|
||||||
|
ctx.Input.SetParam(":splat", treePattern)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// match *.* or :id
|
||||||
|
if len(leaf.wildcards) >= 2 && leaf.wildcards[len(leaf.wildcards)-2] == ":path" && leaf.wildcards[len(leaf.wildcards)-1] == ":ext" {
|
||||||
|
if len(leaf.wildcards) == 2 {
|
||||||
|
lastone := wildcardValues[len(wildcardValues)-1]
|
||||||
|
strs := strings.SplitN(lastone, ".", 2)
|
||||||
|
if len(strs) == 2 {
|
||||||
|
ctx.Input.SetParam(":ext", strs[1])
|
||||||
|
}
|
||||||
|
ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[:len(wildcardValues)-1]...), strs[0]))
|
||||||
|
return true
|
||||||
|
} else if len(wildcardValues) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var index int
|
||||||
|
for index = 0; index < len(leaf.wildcards)-2; index++ {
|
||||||
|
ctx.Input.SetParam(leaf.wildcards[index], wildcardValues[index])
|
||||||
|
}
|
||||||
|
lastone := wildcardValues[len(wildcardValues)-1]
|
||||||
|
strs := strings.SplitN(lastone, ".", 2)
|
||||||
|
if len(strs) == 2 {
|
||||||
|
ctx.Input.SetParam(":ext", strs[1])
|
||||||
|
}
|
||||||
|
if index > (len(wildcardValues) - 1) {
|
||||||
|
ctx.Input.SetParam(":path", "")
|
||||||
|
} else {
|
||||||
|
ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0]))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// match :id
|
||||||
|
if len(leaf.wildcards) != len(wildcardValues) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for j, v := range leaf.wildcards {
|
||||||
|
ctx.Input.SetParam(v, wildcardValues[j])
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !leaf.regexps.MatchString(path.Join(wildcardValues...)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...))
|
||||||
|
for i, match := range matches[1:] {
|
||||||
|
if i < len(leaf.wildcards) {
|
||||||
|
ctx.Input.SetParam(leaf.wildcards[i], match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "/" -> []
|
||||||
|
// "/admin" -> ["admin"]
|
||||||
|
// "/admin/" -> ["admin"]
|
||||||
|
// "/admin/users" -> ["admin", "users"]
|
||||||
|
func splitPath(key string) []string {
|
||||||
|
key = strings.Trim(key, "/ ")
|
||||||
|
if key == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
return strings.Split(key, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// "admin" -> false, nil, ""
|
||||||
|
// ":id" -> true, [:id], ""
|
||||||
|
// "?:id" -> true, [: :id], "" : meaning can empty
|
||||||
|
// ":id:int" -> true, [:id], ([0-9]+)
|
||||||
|
// ":name:string" -> true, [:name], ([\w]+)
|
||||||
|
// ":id([0-9]+)" -> true, [:id], ([0-9]+)
|
||||||
|
// ":id([0-9]+)_:name" -> true, [:id :name], ([0-9]+)_(.+)
|
||||||
|
// "cms_:id_:page.html" -> true, [:id_ :page], cms_(.+)(.+).html
|
||||||
|
// "cms_:id(.+)_:page.html" -> true, [:id :page], cms_(.+)_(.+).html
|
||||||
|
// "*" -> true, [:splat], ""
|
||||||
|
// "*.*" -> true,[. :path :ext], "" . meaning separator
|
||||||
|
func splitSegment(key string) (bool, []string, string) {
|
||||||
|
if strings.HasPrefix(key, "*") {
|
||||||
|
if key == "*.*" {
|
||||||
|
return true, []string{".", ":path", ":ext"}, ""
|
||||||
|
}
|
||||||
|
return true, []string{":splat"}, ""
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(key, ":") {
|
||||||
|
var paramsNum int
|
||||||
|
var out []rune
|
||||||
|
var start bool
|
||||||
|
var startexp bool
|
||||||
|
var param []rune
|
||||||
|
var expt []rune
|
||||||
|
var skipnum int
|
||||||
|
params := []string{}
|
||||||
|
reg := regexp.MustCompile(`[a-zA-Z0-9_]+`)
|
||||||
|
for i, v := range key {
|
||||||
|
if skipnum > 0 {
|
||||||
|
skipnum--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if start {
|
||||||
|
//:id:int and :name:string
|
||||||
|
if v == ':' {
|
||||||
|
if len(key) >= i+4 {
|
||||||
|
if key[i+1:i+4] == "int" {
|
||||||
|
out = append(out, []rune("([0-9]+)")...)
|
||||||
|
params = append(params, ":"+string(param))
|
||||||
|
start = false
|
||||||
|
startexp = false
|
||||||
|
skipnum = 3
|
||||||
|
param = make([]rune, 0)
|
||||||
|
paramsNum++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(key) >= i+7 {
|
||||||
|
if key[i+1:i+7] == "string" {
|
||||||
|
out = append(out, []rune(`([\w]+)`)...)
|
||||||
|
params = append(params, ":"+string(param))
|
||||||
|
paramsNum++
|
||||||
|
start = false
|
||||||
|
startexp = false
|
||||||
|
skipnum = 6
|
||||||
|
param = make([]rune, 0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// params only support a-zA-Z0-9
|
||||||
|
if reg.MatchString(string(v)) {
|
||||||
|
param = append(param, v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v != '(' {
|
||||||
|
out = append(out, []rune(`(.+)`)...)
|
||||||
|
params = append(params, ":"+string(param))
|
||||||
|
param = make([]rune, 0)
|
||||||
|
paramsNum++
|
||||||
|
start = false
|
||||||
|
startexp = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if startexp {
|
||||||
|
if v != ')' {
|
||||||
|
expt = append(expt, v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Escape Sequence '\'
|
||||||
|
if i > 0 && key[i-1] == '\\' {
|
||||||
|
out = append(out, v)
|
||||||
|
} else if v == ':' {
|
||||||
|
param = make([]rune, 0)
|
||||||
|
start = true
|
||||||
|
} else if v == '(' {
|
||||||
|
startexp = true
|
||||||
|
start = false
|
||||||
|
if len(param) > 0 {
|
||||||
|
params = append(params, ":"+string(param))
|
||||||
|
param = make([]rune, 0)
|
||||||
|
}
|
||||||
|
paramsNum++
|
||||||
|
expt = make([]rune, 0)
|
||||||
|
expt = append(expt, '(')
|
||||||
|
} else if v == ')' {
|
||||||
|
startexp = false
|
||||||
|
expt = append(expt, ')')
|
||||||
|
out = append(out, expt...)
|
||||||
|
param = make([]rune, 0)
|
||||||
|
} else if v == '?' {
|
||||||
|
params = append(params, ":")
|
||||||
|
} else {
|
||||||
|
out = append(out, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(param) > 0 {
|
||||||
|
if paramsNum > 0 {
|
||||||
|
out = append(out, []rune(`(.+)`)...)
|
||||||
|
}
|
||||||
|
params = append(params, ":"+string(param))
|
||||||
|
}
|
||||||
|
return true, params, string(out)
|
||||||
|
}
|
||||||
|
return false, nil, ""
|
||||||
|
}
|
|
@ -72,7 +72,7 @@ func GrepFile(patten string, filename string) (lines []string, err error) {
|
||||||
lines = make([]string, 0)
|
lines = make([]string, 0)
|
||||||
reader := bufio.NewReader(fd)
|
reader := bufio.NewReader(fd)
|
||||||
prefix := ""
|
prefix := ""
|
||||||
isLongLine := false
|
var isLongLine bool
|
||||||
for {
|
for {
|
||||||
byteLine, isPrefix, er := reader.ReadLine()
|
byteLine, isPrefix, er := reader.ReadLine()
|
||||||
if er != nil && er != io.EOF {
|
if er != nil && er != io.EOF {
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetGOPATHs returns all paths in GOPATH variable.
|
||||||
|
func GetGOPATHs() []string {
|
||||||
|
gopath := os.Getenv("GOPATH")
|
||||||
|
if gopath == "" && strings.Compare(runtime.Version(), "go1.8") >= 0 {
|
||||||
|
gopath = defaultGOPATH()
|
||||||
|
}
|
||||||
|
return filepath.SplitList(gopath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultGOPATH() string {
|
||||||
|
env := "HOME"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
env = "USERPROFILE"
|
||||||
|
} else if runtime.GOOS == "plan9" {
|
||||||
|
env = "home"
|
||||||
|
}
|
||||||
|
if home := os.Getenv(env); home != "" {
|
||||||
|
return filepath.Join(home, "go")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -3,16 +3,22 @@
|
||||||
"ignore": "test",
|
"ignore": "test",
|
||||||
"package": [
|
"package": [
|
||||||
{
|
{
|
||||||
"checksumSHA1": "/ffb74fNK251iTEebGjybfFBAbs=",
|
"checksumSHA1": "2dBBBK43N/+GT+p4BTeNzYcZBJk=",
|
||||||
"path": "github.com/astaxie/beego/swagger",
|
"path": "github.com/astaxie/beego",
|
||||||
"revision": "a09bafbf2ab483742b17bb352f95dbc0a597e720",
|
"revision": "6fec0a7831b4c05c9a583db04cb0ff233f26b15f",
|
||||||
"revisionTime": "2018-07-21T07:55:28Z"
|
"revisionTime": "2018-07-12T02:48:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "epd3Y7nD7QVzTW0ppwK+q4pKo/4=",
|
"checksumSHA1": "/ffb74fNK251iTEebGjybfFBAbs=",
|
||||||
|
"path": "github.com/astaxie/beego/swagger",
|
||||||
|
"revision": "6fec0a7831b4c05c9a583db04cb0ff233f26b15f",
|
||||||
|
"revisionTime": "2018-07-12T02:48:50Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "W7HBzu7HlF18vCfNPgax7xvHXB0=",
|
||||||
"path": "github.com/astaxie/beego/utils",
|
"path": "github.com/astaxie/beego/utils",
|
||||||
"revision": "323a1c4214101331a4b71922c23d19b7409ac71f",
|
"revision": "6fec0a7831b4c05c9a583db04cb0ff233f26b15f",
|
||||||
"revisionTime": "2017-03-06T13:59:04Z"
|
"revisionTime": "2018-07-12T02:48:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "71Ih+BWcUNqZZcKKWcCmDRTZBFw=",
|
"checksumSHA1": "71Ih+BWcUNqZZcKKWcCmDRTZBFw=",
|
||||||
|
@ -233,7 +239,6 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Y7nctMxT58lRM78VtElPerhcnEs=",
|
"checksumSHA1": "Y7nctMxT58lRM78VtElPerhcnEs=",
|
||||||
"origin": "github.com/beego/bee/vendor/golang.org/x/sys/windows",
|
|
||||||
"path": "golang.org/x/sys/windows",
|
"path": "golang.org/x/sys/windows",
|
||||||
"revision": "fa43e7bc11baaae89f3f902b2b4d832b68234844",
|
"revision": "fa43e7bc11baaae89f3f902b2b4d832b68234844",
|
||||||
"revisionTime": "2018-10-11T14:35:51Z"
|
"revisionTime": "2018-10-11T14:35:51Z"
|
||||||
|
|
Loading…
Reference in New Issue