mirror of
https://github.com/astaxie/beego.git
synced 2024-11-25 18:00:54 +00:00
Merge pull request #402 from fuxiaohei/master
add api comments in file memzipfile.go,reload.go,router.go,template.go and templatefunc.go
This commit is contained in:
commit
8ba6dbb9a0
22
app.go
22
app.go
@ -61,7 +61,7 @@ func (app *App) Run() {
|
||||
if nil != err {
|
||||
BeeLogger.Critical("ResolveTCPAddr:", err)
|
||||
}
|
||||
l, err = GetInitListner(laddr)
|
||||
l, err = GetInitListener(laddr)
|
||||
theStoppable = newStoppable(l)
|
||||
err = server.Serve(theStoppable)
|
||||
theStoppable.wg.Wait()
|
||||
@ -92,19 +92,19 @@ func (app *App) Run() {
|
||||
// The c argument needs a controller handler implemented beego.ControllerInterface.
|
||||
// The mapping methods argument only need one string to define custom router rules.
|
||||
// usage:
|
||||
// simple router
|
||||
// beego.Router("/admin", &admin.UserController{})
|
||||
// beego.Router("/admin/index", &admin.ArticleController{})
|
||||
// simple router
|
||||
// beego.Router("/admin", &admin.UserController{})
|
||||
// beego.Router("/admin/index", &admin.ArticleController{})
|
||||
//
|
||||
// regex router
|
||||
// regex router
|
||||
//
|
||||
// beego.Router(“/api/:id([0-9]+)“, &controllers.RController{})
|
||||
// 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")
|
||||
// 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 (app *App) Router(path string, c ControllerInterface, mappingMethods ...string) *App {
|
||||
app.Handlers.Add(path, c, mappingMethods...)
|
||||
return app
|
||||
@ -118,7 +118,7 @@ func (app *App) AutoRouter(c ControllerInterface) *App {
|
||||
return app
|
||||
}
|
||||
|
||||
// UrlFor does another controller handler with params in current context.
|
||||
// UrlFor creates a url with another registered controller handler with params.
|
||||
// The endpoint is formed as path.controller.name to defined the controller method which will run.
|
||||
// The values need key-pair data to assign into controller method.
|
||||
func (app *App) UrlFor(endpoint string, values ...string) string {
|
||||
|
@ -16,7 +16,8 @@ import (
|
||||
|
||||
var gmfim map[string]*MemFileInfo = make(map[string]*MemFileInfo)
|
||||
|
||||
//TODO: 加锁保证数据完整性
|
||||
// OpenMemZipFile returns MemFile object with a compressed static file.
|
||||
// it's used for serve static file if gzip enable.
|
||||
func OpenMemZipFile(path string, zip string) (*MemFile, error) {
|
||||
osfile, e := os.Open(path)
|
||||
if e != nil {
|
||||
@ -86,6 +87,8 @@ func OpenMemZipFile(path string, zip string) (*MemFile, error) {
|
||||
return &MemFile{fi: cfi, offset: 0}, nil
|
||||
}
|
||||
|
||||
// MemFileInfo contains a compressed file bytes and file information.
|
||||
// it implements os.FileInfo interface.
|
||||
type MemFileInfo struct {
|
||||
os.FileInfo
|
||||
modTime time.Time
|
||||
@ -94,49 +97,62 @@ type MemFileInfo struct {
|
||||
fileSize int64
|
||||
}
|
||||
|
||||
// Name returns the compressed filename.
|
||||
func (fi *MemFileInfo) Name() string {
|
||||
return fi.Name()
|
||||
}
|
||||
|
||||
// Size returns the raw file content size, not compressed size.
|
||||
func (fi *MemFileInfo) Size() int64 {
|
||||
return fi.contentSize
|
||||
}
|
||||
|
||||
// Mode returns file mode.
|
||||
func (fi *MemFileInfo) Mode() os.FileMode {
|
||||
return fi.Mode()
|
||||
}
|
||||
|
||||
// ModTime returns the last modified time of raw file.
|
||||
func (fi *MemFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
|
||||
// IsDir returns the compressing file is a directory or not.
|
||||
func (fi *MemFileInfo) IsDir() bool {
|
||||
return fi.IsDir()
|
||||
}
|
||||
|
||||
// return nil. implement the os.FileInfo interface method.
|
||||
func (fi *MemFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MemFile contains MemFileInfo and bytes offset when reading.
|
||||
// it implements io.Reader,io.ReadCloser and io.Seeker.
|
||||
type MemFile struct {
|
||||
fi *MemFileInfo
|
||||
offset int64
|
||||
}
|
||||
|
||||
// Close memfile.
|
||||
func (f *MemFile) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get os.FileInfo of memfile.
|
||||
func (f *MemFile) Stat() (os.FileInfo, error) {
|
||||
return f.fi, nil
|
||||
}
|
||||
|
||||
// read os.FileInfo of files in directory of memfile.
|
||||
// it returns empty slice.
|
||||
func (f *MemFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
infos := []os.FileInfo{}
|
||||
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
// Read bytes from the compressed file bytes.
|
||||
func (f *MemFile) Read(p []byte) (n int, err error) {
|
||||
if len(f.fi.content)-int(f.offset) >= len(p) {
|
||||
n = len(p)
|
||||
@ -152,6 +168,7 @@ func (f *MemFile) Read(p []byte) (n int, err error) {
|
||||
var errWhence = errors.New("Seek: invalid whence")
|
||||
var errOffset = errors.New("Seek: invalid offset")
|
||||
|
||||
// Read bytes from the compressed file bytes by seeker.
|
||||
func (f *MemFile) Seek(offset int64, whence int) (ret int64, err error) {
|
||||
switch whence {
|
||||
default:
|
||||
@ -169,8 +186,9 @@ func (f *MemFile) Seek(offset int64, whence int) (ret int64, err error) {
|
||||
return f.offset, nil
|
||||
}
|
||||
|
||||
//返回: gzip, deflate, 优先gzip
|
||||
//返回空, 表示不zip
|
||||
// GetAcceptEncodingZip returns accept encoding format in http header.
|
||||
// zip is first, then deflate if both accepted.
|
||||
// If no accepted, return empty string.
|
||||
func GetAcceptEncodingZip(r *http.Request) string {
|
||||
ss := r.Header.Get("Accept-Encoding")
|
||||
ss = strings.ToLower(ss)
|
||||
@ -181,8 +199,10 @@ func GetAcceptEncodingZip(r *http.Request) string {
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CloseZWriter closes the io.Writer after compressing static file.
|
||||
func CloseZWriter(zwriter io.Writer) {
|
||||
if zwriter == nil {
|
||||
return
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// An environment variable when restarting application http listener.
|
||||
FDKey = "BEEGO_HOT_FD"
|
||||
)
|
||||
|
||||
@ -32,6 +33,7 @@ type conn struct {
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Close current processing connection.
|
||||
func (c conn) Close() error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
@ -67,6 +69,8 @@ func newStoppable(l net.Listener) (sl *stoppableListener) {
|
||||
return
|
||||
}
|
||||
|
||||
// Set stopped Listener to accept requests again.
|
||||
// it returns the accepted and closable connection or error.
|
||||
func (sl *stoppableListener) Accept() (c net.Conn, err error) {
|
||||
c, err = sl.Listener.Accept()
|
||||
if err != nil {
|
||||
@ -80,6 +84,7 @@ func (sl *stoppableListener) Accept() (c net.Conn, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Listener waits signal to kill or interrupt then restart.
|
||||
func WaitSignal(l net.Listener) error {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, os.Interrupt, os.Kill)
|
||||
@ -101,6 +106,7 @@ func WaitSignal(l net.Listener) error {
|
||||
return nil // It'll never get here.
|
||||
}
|
||||
|
||||
// Kill current running os process.
|
||||
func CloseSelf() error {
|
||||
ppid := os.Getpid()
|
||||
if ppid == 1 { // init provided sockets, for example systemd
|
||||
@ -140,7 +146,8 @@ func Restart(l net.Listener) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetInitListner(tcpaddr *net.TCPAddr) (l net.Listener, err error) {
|
||||
// Get current net.Listen in running process.
|
||||
func GetInitListener(tcpaddr *net.TCPAddr) (l net.Listener, err error) {
|
||||
countStr := os.Getenv(FDKey)
|
||||
if countStr == "" {
|
||||
return net.ListenTCP("tcp", tcpaddr)
|
||||
|
53
router.go
53
router.go
@ -19,6 +19,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// default filter execution points
|
||||
BeforeRouter = iota
|
||||
AfterStatic
|
||||
BeforeExec
|
||||
@ -27,6 +28,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// supported http methods.
|
||||
HTTPMETHOD = []string{"get", "post", "put", "delete", "patch", "options", "head"}
|
||||
)
|
||||
|
||||
@ -39,6 +41,7 @@ type controllerInfo struct {
|
||||
hasMethod bool
|
||||
}
|
||||
|
||||
// ControllerRegistor containers registered router rules, controller handlers and filters.
|
||||
type ControllerRegistor struct {
|
||||
routers []*controllerInfo // regexp router storage
|
||||
fixrouters []*controllerInfo // fixed router storage
|
||||
@ -48,6 +51,7 @@ type ControllerRegistor struct {
|
||||
autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type
|
||||
}
|
||||
|
||||
// NewControllerRegistor returns a new ControllerRegistor.
|
||||
func NewControllerRegistor() *ControllerRegistor {
|
||||
return &ControllerRegistor{
|
||||
routers: make([]*controllerInfo, 0),
|
||||
@ -56,15 +60,16 @@ func NewControllerRegistor() *ControllerRegistor {
|
||||
}
|
||||
}
|
||||
|
||||
//methods support like this:
|
||||
//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")
|
||||
// Add controller handler and pattern rules to ControllerRegistor.
|
||||
// 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 *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingMethods ...string) {
|
||||
parts := strings.Split(pattern, "/")
|
||||
|
||||
@ -210,11 +215,11 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
|
||||
}
|
||||
}
|
||||
|
||||
// add auto router to controller
|
||||
// example beego.AddAuto(&MainContorlller{})
|
||||
// MainController has method List and Page
|
||||
// you can visit the url /main/list to exec List function
|
||||
// /main/page to exec Page function
|
||||
// Add auto router to ControllerRegistor.
|
||||
// example beego.AddAuto(&MainContorlller{}),
|
||||
// MainController has method List and Page.
|
||||
// visit the url /main/list to exec List function
|
||||
// /main/page to exec Page function.
|
||||
func (p *ControllerRegistor) AddAuto(c ControllerInterface) {
|
||||
p.enableAuto = true
|
||||
reflectVal := reflect.ValueOf(c)
|
||||
@ -231,6 +236,8 @@ func (p *ControllerRegistor) AddAuto(c ControllerInterface) {
|
||||
}
|
||||
}
|
||||
|
||||
// [Deprecated] use InsertFilter.
|
||||
// Add FilterFunc with pattern for action.
|
||||
func (p *ControllerRegistor) AddFilter(pattern, action string, filter FilterFunc) {
|
||||
mr := buildFilter(pattern, filter)
|
||||
switch action {
|
||||
@ -248,12 +255,15 @@ func (p *ControllerRegistor) AddFilter(pattern, action string, filter FilterFunc
|
||||
p.enableFilter = true
|
||||
}
|
||||
|
||||
// Add a FilterFunc with pattern rule and action constant.
|
||||
func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc) {
|
||||
mr := buildFilter(pattern, filter)
|
||||
p.filters[pos] = append(p.filters[pos], mr)
|
||||
p.enableFilter = true
|
||||
}
|
||||
|
||||
// UrlFor does another controller handler in this request function.
|
||||
// it can access any controller method.
|
||||
func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
|
||||
paths := strings.Split(endpoint, ".")
|
||||
if len(paths) <= 1 {
|
||||
@ -369,7 +379,7 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// main function to serveHTTP
|
||||
// Implement http.Handler interface.
|
||||
func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
@ -753,8 +763,8 @@ Admin:
|
||||
}
|
||||
}
|
||||
|
||||
// there always should be error handler that sets error code accordingly for all unhandled errors
|
||||
// in order to have custom UI for error page it's necessary to override "500" error
|
||||
// there always should be error handler that sets error code accordingly for all unhandled errors.
|
||||
// in order to have custom UI for error page it's necessary to override "500" error.
|
||||
func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) {
|
||||
handler := middleware.SimpleServerError
|
||||
ok := true
|
||||
@ -771,6 +781,9 @@ func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.Resp
|
||||
return handler
|
||||
}
|
||||
|
||||
// returns method name from request header or form field.
|
||||
// sometimes browsers can't create PUT and DELETE request.
|
||||
// set a form field "_method" instead.
|
||||
func (p *ControllerRegistor) getRunMethod(method string, context *beecontext.Context, router *controllerInfo) string {
|
||||
method = strings.ToLower(method)
|
||||
if method == "post" && strings.ToLower(context.Input.Query("_method")) == "put" {
|
||||
@ -806,6 +819,7 @@ func (w *responseWriter) Header() http.Header {
|
||||
return w.writer.Header()
|
||||
}
|
||||
|
||||
// Init content-length header.
|
||||
func (w *responseWriter) InitHeadContent(contentlength int64) {
|
||||
if w.contentEncoding == "gzip" {
|
||||
w.Header().Set("Content-Encoding", "gzip")
|
||||
@ -817,14 +831,15 @@ func (w *responseWriter) InitHeadContent(contentlength int64) {
|
||||
}
|
||||
|
||||
// Write writes the data to the connection as part of an HTTP reply,
|
||||
// and sets `started` to true
|
||||
// and sets `started` to true.
|
||||
// started means the response has sent out.
|
||||
func (w *responseWriter) Write(p []byte) (int, error) {
|
||||
w.started = true
|
||||
return w.writer.Write(p)
|
||||
}
|
||||
|
||||
// WriteHeader sends an HTTP response header with status code,
|
||||
// and sets `started` to true
|
||||
// and sets `started` to true.
|
||||
func (w *responseWriter) WriteHeader(code int) {
|
||||
w.status = code
|
||||
w.started = true
|
||||
|
11
template.go
11
template.go
@ -17,8 +17,9 @@ import (
|
||||
|
||||
var (
|
||||
beegoTplFuncMap template.FuncMap
|
||||
BeeTemplates map[string]*template.Template
|
||||
BeeTemplateExt []string
|
||||
// beego template caching map ans supported template file extensions.
|
||||
BeeTemplates map[string]*template.Template
|
||||
BeeTemplateExt []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -50,7 +51,7 @@ func init() {
|
||||
beegoTplFuncMap["urlfor"] = UrlFor // !=
|
||||
}
|
||||
|
||||
// AddFuncMap let user to register a func in the template
|
||||
// AddFuncMap let user to register a func in the template.
|
||||
func AddFuncMap(key string, funname interface{}) error {
|
||||
beegoTplFuncMap[key] = funname
|
||||
return nil
|
||||
@ -88,6 +89,7 @@ func (self *templatefile) visit(paths string, f os.FileInfo, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// return this path has supported template extension of beego or not.
|
||||
func HasTemplateExt(paths string) bool {
|
||||
for _, v := range BeeTemplateExt {
|
||||
if strings.HasSuffix(paths, "."+v) {
|
||||
@ -97,6 +99,7 @@ func HasTemplateExt(paths string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// add new extension for template.
|
||||
func AddTemplateExt(ext string) {
|
||||
for _, v := range BeeTemplateExt {
|
||||
if v == ext {
|
||||
@ -106,6 +109,8 @@ func AddTemplateExt(ext string) {
|
||||
BeeTemplateExt = append(BeeTemplateExt, ext)
|
||||
}
|
||||
|
||||
// build all template files in a directory.
|
||||
// it makes beego can render any template file in view directory.
|
||||
func BuildTemplate(dir string) error {
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Substr() return the substr from start to length
|
||||
// Substr returns the substr from start to length.
|
||||
func Substr(s string, start, length int) string {
|
||||
bt := []rune(s)
|
||||
if start < 0 {
|
||||
@ -27,7 +27,7 @@ func Substr(s string, start, length int) string {
|
||||
return string(bt[start:end])
|
||||
}
|
||||
|
||||
// Html2str() returns escaping text convert from html
|
||||
// Html2str returns escaping text convert from html.
|
||||
func Html2str(html string) string {
|
||||
src := string(html)
|
||||
|
||||
@ -60,6 +60,7 @@ func DateFormat(t time.Time, layout string) (datestring string) {
|
||||
return
|
||||
}
|
||||
|
||||
// DateFormat pattern rules.
|
||||
var DatePatterns = []string{
|
||||
// year
|
||||
"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
|
||||
@ -100,14 +101,14 @@ var DatePatterns = []string{
|
||||
"r", time.RFC1123Z,
|
||||
}
|
||||
|
||||
// Parse Date use PHP time format
|
||||
// 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
|
||||
// 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)
|
||||
@ -115,7 +116,7 @@ func Date(t time.Time, format string) string {
|
||||
}
|
||||
|
||||
// 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"
|
||||
// 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)) {
|
||||
@ -124,10 +125,12 @@ func Compare(a, b interface{}) (equal bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Convert string to template.HTML type.
|
||||
func Str2html(raw string) template.HTML {
|
||||
return template.HTML(raw)
|
||||
}
|
||||
|
||||
// Htmlquote returns quoted html string.
|
||||
func Htmlquote(src string) string {
|
||||
//HTML编码为实体符号
|
||||
/*
|
||||
@ -150,6 +153,7 @@ func Htmlquote(src string) string {
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
// Htmlunquote returns unquoted html string.
|
||||
func Htmlunquote(src string) string {
|
||||
//实体符号解释为HTML
|
||||
/*
|
||||
@ -174,13 +178,14 @@ func Htmlunquote(src string) string {
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
// This will reference the index function local to the current blueprint:
|
||||
// UrlFor returns url string with another registered controller handler with params.
|
||||
// usage:
|
||||
// UrlFor(".index")
|
||||
// ... print UrlFor("index")
|
||||
// ... print UrlFor("login")
|
||||
// ... print UrlFor("login", "next","/"")
|
||||
// ... print UrlFor("profile", "username","John Doe")
|
||||
// ...
|
||||
// print UrlFor("index")
|
||||
// print UrlFor("login")
|
||||
// print UrlFor("login", "next","/"")
|
||||
// print UrlFor("profile", "username","John Doe")
|
||||
// result:
|
||||
// /
|
||||
// /login
|
||||
// /login?next=/
|
||||
@ -189,7 +194,7 @@ func UrlFor(endpoint string, values ...string) string {
|
||||
return BeeApp.UrlFor(endpoint, values...)
|
||||
}
|
||||
|
||||
//This can be changed to a better name
|
||||
// returns script tag with src string.
|
||||
func AssetsJs(src string) template.HTML {
|
||||
text := string(src)
|
||||
|
||||
@ -198,7 +203,7 @@ func AssetsJs(src string) template.HTML {
|
||||
return template.HTML(text)
|
||||
}
|
||||
|
||||
//This can be changed to a better name
|
||||
// returns stylesheet link tag with str string.
|
||||
func AssetsCss(src string) template.HTML {
|
||||
text := string(src)
|
||||
|
||||
@ -207,7 +212,7 @@ func AssetsCss(src string) template.HTML {
|
||||
return template.HTML(text)
|
||||
}
|
||||
|
||||
// parse form values to struct via tag
|
||||
// parse form values to struct via tag.
|
||||
func ParseForm(form url.Values, obj interface{}) error {
|
||||
objT := reflect.TypeOf(obj)
|
||||
objV := reflect.ValueOf(obj)
|
||||
@ -295,7 +300,8 @@ var unKind = map[reflect.Kind]bool{
|
||||
reflect.UnsafePointer: true,
|
||||
}
|
||||
|
||||
// obj must be a struct pointer
|
||||
// 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)
|
||||
|
Loading…
Reference in New Issue
Block a user