mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
Update to 5.0.4 - Read HISTORY.md
This commit is contained in:
parent
424ede7258
commit
309b037e3b
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -1,4 +1,4 @@
|
|||
- Version : **5.0.1**
|
||||
- Version : **5.0.4**
|
||||
|
||||
- Operating System:
|
||||
|
||||
|
|
38
HISTORY.md
38
HISTORY.md
|
@ -2,6 +2,44 @@
|
|||
|
||||
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
|
||||
|
||||
## 5.0.3 -> 5.0.4
|
||||
|
||||
|
||||
The use of `iris.BodyDecoder` as a custom decoder that you can implement to a type in order to be used as the decoder/binder for the request body and override the json.Unmarshal(`context.ReadJSON`) or xml.Unmarshal(`context.ReadXML`) was very useful and gave you some kind of **per-type-binder** extensibility.
|
||||
|
||||
|
||||
|
||||
|
||||
**NEW** `context.UnmarshalBody`: **Per-service-binder**. Side by side with the `iris.BodyDecoder`. We now have a second way to pass a custom `Unmarshaler` to override the `json.Unmarshal` and `xml.Unmarshal`.
|
||||
|
||||
If the object doesn't implements the `iris.BodyDecoder` but you still want to implement your own algorithm to parse []byte as an 'object' instead of the iris' defaults.
|
||||
|
||||
```go
|
||||
type Unmarshaler interface {
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
}
|
||||
|
||||
```
|
||||
`context.ReadJSON & context.ReadXML` have been also refactored to work with this interface and the new `context.DeodeBody` function, look:
|
||||
|
||||
```go
|
||||
// ReadJSON reads JSON from request's body
|
||||
// and binds it to a value of any json-valid type
|
||||
func (ctx *Context) ReadJSON(jsonObject interface{}) error {
|
||||
return ctx.UnmarshalBody(jsonObject, UnmarshalerFunc(json.Unmarshal))
|
||||
}
|
||||
|
||||
// ReadXML reads XML from request's body
|
||||
// and binds it to a value of any xml-valid type
|
||||
func (ctx *Context) ReadXML(xmlObject interface{}) error {
|
||||
return ctx.UnmarshalBody(xmlObject, UnmarshalerFunc(xml.Unmarshal))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Both `encoding/json` and `encoding/xml` standard packages have valid `Unmarshal function` so they can be used as `iris.Unmarshaller` (with the help of `iris.UnmarshallerFunc` which just converts the signature to the `iris.Unmarshaller` interface). You only have to implement one function and it will work with any 'object' passed to the `UnmarshalBody` even if the object doesn't implements the `iris.BodyDecoder`.
|
||||
|
||||
|
||||
## 5.0.2 -> 5.0.3
|
||||
|
||||
- Fix `https relative redirect paths`, a very old issue, which I just saw, peaceful, again :)
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<br/>
|
||||
|
||||
|
||||
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%205.0.3%20-blue.svg?style=flat-square" alt="Releases"></a>
|
||||
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%205.0.4%20-blue.svg?style=flat-square" alt="Releases"></a>
|
||||
|
||||
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
|
||||
|
||||
|
@ -36,7 +36,7 @@ ideally suited for <br/> both experienced and novice developers.<br/><br/>
|
|||
|
||||
Besides the fact that Iris is faster than any alternatives you may met before, <br/> thanks to its fluent API, <b>you don't have to be an expert to work with it.</b><br/> <br/>
|
||||
|
||||
If you're coming from <a href="(https://nodejs.org/en/">Node.js</a> world, this is the <a href="https://github.com/expressjs/express">expressjs</a> alternative for the <a href="https://golang.org">Go Programming Language.</a>
|
||||
If you're coming from <a href="https://nodejs.org/en/">Node.js</a> world, this is the <a href="https://github.com/expressjs/express">expressjs</a> alternative for the <a href="https://golang.org">Go Programming Language.</a>
|
||||
<br/>
|
||||
|
||||
<br/>
|
||||
|
@ -873,7 +873,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
|
|||
Versioning
|
||||
------------
|
||||
|
||||
Current: **v5.0.3**
|
||||
Current: **v5.0.4**
|
||||
|
||||
Stable: **[v4 LTS](https://github.com/kataras/iris/tree/4.0.0#versioning)**
|
||||
|
||||
|
|
|
@ -617,7 +617,6 @@ var (
|
|||
)
|
||||
|
||||
var (
|
||||
universe time.Time // 0001-01-01 00:00:00 +0000 UTC
|
||||
// CookieExpireNever the default cookie's life for sessions, unlimited (23 years)
|
||||
CookieExpireNever = time.Now().AddDate(23, 0, 0)
|
||||
)
|
||||
|
|
191
context.go
191
context.go
|
@ -26,8 +26,6 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// DefaultUserAgent default to 'iris' but it is not used anywhere yet
|
||||
defaultUserAgent = "iris"
|
||||
// ContentType represents the header["Content-Type"]
|
||||
contentType = "Content-Type"
|
||||
// ContentLength represents the header["Content-Length"]
|
||||
|
@ -80,10 +78,7 @@ const (
|
|||
var (
|
||||
errTemplateExecute = errors.New("Unable to execute a template. Trace: %s")
|
||||
errFlashNotFound = errors.New("Unable to get flash message. Trace: Cookie does not exists")
|
||||
errSessionNil = errors.New("Unable to set session, Config().Session.Provider is nil, please refer to the docs!")
|
||||
errNoForm = errors.New("Request has no any valid form")
|
||||
errWriteJSON = errors.New("Before JSON be written to the body, JSON Encoder returned an error. Trace: %s")
|
||||
errRenderMarshalled = errors.New("Before +type Rendering, MarshalIndent returned an error. Trace: %s")
|
||||
errReadBody = errors.New("While trying to read %s from the request body. Trace %s")
|
||||
errServeContent = errors.New("While trying to serve content to the client. Trace %s")
|
||||
)
|
||||
|
@ -282,22 +277,27 @@ func (ctx *Context) FormValues(name string) []string {
|
|||
|
||||
// PostValuesAll returns all post data values with their keys
|
||||
// multipart, form data, get & post query arguments
|
||||
func (ctx *Context) PostValuesAll() (valuesAll map[string][]string) {
|
||||
reqCtx := ctx.RequestCtx
|
||||
valuesAll = make(map[string][]string)
|
||||
//
|
||||
// NOTE: A check for nil is necessary for zero results
|
||||
func (ctx *Context) PostValuesAll() map[string][]string {
|
||||
// first check if we have multipart form
|
||||
multipartForm, err := reqCtx.MultipartForm()
|
||||
multipartForm, err := ctx.MultipartForm()
|
||||
if err == nil {
|
||||
//we have multipart form
|
||||
return multipartForm.Value
|
||||
}
|
||||
// if no multipart and post arguments ( means normal form)
|
||||
|
||||
if reqCtx.PostArgs().Len() == 0 && reqCtx.QueryArgs().Len() == 0 {
|
||||
return // no found
|
||||
postArgs := ctx.PostArgs()
|
||||
queryArgs := ctx.QueryArgs()
|
||||
|
||||
len := postArgs.Len() + queryArgs.Len()
|
||||
if len == 0 {
|
||||
return nil // nothing found
|
||||
}
|
||||
|
||||
reqCtx.PostArgs().VisitAll(func(k []byte, v []byte) {
|
||||
valuesAll := make(map[string][]string, len)
|
||||
|
||||
visitor := func(k []byte, v []byte) {
|
||||
key := string(k)
|
||||
value := string(v)
|
||||
// for slices
|
||||
|
@ -306,21 +306,11 @@ func (ctx *Context) PostValuesAll() (valuesAll map[string][]string) {
|
|||
} else {
|
||||
valuesAll[key] = []string{value}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
reqCtx.QueryArgs().VisitAll(func(k []byte, v []byte) {
|
||||
key := string(k)
|
||||
value := string(v)
|
||||
// for slices
|
||||
if valuesAll[key] != nil {
|
||||
valuesAll[key] = append(valuesAll[key], value)
|
||||
} else {
|
||||
valuesAll[key] = []string{value}
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
postArgs.VisitAll(visitor)
|
||||
queryArgs.VisitAll(visitor)
|
||||
return valuesAll
|
||||
}
|
||||
|
||||
// PostValues returns the post data values as []string of a single key/name
|
||||
|
@ -370,90 +360,61 @@ type BodyDecoder interface {
|
|||
Decode(data []byte) error
|
||||
}
|
||||
|
||||
// ReadJSON reads JSON from request's body
|
||||
// Unmarshaler is the interface implemented by types that can unmarshal any raw data
|
||||
// TIP INFO: Any v object which implements the BodyDecoder can be override the unmarshaler
|
||||
type Unmarshaler interface {
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
}
|
||||
|
||||
// UnmarshalerFunc a shortcut for the Unmarshaler interface
|
||||
//
|
||||
// See 'Unmarshaler' and 'BodyDecoder' for more
|
||||
type UnmarshalerFunc func(data []byte, v interface{}) error
|
||||
|
||||
// Unmarshal parses the X-encoded data and stores the result in the value pointed to by v.
|
||||
// Unmarshal uses the inverse of the encodings that Marshal uses, allocating maps,
|
||||
// slices, and pointers as necessary.
|
||||
func (u UnmarshalerFunc) Unmarshal(data []byte, v interface{}) error {
|
||||
return u(data, v)
|
||||
}
|
||||
|
||||
// UnmarshalBody reads the request's body and binds it to a value or pointer of any type
|
||||
// Examples of usage: context.ReadJSON, context.ReadXML
|
||||
func (ctx *Context) UnmarshalBody(v interface{}, unmarshaler Unmarshaler) error {
|
||||
rawData := ctx.Request.Body()
|
||||
|
||||
// check if the v contains its own decode
|
||||
// in this case the v should be a pointer also,
|
||||
// but this is up to the user's custom Decode implementation*
|
||||
//
|
||||
// See 'BodyDecoder' for more
|
||||
if decoder, isDecoder := v.(BodyDecoder); isDecoder {
|
||||
return decoder.Decode(rawData)
|
||||
}
|
||||
|
||||
// check if v is already a pointer, if yes then pass as it's
|
||||
if reflect.TypeOf(v).Kind() == reflect.Ptr {
|
||||
return unmarshaler.Unmarshal(rawData, v)
|
||||
}
|
||||
// finally, if the v doesn't contains a self-body decoder and it's not a pointer
|
||||
// use the custom unmarshaler to bind the body
|
||||
return unmarshaler.Unmarshal(rawData, &v)
|
||||
}
|
||||
|
||||
// ReadJSON reads JSON from request's body and binds it to a value of any json-valid type
|
||||
func (ctx *Context) ReadJSON(jsonObject interface{}) error {
|
||||
rawData := ctx.Request.Body()
|
||||
|
||||
// check if the jsonObject contains its own decode
|
||||
// in this case the jsonObject should be a pointer also,
|
||||
// but this is up to the user's custom Decode implementation*
|
||||
//
|
||||
// See 'BodyDecoder' for more
|
||||
if decoder, isDecoder := jsonObject.(BodyDecoder); isDecoder {
|
||||
return decoder.Decode(rawData)
|
||||
return ctx.UnmarshalBody(jsonObject, UnmarshalerFunc(json.Unmarshal))
|
||||
}
|
||||
|
||||
// check if jsonObject is already a pointer, if yes then pass as it's
|
||||
if reflect.TypeOf(jsonObject).Kind() == reflect.Ptr {
|
||||
return json.Unmarshal(rawData, jsonObject)
|
||||
}
|
||||
// finally, if the jsonObject doesn't contains a self-body decoder and it's not a pointer
|
||||
return json.Unmarshal(rawData, &jsonObject)
|
||||
}
|
||||
|
||||
// ReadXML reads XML from request's body
|
||||
// ReadXML reads XML from request's body and binds it to a value of any xml-valid type
|
||||
func (ctx *Context) ReadXML(xmlObject interface{}) error {
|
||||
rawData := ctx.Request.Body()
|
||||
|
||||
// check if the xmlObject contains its own decode
|
||||
// in this case the jsonObject should be a pointer also,
|
||||
// but this is up to the user's custom Decode implementation*
|
||||
//
|
||||
// See 'BodyDecoder' for more
|
||||
if decoder, isDecoder := xmlObject.(BodyDecoder); isDecoder {
|
||||
return decoder.Decode(rawData)
|
||||
}
|
||||
|
||||
// check if xmlObject is already a pointer, if yes then pass as it's
|
||||
if reflect.TypeOf(xmlObject).Kind() == reflect.Ptr {
|
||||
return xml.Unmarshal(rawData, xmlObject)
|
||||
}
|
||||
// finally, if the xmlObject doesn't contains a self-body decoder and it's not a pointer
|
||||
return xml.Unmarshal(rawData, &xmlObject)
|
||||
return ctx.UnmarshalBody(xmlObject, UnmarshalerFunc(xml.Unmarshal))
|
||||
}
|
||||
|
||||
// ReadForm binds the formObject with the form data
|
||||
// it supports any kind of struct
|
||||
func (ctx *Context) ReadForm(formObject interface{}) error {
|
||||
reqCtx := ctx.RequestCtx
|
||||
// first check if we have multipart form
|
||||
multipartForm, err := reqCtx.MultipartForm()
|
||||
if err == nil {
|
||||
//we have multipart form
|
||||
return errReadBody.With(formBinder.Decode(multipartForm.Value, formObject))
|
||||
}
|
||||
// if no multipart and post arguments ( means normal form)
|
||||
|
||||
if reqCtx.PostArgs().Len() == 0 && reqCtx.QueryArgs().Len() == 0 {
|
||||
return errReadBody.With(errNoForm)
|
||||
}
|
||||
|
||||
form := make(map[string][]string, reqCtx.PostArgs().Len()+reqCtx.QueryArgs().Len())
|
||||
|
||||
reqCtx.PostArgs().VisitAll(func(k []byte, v []byte) {
|
||||
key := string(k)
|
||||
value := string(v)
|
||||
// for slices
|
||||
if form[key] != nil {
|
||||
form[key] = append(form[key], value)
|
||||
} else {
|
||||
form[key] = []string{value}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
reqCtx.QueryArgs().VisitAll(func(k []byte, v []byte) {
|
||||
key := string(k)
|
||||
value := string(v)
|
||||
// for slices
|
||||
if form[key] != nil {
|
||||
form[key] = append(form[key], value)
|
||||
} else {
|
||||
form[key] = []string{value}
|
||||
}
|
||||
})
|
||||
|
||||
return errReadBody.With(formBinder.Decode(form, formObject))
|
||||
return errReadBody.With(formBinder.Decode(ctx.PostValuesAll(), formObject))
|
||||
}
|
||||
|
||||
/* Response */
|
||||
|
@ -699,6 +660,34 @@ func (ctx *Context) Markdown(status int, markdown string) {
|
|||
ctx.HTML(status, ctx.MarkdownString(markdown))
|
||||
}
|
||||
|
||||
// staticCachePassed checks the IfModifiedSince header and
|
||||
// returns true if (client-side) duration has expired
|
||||
func (ctx *Context) staticCachePassed(modtime time.Time) bool {
|
||||
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(StaticCacheDuration)) {
|
||||
ctx.Response.Header.Del(contentType)
|
||||
ctx.Response.Header.Del(contentLength)
|
||||
ctx.SetStatusCode(StatusNotModified)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetClientCachedBody like SetBody but it sends with an expiration datetime
|
||||
// which is managed by the client-side (all major browsers supports this feature)
|
||||
func (ctx *Context) SetClientCachedBody(status int, bodyContent []byte, cType string, modtime time.Time) {
|
||||
if ctx.staticCachePassed(modtime) {
|
||||
return
|
||||
}
|
||||
|
||||
modtimeFormatted := modtime.UTC().Format(ctx.framework.Config.TimeFormat)
|
||||
|
||||
ctx.Response.Header.Set(contentType, cType)
|
||||
ctx.Response.Header.Set(lastModified, modtimeFormatted)
|
||||
ctx.SetStatusCode(status)
|
||||
|
||||
ctx.Response.SetBody(bodyContent)
|
||||
}
|
||||
|
||||
// ServeContent serves content, headers are autoset
|
||||
// receives three parameters, it's low-level function, instead you can use .ServeFile(string,bool)/SendFile(string,string)
|
||||
//
|
||||
|
|
3
http.go
3
http.go
|
@ -313,8 +313,7 @@ const (
|
|||
// matchEverythingByte is just a byte of '*" rune/char
|
||||
matchEverythingByte = byte('*')
|
||||
|
||||
isStatic entryCase = iota
|
||||
isRoot
|
||||
isRoot entryCase = iota
|
||||
hasParams
|
||||
matchEverything
|
||||
)
|
||||
|
|
146
iris.go
146
iris.go
|
@ -80,7 +80,7 @@ const (
|
|||
// IsLongTermSupport flag is true when the below version number is a long-term-support version
|
||||
IsLongTermSupport = false
|
||||
// Version is the current version number of the Iris web framework
|
||||
Version = "5.0.3"
|
||||
Version = "5.0.4"
|
||||
|
||||
banner = ` _____ _
|
||||
|_ _| (_)
|
||||
|
@ -900,6 +900,28 @@ func Path(routeName string, args ...interface{}) string {
|
|||
return Default.Path(routeName, args...)
|
||||
}
|
||||
|
||||
func joinPathArguments(args ...interface{}) []interface{} {
|
||||
arguments := args[0:]
|
||||
for i, v := range arguments {
|
||||
if arr, ok := v.([]string); ok {
|
||||
if len(arr) > 0 {
|
||||
interfaceArr := make([]interface{}, len(arr))
|
||||
for j, sv := range arr {
|
||||
interfaceArr[j] = sv
|
||||
}
|
||||
// replace the current slice
|
||||
// with the first string element (always as interface{})
|
||||
arguments[i] = interfaceArr[0]
|
||||
// append the rest of them to the slice itself
|
||||
// the range is not affected by these things in go,
|
||||
// so we are safe to do it.
|
||||
arguments = append(args, interfaceArr[1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return arguments
|
||||
}
|
||||
|
||||
// Path used to check arguments with the route's named parameters and return the correct url
|
||||
// if parse failed returns empty string
|
||||
func (s *Framework) Path(routeName string, args ...interface{}) string {
|
||||
|
@ -950,22 +972,7 @@ func (s *Framework) Path(routeName string, args ...interface{}) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
arguments := args[0:]
|
||||
|
||||
// check for arrays
|
||||
for i, v := range arguments {
|
||||
if arr, ok := v.([]string); ok {
|
||||
if len(arr) > 0 {
|
||||
interfaceArr := make([]interface{}, len(arr))
|
||||
for j, sv := range arr {
|
||||
interfaceArr[j] = sv
|
||||
}
|
||||
arguments[i] = interfaceArr[0]
|
||||
arguments = append(arguments, interfaceArr[1:]...)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
arguments := joinPathArguments(args...)
|
||||
|
||||
return fmt.Sprintf(r.formattedPath, arguments...)
|
||||
}
|
||||
|
@ -1019,22 +1026,7 @@ func (s *Framework) URL(routeName string, args ...interface{}) (url string) {
|
|||
|
||||
scheme := s.Config.VScheme // if s.Config.VScheme was setted, that will be used instead of the real, in order to make easy to run behind nginx
|
||||
host := s.Config.VHost // if s.Config.VHost was setted, that will be used instead of the real, in order to make easy to run behind nginx
|
||||
arguments := args[0:]
|
||||
|
||||
// join arrays as arguments
|
||||
for i, v := range arguments {
|
||||
if arr, ok := v.([]string); ok {
|
||||
if len(arr) > 0 {
|
||||
interfaceArr := make([]interface{}, len(arr))
|
||||
for j, sv := range arr {
|
||||
interfaceArr[j] = sv
|
||||
}
|
||||
arguments[i] = interfaceArr[0]
|
||||
arguments = append(arguments, interfaceArr[1:]...)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
arguments := joinPathArguments(args...)
|
||||
|
||||
// if it's dynamic subdomain then the first argument is the subdomain part
|
||||
if r.subdomain == dynamicSubdomainIndicator {
|
||||
|
@ -1741,6 +1733,21 @@ func Static(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
|||
return Default.Static(reqPath, systemPath, stripSlashes)
|
||||
}
|
||||
|
||||
// if / then returns /*wildcard or /something then /something/*wildcard
|
||||
// if empty then returns /*wildcard too
|
||||
func validateWildcard(reqPath string, paramName string) string {
|
||||
if reqPath[len(reqPath)-1] != slashByte {
|
||||
reqPath += slash
|
||||
}
|
||||
reqPath += "*" + paramName
|
||||
return reqPath
|
||||
}
|
||||
|
||||
func (api *muxAPI) registerResourceRoute(reqPath string, h HandlerFunc) RouteNameFunc {
|
||||
api.Head(reqPath, h)
|
||||
return api.Get(reqPath, h)
|
||||
}
|
||||
|
||||
// Static registers a route which serves a system directory
|
||||
// this doesn't generates an index page which list all files
|
||||
// no compression is used also, for these features look at StaticFS func
|
||||
|
@ -1752,14 +1759,9 @@ func Static(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
|||
// * stripSlashes = 1, original path: "/foo/bar", result: "/bar"
|
||||
// * stripSlashes = 2, original path: "/foo/bar", result: ""
|
||||
func (api *muxAPI) Static(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
||||
if reqPath[len(reqPath)-1] != slashByte { // if / then /*filepath, if /something then /something/*filepath
|
||||
reqPath += slash
|
||||
}
|
||||
|
||||
h := api.StaticHandler(systemPath, stripSlashes, false, false, nil)
|
||||
|
||||
api.Head(reqPath+"*filepath", h)
|
||||
return api.Get(reqPath+"*filepath", h)
|
||||
reqPath = validateWildcard(reqPath, "filepath")
|
||||
return api.registerResourceRoute(reqPath, h)
|
||||
}
|
||||
|
||||
// StaticFS registers a route which serves a system directory
|
||||
|
@ -1791,13 +1793,9 @@ func StaticFS(reqPath string, systemPath string, stripSlashes int) RouteNameFunc
|
|||
// * stripSlashes = 1, original path: "/foo/bar", result: "/bar"
|
||||
// * stripSlashes = 2, original path: "/foo/bar", result: ""
|
||||
func (api *muxAPI) StaticFS(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
||||
if reqPath[len(reqPath)-1] != slashByte {
|
||||
reqPath += slash
|
||||
}
|
||||
|
||||
h := api.StaticHandler(systemPath, stripSlashes, true, true, nil)
|
||||
api.Head(reqPath+"*filepath", h)
|
||||
return api.Get(reqPath+"*filepath", h)
|
||||
reqPath = validateWildcard(reqPath, "filepath")
|
||||
return api.registerResourceRoute(reqPath, h)
|
||||
}
|
||||
|
||||
// StaticWeb same as Static but if index.html exists and request uri is '/' then display the index.html's contents
|
||||
|
@ -1824,18 +1822,13 @@ func StaticWeb(reqPath string, systemPath string, stripSlashes int) RouteNameFun
|
|||
// * if you don't know what to put on stripSlashes just 1
|
||||
// example: https://github.com/iris-contrib/examples/tree/master/static_web
|
||||
func (api *muxAPI) StaticWeb(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
||||
if reqPath[len(reqPath)-1] != slashByte { // if / then /*filepath, if /something then /something/*filepath
|
||||
reqPath += slash
|
||||
}
|
||||
//todo: fs.go
|
||||
hasIndex := utils.Exists(systemPath + utils.PathSeparator + "index.html")
|
||||
var indexNames []string
|
||||
if hasIndex {
|
||||
indexNames = []string{"index.html"}
|
||||
}
|
||||
serveHandler := api.StaticHandler(systemPath, stripSlashes, false, !hasIndex, indexNames) // if not index.html exists then generate index.html which shows the list of files
|
||||
api.Head(reqPath+"*filepath", serveHandler)
|
||||
return api.Get(reqPath+"*filepath", serveHandler)
|
||||
return api.registerResourceRoute(reqPath+"*filepath", serveHandler)
|
||||
}
|
||||
|
||||
// StaticServe serves a directory as web resource
|
||||
|
@ -1890,26 +1883,11 @@ func StaticContent(reqPath string, contentType string, content []byte) RouteName
|
|||
// a good example of this is how the websocket server uses that to auto-register the /iris-ws.js
|
||||
func (api *muxAPI) StaticContent(reqPath string, cType string, content []byte) RouteNameFunc { // func(string) because we use that on websockets
|
||||
modtime := time.Now()
|
||||
modtimeStr := ""
|
||||
h := func(ctx *Context) {
|
||||
if modtimeStr == "" {
|
||||
modtimeStr = modtime.UTC().Format(ctx.framework.Config.TimeFormat)
|
||||
ctx.SetClientCachedBody(StatusOK, content, cType, modtime)
|
||||
}
|
||||
|
||||
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(StaticCacheDuration)) {
|
||||
ctx.Response.Header.Del(contentType)
|
||||
ctx.Response.Header.Del(contentLength)
|
||||
ctx.SetStatusCode(StatusNotModified)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set(contentType, cType)
|
||||
ctx.Response.Header.Set(lastModified, modtimeStr)
|
||||
ctx.SetStatusCode(StatusOK)
|
||||
ctx.Response.SetBody(content)
|
||||
}
|
||||
api.Head(reqPath, h)
|
||||
return api.Get(reqPath, h)
|
||||
return api.registerResourceRoute(reqPath, h)
|
||||
}
|
||||
|
||||
// StaticEmbedded used when files are distrubuted inside the app executable, using go-bindata mostly
|
||||
|
@ -1983,7 +1961,6 @@ func (api *muxAPI) StaticEmbedded(requestPath string, vdir string, assetFn func(
|
|||
}
|
||||
|
||||
modtime := time.Now()
|
||||
modtimeStr := ""
|
||||
h := func(ctx *Context) {
|
||||
|
||||
reqPath := ctx.Param(paramName)
|
||||
|
@ -1994,17 +1971,6 @@ func (api *muxAPI) StaticEmbedded(requestPath string, vdir string, assetFn func(
|
|||
continue
|
||||
}
|
||||
|
||||
if modtimeStr == "" {
|
||||
modtimeStr = modtime.UTC().Format(ctx.framework.Config.TimeFormat)
|
||||
}
|
||||
|
||||
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(StaticCacheDuration)) {
|
||||
ctx.Response.Header.Del(contentType)
|
||||
ctx.Response.Header.Del(contentLength)
|
||||
ctx.SetStatusCode(StatusNotModified)
|
||||
return
|
||||
}
|
||||
|
||||
cType := fs.TypeByExtension(path)
|
||||
fullpath := vdir + path
|
||||
|
||||
|
@ -2014,24 +1980,16 @@ func (api *muxAPI) StaticEmbedded(requestPath string, vdir string, assetFn func(
|
|||
continue
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set(contentType, cType)
|
||||
ctx.Response.Header.Set(lastModified, modtimeStr)
|
||||
|
||||
ctx.SetStatusCode(StatusOK)
|
||||
ctx.SetContentType(cType)
|
||||
|
||||
ctx.Response.SetBody(buf)
|
||||
ctx.SetClientCachedBody(StatusOK, buf, cType, modtime)
|
||||
return
|
||||
}
|
||||
|
||||
// not found
|
||||
// not found or error
|
||||
ctx.EmitError(StatusNotFound)
|
||||
|
||||
}
|
||||
|
||||
api.Head(requestPath, h)
|
||||
|
||||
return api.Get(requestPath, h)
|
||||
return api.registerResourceRoute(requestPath, h)
|
||||
}
|
||||
|
||||
// Favicon serves static favicon
|
||||
|
@ -2104,8 +2062,8 @@ func (api *muxAPI) Favicon(favPath string, requestPath ...string) RouteNameFunc
|
|||
if len(requestPath) > 0 {
|
||||
reqPath = requestPath[0]
|
||||
}
|
||||
api.Head(reqPath, h)
|
||||
return api.Get(reqPath, h)
|
||||
|
||||
return api.registerResourceRoute(reqPath, h)
|
||||
}
|
||||
|
||||
// Layout oerrides the parent template layout with a more specific layout for this Party
|
||||
|
|
15
iris/fs.go
15
iris/fs.go
|
@ -7,21 +7,6 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var workingDir string
|
||||
|
||||
func getWorkingDir() string {
|
||||
if workingDir == "" {
|
||||
errUnableToGetWD := errors.New(Name + ": Unable to get working directory, %s")
|
||||
// set the current working dir
|
||||
d, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(errUnableToGetWD.Format(err))
|
||||
}
|
||||
workingDir = d
|
||||
}
|
||||
return workingDir
|
||||
}
|
||||
|
||||
var goPath string
|
||||
|
||||
// returns the (last) gopath+"/src/"
|
||||
|
|
|
@ -9,7 +9,6 @@ var (
|
|||
// Name the name of the cmd tool
|
||||
Name = "Iris Command Line Tool"
|
||||
app *cli.App
|
||||
defaultInstallDir string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -127,27 +127,28 @@ func (t testPluginActivationType) Activate(p iris.PluginContainer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// a plugin may contain children plugins too, but,
|
||||
// if an error happened then all of them are not activated/added to the plugin container
|
||||
func AddPluginTo(t *testing.T, plugins iris.PluginContainer, plugin iris.Plugin, expectingCount int) {
|
||||
plugins.Add(plugin)
|
||||
if plugins.Len() != expectingCount { // 2 because it registeres a second plugin also
|
||||
t.Fatalf("Expected activated plugins to be: %d but we got: %d", expectingCount, plugins.Len())
|
||||
}
|
||||
}
|
||||
|
||||
// if any error returned from the Activate plugin's method,
|
||||
// then this plugin and the plugins it registers should not be registered at all
|
||||
func TestPluginActivate(t *testing.T) {
|
||||
iris.ResetDefault()
|
||||
var plugins = iris.Default.Plugins
|
||||
myplugin := testPluginActivationType{shouldError: false}
|
||||
plugins.Add(myplugin)
|
||||
plugins := iris.Plugins
|
||||
|
||||
if plugins.Len() != 2 { // 2 because it registeres a second plugin also
|
||||
t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, plugins.Len())
|
||||
}
|
||||
}
|
||||
myValidPluginWithChild := testPluginActivationType{shouldError: false}
|
||||
AddPluginTo(t, plugins, myValidPluginWithChild, 2) // 2 because its children registered also (its parent is not throwing an error)
|
||||
|
||||
// if any error returned from the Activate plugin's method, then this plugin and the plugins it registers should not be registered at all
|
||||
func TestPluginActivationError(t *testing.T) {
|
||||
iris.ResetDefault()
|
||||
var plugins = iris.Default.Plugins
|
||||
myplugin := testPluginActivationType{shouldError: true}
|
||||
plugins.Add(myplugin)
|
||||
|
||||
if plugins.Len() > 0 {
|
||||
t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, plugins.Len())
|
||||
}
|
||||
myInvalidPlugin := testPluginActivationType{shouldError: true}
|
||||
// should stay 2, even if we tried to add a new one,
|
||||
// because it cancels the registration of its children too (shouldError = true)
|
||||
AddPluginTo(t, plugins, myInvalidPlugin, 2)
|
||||
}
|
||||
|
||||
func TestPluginEvents(t *testing.T) {
|
||||
|
|
|
@ -6,10 +6,6 @@ import (
|
|||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
builtinFuncs = [...]string{"url", "urlpath"}
|
||||
)
|
||||
|
||||
const (
|
||||
// NoLayout to disable layout for a particular template file
|
||||
NoLayout = template.NoLayout
|
||||
|
|
Loading…
Reference in New Issue
Block a user