mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +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:
|
- 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`.
|
**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
|
## 5.0.2 -> 5.0.3
|
||||||
|
|
||||||
- Fix `https relative redirect paths`, a very old issue, which I just saw, peaceful, again :)
|
- Fix `https relative redirect paths`, a very old issue, which I just saw, peaceful, again :)
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<br/>
|
<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>
|
<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/>
|
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/>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -873,7 +873,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
|
||||||
Versioning
|
Versioning
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Current: **v5.0.3**
|
Current: **v5.0.4**
|
||||||
|
|
||||||
Stable: **[v4 LTS](https://github.com/kataras/iris/tree/4.0.0#versioning)**
|
Stable: **[v4 LTS](https://github.com/kataras/iris/tree/4.0.0#versioning)**
|
||||||
|
|
||||||
|
|
|
@ -617,7 +617,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
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 the default cookie's life for sessions, unlimited (23 years)
|
||||||
CookieExpireNever = time.Now().AddDate(23, 0, 0)
|
CookieExpireNever = time.Now().AddDate(23, 0, 0)
|
||||||
)
|
)
|
||||||
|
|
189
context.go
189
context.go
|
@ -26,8 +26,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultUserAgent default to 'iris' but it is not used anywhere yet
|
|
||||||
defaultUserAgent = "iris"
|
|
||||||
// ContentType represents the header["Content-Type"]
|
// ContentType represents the header["Content-Type"]
|
||||||
contentType = "Content-Type"
|
contentType = "Content-Type"
|
||||||
// ContentLength represents the header["Content-Length"]
|
// ContentLength represents the header["Content-Length"]
|
||||||
|
@ -78,14 +76,11 @@ const (
|
||||||
// errors
|
// errors
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errTemplateExecute = errors.New("Unable to execute a template. Trace: %s")
|
errTemplateExecute = errors.New("Unable to execute a template. Trace: %s")
|
||||||
errFlashNotFound = errors.New("Unable to get flash message. Trace: Cookie does not exists")
|
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")
|
||||||
errNoForm = errors.New("Request has no any valid form")
|
errReadBody = errors.New("While trying to read %s from the request body. Trace %s")
|
||||||
errWriteJSON = errors.New("Before JSON be written to the body, JSON Encoder returned an error. Trace: %s")
|
errServeContent = errors.New("While trying to serve content to the client. 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")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -282,22 +277,27 @@ func (ctx *Context) FormValues(name string) []string {
|
||||||
|
|
||||||
// PostValuesAll returns all post data values with their keys
|
// PostValuesAll returns all post data values with their keys
|
||||||
// multipart, form data, get & post query arguments
|
// multipart, form data, get & post query arguments
|
||||||
func (ctx *Context) PostValuesAll() (valuesAll map[string][]string) {
|
//
|
||||||
reqCtx := ctx.RequestCtx
|
// NOTE: A check for nil is necessary for zero results
|
||||||
valuesAll = make(map[string][]string)
|
func (ctx *Context) PostValuesAll() map[string][]string {
|
||||||
// first check if we have multipart form
|
// first check if we have multipart form
|
||||||
multipartForm, err := reqCtx.MultipartForm()
|
multipartForm, err := ctx.MultipartForm()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//we have multipart form
|
//we have multipart form
|
||||||
return multipartForm.Value
|
return multipartForm.Value
|
||||||
}
|
}
|
||||||
// if no multipart and post arguments ( means normal form)
|
|
||||||
|
|
||||||
if reqCtx.PostArgs().Len() == 0 && reqCtx.QueryArgs().Len() == 0 {
|
postArgs := ctx.PostArgs()
|
||||||
return // no found
|
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)
|
key := string(k)
|
||||||
value := string(v)
|
value := string(v)
|
||||||
// for slices
|
// for slices
|
||||||
|
@ -306,21 +306,11 @@ func (ctx *Context) PostValuesAll() (valuesAll map[string][]string) {
|
||||||
} else {
|
} else {
|
||||||
valuesAll[key] = []string{value}
|
valuesAll[key] = []string{value}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
})
|
postArgs.VisitAll(visitor)
|
||||||
|
queryArgs.VisitAll(visitor)
|
||||||
reqCtx.QueryArgs().VisitAll(func(k []byte, v []byte) {
|
return valuesAll
|
||||||
key := string(k)
|
|
||||||
value := string(v)
|
|
||||||
// for slices
|
|
||||||
if valuesAll[key] != nil {
|
|
||||||
valuesAll[key] = append(valuesAll[key], value)
|
|
||||||
} else {
|
|
||||||
valuesAll[key] = []string{value}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostValues returns the post data values as []string of a single key/name
|
// PostValues returns the post data values as []string of a single key/name
|
||||||
|
@ -370,90 +360,61 @@ type BodyDecoder interface {
|
||||||
Decode(data []byte) error
|
Decode(data []byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadJSON reads JSON from request's body
|
// Unmarshaler is the interface implemented by types that can unmarshal any raw data
|
||||||
func (ctx *Context) ReadJSON(jsonObject interface{}) error {
|
// TIP INFO: Any v object which implements the BodyDecoder can be override the unmarshaler
|
||||||
rawData := ctx.Request.Body()
|
type Unmarshaler interface {
|
||||||
|
Unmarshal(data []byte, v interface{}) error
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
// UnmarshalerFunc a shortcut for the Unmarshaler interface
|
||||||
func (ctx *Context) ReadXML(xmlObject interface{}) error {
|
//
|
||||||
|
// 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()
|
rawData := ctx.Request.Body()
|
||||||
|
|
||||||
// check if the xmlObject contains its own decode
|
// check if the v contains its own decode
|
||||||
// in this case the jsonObject should be a pointer also,
|
// in this case the v should be a pointer also,
|
||||||
// but this is up to the user's custom Decode implementation*
|
// but this is up to the user's custom Decode implementation*
|
||||||
//
|
//
|
||||||
// See 'BodyDecoder' for more
|
// See 'BodyDecoder' for more
|
||||||
if decoder, isDecoder := xmlObject.(BodyDecoder); isDecoder {
|
if decoder, isDecoder := v.(BodyDecoder); isDecoder {
|
||||||
return decoder.Decode(rawData)
|
return decoder.Decode(rawData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if xmlObject is already a pointer, if yes then pass as it's
|
// check if v is already a pointer, if yes then pass as it's
|
||||||
if reflect.TypeOf(xmlObject).Kind() == reflect.Ptr {
|
if reflect.TypeOf(v).Kind() == reflect.Ptr {
|
||||||
return xml.Unmarshal(rawData, xmlObject)
|
return unmarshaler.Unmarshal(rawData, v)
|
||||||
}
|
}
|
||||||
// finally, if the xmlObject doesn't contains a self-body decoder and it's not a pointer
|
// finally, if the v doesn't contains a self-body decoder and it's not a pointer
|
||||||
return xml.Unmarshal(rawData, &xmlObject)
|
// 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 {
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadForm binds the formObject with the form data
|
// ReadForm binds the formObject with the form data
|
||||||
// it supports any kind of struct
|
// it supports any kind of struct
|
||||||
func (ctx *Context) ReadForm(formObject interface{}) error {
|
func (ctx *Context) ReadForm(formObject interface{}) error {
|
||||||
reqCtx := ctx.RequestCtx
|
return errReadBody.With(formBinder.Decode(ctx.PostValuesAll(), formObject))
|
||||||
// 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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Response */
|
/* Response */
|
||||||
|
@ -699,6 +660,34 @@ func (ctx *Context) Markdown(status int, markdown string) {
|
||||||
ctx.HTML(status, ctx.MarkdownString(markdown))
|
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
|
// ServeContent serves content, headers are autoset
|
||||||
// receives three parameters, it's low-level function, instead you can use .ServeFile(string,bool)/SendFile(string,string)
|
// 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 is just a byte of '*" rune/char
|
||||||
matchEverythingByte = byte('*')
|
matchEverythingByte = byte('*')
|
||||||
|
|
||||||
isStatic entryCase = iota
|
isRoot entryCase = iota
|
||||||
isRoot
|
|
||||||
hasParams
|
hasParams
|
||||||
matchEverything
|
matchEverything
|
||||||
)
|
)
|
||||||
|
|
148
iris.go
148
iris.go
|
@ -80,7 +80,7 @@ const (
|
||||||
// IsLongTermSupport flag is true when the below version number is a long-term-support version
|
// IsLongTermSupport flag is true when the below version number is a long-term-support version
|
||||||
IsLongTermSupport = false
|
IsLongTermSupport = false
|
||||||
// Version is the current version number of the Iris web framework
|
// Version is the current version number of the Iris web framework
|
||||||
Version = "5.0.3"
|
Version = "5.0.4"
|
||||||
|
|
||||||
banner = ` _____ _
|
banner = ` _____ _
|
||||||
|_ _| (_)
|
|_ _| (_)
|
||||||
|
@ -900,6 +900,28 @@ func Path(routeName string, args ...interface{}) string {
|
||||||
return Default.Path(routeName, args...)
|
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
|
// Path used to check arguments with the route's named parameters and return the correct url
|
||||||
// if parse failed returns empty string
|
// if parse failed returns empty string
|
||||||
func (s *Framework) Path(routeName string, args ...interface{}) string {
|
func (s *Framework) Path(routeName string, args ...interface{}) string {
|
||||||
|
@ -950,22 +972,7 @@ func (s *Framework) Path(routeName string, args ...interface{}) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
arguments := args[0:]
|
arguments := joinPathArguments(args...)
|
||||||
|
|
||||||
// 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:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(r.formattedPath, arguments...)
|
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
|
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
|
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:]
|
arguments := joinPathArguments(args...)
|
||||||
|
|
||||||
// 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:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if it's dynamic subdomain then the first argument is the subdomain part
|
// if it's dynamic subdomain then the first argument is the subdomain part
|
||||||
if r.subdomain == dynamicSubdomainIndicator {
|
if r.subdomain == dynamicSubdomainIndicator {
|
||||||
|
@ -1741,6 +1733,21 @@ func Static(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
||||||
return Default.Static(reqPath, systemPath, stripSlashes)
|
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
|
// Static registers a route which serves a system directory
|
||||||
// this doesn't generates an index page which list all files
|
// this doesn't generates an index page which list all files
|
||||||
// no compression is used also, for these features look at StaticFS func
|
// 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 = 1, original path: "/foo/bar", result: "/bar"
|
||||||
// * stripSlashes = 2, original path: "/foo/bar", result: ""
|
// * stripSlashes = 2, original path: "/foo/bar", result: ""
|
||||||
func (api *muxAPI) Static(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
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)
|
h := api.StaticHandler(systemPath, stripSlashes, false, false, nil)
|
||||||
|
reqPath = validateWildcard(reqPath, "filepath")
|
||||||
api.Head(reqPath+"*filepath", h)
|
return api.registerResourceRoute(reqPath, h)
|
||||||
return api.Get(reqPath+"*filepath", h)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StaticFS registers a route which serves a system directory
|
// 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 = 1, original path: "/foo/bar", result: "/bar"
|
||||||
// * stripSlashes = 2, original path: "/foo/bar", result: ""
|
// * stripSlashes = 2, original path: "/foo/bar", result: ""
|
||||||
func (api *muxAPI) StaticFS(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
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)
|
h := api.StaticHandler(systemPath, stripSlashes, true, true, nil)
|
||||||
api.Head(reqPath+"*filepath", h)
|
reqPath = validateWildcard(reqPath, "filepath")
|
||||||
return api.Get(reqPath+"*filepath", h)
|
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
|
// 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
|
// * if you don't know what to put on stripSlashes just 1
|
||||||
// example: https://github.com/iris-contrib/examples/tree/master/static_web
|
// example: https://github.com/iris-contrib/examples/tree/master/static_web
|
||||||
func (api *muxAPI) StaticWeb(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
|
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")
|
hasIndex := utils.Exists(systemPath + utils.PathSeparator + "index.html")
|
||||||
var indexNames []string
|
var indexNames []string
|
||||||
if hasIndex {
|
if hasIndex {
|
||||||
indexNames = []string{"index.html"}
|
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
|
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.registerResourceRoute(reqPath+"*filepath", serveHandler)
|
||||||
return api.Get(reqPath+"*filepath", serveHandler)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StaticServe serves a directory as web resource
|
// 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
|
// 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
|
func (api *muxAPI) StaticContent(reqPath string, cType string, content []byte) RouteNameFunc { // func(string) because we use that on websockets
|
||||||
modtime := time.Now()
|
modtime := time.Now()
|
||||||
modtimeStr := ""
|
|
||||||
h := func(ctx *Context) {
|
h := func(ctx *Context) {
|
||||||
if modtimeStr == "" {
|
ctx.SetClientCachedBody(StatusOK, content, cType, modtime)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// 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()
|
modtime := time.Now()
|
||||||
modtimeStr := ""
|
|
||||||
h := func(ctx *Context) {
|
h := func(ctx *Context) {
|
||||||
|
|
||||||
reqPath := ctx.Param(paramName)
|
reqPath := ctx.Param(paramName)
|
||||||
|
@ -1994,17 +1971,6 @@ func (api *muxAPI) StaticEmbedded(requestPath string, vdir string, assetFn func(
|
||||||
continue
|
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)
|
cType := fs.TypeByExtension(path)
|
||||||
fullpath := vdir + path
|
fullpath := vdir + path
|
||||||
|
|
||||||
|
@ -2014,24 +1980,16 @@ func (api *muxAPI) StaticEmbedded(requestPath string, vdir string, assetFn func(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Response.Header.Set(contentType, cType)
|
ctx.SetClientCachedBody(StatusOK, buf, cType, modtime)
|
||||||
ctx.Response.Header.Set(lastModified, modtimeStr)
|
|
||||||
|
|
||||||
ctx.SetStatusCode(StatusOK)
|
|
||||||
ctx.SetContentType(cType)
|
|
||||||
|
|
||||||
ctx.Response.SetBody(buf)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// not found
|
// not found or error
|
||||||
ctx.EmitError(StatusNotFound)
|
ctx.EmitError(StatusNotFound)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
api.Head(requestPath, h)
|
return api.registerResourceRoute(requestPath, h)
|
||||||
|
|
||||||
return api.Get(requestPath, h)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Favicon serves static favicon
|
// Favicon serves static favicon
|
||||||
|
@ -2104,8 +2062,8 @@ func (api *muxAPI) Favicon(favPath string, requestPath ...string) RouteNameFunc
|
||||||
if len(requestPath) > 0 {
|
if len(requestPath) > 0 {
|
||||||
reqPath = 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
|
// 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"
|
"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
|
var goPath string
|
||||||
|
|
||||||
// returns the (last) gopath+"/src/"
|
// returns the (last) gopath+"/src/"
|
||||||
|
|
|
@ -7,9 +7,8 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Name the name of the cmd tool
|
// Name the name of the cmd tool
|
||||||
Name = "Iris Command Line Tool"
|
Name = "Iris Command Line Tool"
|
||||||
app *cli.App
|
app *cli.App
|
||||||
defaultInstallDir string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -127,27 +127,28 @@ func (t testPluginActivationType) Activate(p iris.PluginContainer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginActivate(t *testing.T) {
|
// a plugin may contain children plugins too, but,
|
||||||
iris.ResetDefault()
|
// if an error happened then all of them are not activated/added to the plugin container
|
||||||
var plugins = iris.Default.Plugins
|
func AddPluginTo(t *testing.T, plugins iris.PluginContainer, plugin iris.Plugin, expectingCount int) {
|
||||||
myplugin := testPluginActivationType{shouldError: false}
|
plugins.Add(plugin)
|
||||||
plugins.Add(myplugin)
|
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 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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if any error returned from the Activate plugin's method, then this plugin and the plugins it registers should not be registered at all
|
// if any error returned from the Activate plugin's method,
|
||||||
func TestPluginActivationError(t *testing.T) {
|
// then this plugin and the plugins it registers should not be registered at all
|
||||||
|
func TestPluginActivate(t *testing.T) {
|
||||||
iris.ResetDefault()
|
iris.ResetDefault()
|
||||||
var plugins = iris.Default.Plugins
|
plugins := iris.Plugins
|
||||||
myplugin := testPluginActivationType{shouldError: true}
|
|
||||||
plugins.Add(myplugin)
|
|
||||||
|
|
||||||
if plugins.Len() > 0 {
|
myValidPluginWithChild := testPluginActivationType{shouldError: false}
|
||||||
t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, plugins.Len())
|
AddPluginTo(t, plugins, myValidPluginWithChild, 2) // 2 because its children registered also (its parent is not throwing an error)
|
||||||
}
|
|
||||||
|
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) {
|
func TestPluginEvents(t *testing.T) {
|
||||||
|
|
|
@ -6,10 +6,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
builtinFuncs = [...]string{"url", "urlpath"}
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// NoLayout to disable layout for a particular template file
|
// NoLayout to disable layout for a particular template file
|
||||||
NoLayout = template.NoLayout
|
NoLayout = template.NoLayout
|
||||||
|
|
Loading…
Reference in New Issue
Block a user