Some minor but helpful additions, like CookieOption. Relative: https://github.com/kataras/iris/issues/1018. Simple cookies example added too. Cookie encoding (side by side with the already session's cookie id encoding) and version upgrade will come tomorrow with a new HISTORY.md entry as well, stay tuned!

Former-commit-id: d14181fac998d32d77690b1b3e42b6c7c72f1ace
This commit is contained in:
Gerasimos Maropoulos 2018-06-02 07:28:40 +03:00
parent f84248cb4e
commit fcff62d5b4
8 changed files with 277 additions and 30 deletions

View File

@ -395,6 +395,10 @@ iris cache library lives on its own [package](https://github.com/kataras/iris/tr
> You're free to use your own favourite caching package if you'd like so. > You're free to use your own favourite caching package if you'd like so.
### Cookies
- [Basic](cookies/basic/main.go)
### Sessions ### Sessions
iris session manager lives on its own [package](https://github.com/kataras/iris/tree/master/sessions). iris session manager lives on its own [package](https://github.com/kataras/iris/tree/master/sessions).

View File

@ -394,6 +394,10 @@ Iris 独立缓存包 [package](https://github.com/kataras/iris/tree/master/cache
> 可以随意使用自定义的缓存包。 > 可以随意使用自定义的缓存包。
### Cookies
- [Basic](cookies/basic/main.go)
### Sessions ### Sessions
Iris session 管理独立包 [package](https://github.com/kataras/iris/tree/master/sessions). Iris session 管理独立包 [package](https://github.com/kataras/iris/tree/master/sessions).

View File

@ -0,0 +1,64 @@
package main
import "github.com/kataras/iris"
func newApp() *iris.Application {
app := iris.New()
// Set A Cookie.
app.Get("/cookies/{name}/{value}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
value := ctx.Params().Get("value")
ctx.SetCookieKV(name, value) // <--
// Alternatively: ctx.SetCookie(&http.Cookie{...})
//
// If you want to set custom the path:
// ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
//
// If you want to be visible only to current request path:
// (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible)
// ctx.SetCookieKV(name, value, iris.CookieCleanPath /* or iris.CookiePath("") */)
// More:
// iris.CookieExpires(time.Duration)
// iris.CookieHTTPOnly(false)
ctx.Writef("cookie added: %s = %s", name, value)
})
// Retrieve A Cookie.
app.Get("/cookies/{name}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
value := ctx.GetCookie(name) // <--
// If you want more than the value then:
// cookie, err := ctx.Request().Cookie(name)
// if err != nil {
// handle error.
// }
ctx.WriteString(value)
})
// Delete A Cookie.
app.Delete("/cookies/{name}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
ctx.RemoveCookie(name) // <--
// If you want to set custom the path:
// ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
ctx.Writef("cookie %s removed", name)
})
return app
}
func main() {
app := newApp()
// GET: http://localhost:8080/cookies/my_name/my_value
// GET: http://localhost:8080/cookies/my_name
// DELETE: http://localhost:8080/cookies/my_name
app.Run(iris.Addr(":8080"))
}

View File

@ -0,0 +1,32 @@
package main
import (
"fmt"
"testing"
"github.com/kataras/iris/httptest"
)
func TestCookiesBasic(t *testing.T) {
app := newApp()
e := httptest.New(t, app, httptest.URL("http://example.com"))
cookieName, cookieValue := "my_cookie_name", "my_cookie_value"
// Test Set A Cookie.
t1 := e.GET(fmt.Sprintf("/cookies/%s/%s", cookieName, cookieValue)).Expect().Status(httptest.StatusOK)
t1.Cookie(cookieName).Value().Equal(cookieValue) // validate cookie's existence, it should be there now.
t1.Body().Contains(cookieValue)
// Test Retrieve A Cookie.
t2 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK)
t2.Body().Equal(cookieValue)
// Test Remove A Cookie.
t3 := e.DELETE(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK)
t3.Body().Contains(cookieName)
t4 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK)
t4.Cookies().Empty()
t4.Body().Empty()
}

View File

@ -854,18 +854,41 @@ type Context interface {
// | Cookies | // | Cookies |
// +------------------------------------------------------------+ // +------------------------------------------------------------+
// SetCookie adds a cookie // SetCookie adds a cookie.
SetCookie(cookie *http.Cookie) // Use of the "options" is not required, they can be used to amend the "cookie".
// SetCookieKV adds a cookie, receives just a name(string) and a value(string)
// //
// If you use this method, it expires at 2 hours // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
// use ctx.SetCookie or http.SetCookie if you want to change more fields. SetCookie(cookie *http.Cookie, options ...CookieOption)
SetCookieKV(name, value string) // SetCookieKV adds a cookie, requires the name(string) and the value(string).
//
// By default it expires at 2 hours and it's added to the root path,
// use the `CookieExpires` and `CookiePath` to modify them.
// Alternatively: ctx.SetCookie(&http.Cookie{...})
//
// If you want to set custom the path:
// ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
//
// If you want to be visible only to current request path:
// ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath(""))
// More:
// iris.CookieExpires(time.Duration)
// iris.CookieHTTPOnly(false)
//
// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
SetCookieKV(name, value string, options ...CookieOption)
// GetCookie returns cookie's value by it's name // GetCookie returns cookie's value by it's name
// returns empty string if nothing was found. // returns empty string if nothing was found.
GetCookie(name string) string //
// RemoveCookie deletes a cookie by it's name. // If you want more than the value then:
RemoveCookie(name string) // cookie, err := ctx.Request().Cookie("name")
//
// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
GetCookie(name string, options ...CookieOption) string
// RemoveCookie deletes a cookie by it's name and path = "/".
// Tip: change the cookie's path to the current one by: RemoveCookie("name", iris.CookieCleanPath)
//
// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
RemoveCookie(name string, options ...CookieOption)
// VisitAllCookies takes a visitor which loops // VisitAllCookies takes a visitor which loops
// on each (request's) cookies' name and value. // on each (request's) cookies' name and value.
VisitAllCookies(visitor func(name string, value string)) VisitAllCookies(visitor func(name string, value string))
@ -1233,7 +1256,7 @@ func (ctx *context) HandlerName() string {
// It can be changed to a customized one if needed (very advanced usage). // It can be changed to a customized one if needed (very advanced usage).
// //
// See `DefaultNext` for more information about this and why it's exported like this. // See `DefaultNext` for more information about this and why it's exported like this.
var Next = DefaultNext ///TODO: add an example for this usecase, i.e describe handlers and skip only file handlers. var Next = DefaultNext
// DefaultNext is the default function that executed on each middleware if `ctx.Next()` // DefaultNext is the default function that executed on each middleware if `ctx.Next()`
// is called. // is called.
@ -2992,57 +3015,144 @@ func (ctx *context) SendFile(filename string, destinationName string) error {
} }
// +------------------------------------------------------------+ // +------------------------------------------------------------+
// | Cookies, Session and Flashes | // | Cookies |
// +------------------------------------------------------------+ // +------------------------------------------------------------+
// SetCookie adds a cookie // CookieOption is the type of function that is accepted on
func (ctx *context) SetCookie(cookie *http.Cookie) { // context's methods like `SetCookieKV`, `RemoveCookie` and `SetCookie`
// as their (last) variadic input argument to amend the end cookie's form.
//
// Any custom or built'n `CookieOption` is valid,
// see `CookiePath`, `CookieCleanPath`, `CookieExpires` and `CookieHTTPOnly` for more.
type CookieOption func(*http.Cookie)
// CookiePath is a `CookieOption`.
// Use it to change the cookie's Path field.
func CookiePath(path string) CookieOption {
return func(c *http.Cookie) {
c.Path = path
}
}
// CookieCleanPath is a `CookieOption`.
// Use it to clear the cookie's Path field, exactly the same as `CookiePath("")`.
func CookieCleanPath(c *http.Cookie) {
c.Path = ""
}
// CookieExpires is a `CookieOption`.
// Use it to change the cookie's Expires and MaxAge fields by passing the lifetime of the cookie.
func CookieExpires(durFromNow time.Duration) CookieOption {
return func(c *http.Cookie) {
c.Expires = time.Now().Add(durFromNow)
c.MaxAge = int(durFromNow.Seconds())
}
}
// CookieHTTPOnly is a `CookieOption`.
// Use it to set the cookie's HttpOnly field to false or true.
// HttpOnly field defaults to true for `RemoveCookie` and `SetCookieKV`.
func CookieHTTPOnly(httpOnly bool) CookieOption {
return func(c *http.Cookie) {
c.HttpOnly = httpOnly
}
}
// SetCookie adds a cookie.
// Use of the "options" is not required, they can be used to amend the "cookie".
//
// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
func (ctx *context) SetCookie(cookie *http.Cookie, options ...CookieOption) {
for _, opt := range options {
opt(cookie)
}
http.SetCookie(ctx.writer, cookie) http.SetCookie(ctx.writer, cookie)
} }
var ( // SetCookieKV adds a cookie, requires the name(string) and the value(string).
// SetCookieKVExpiration is 2 hours by-default
// you can change it or simple, use the SetCookie for more control.
SetCookieKVExpiration = time.Duration(120) * time.Minute
)
// SetCookieKV adds a cookie, receives just a name(string) and a value(string)
// //
// If you use this method, it expires at 2 hours // By default it expires at 2 hours and it's added to the root path,
// use ctx.SetCookie or http.SetCookie if you want to change more fields. // use the `CookieExpires` and `CookiePath` to modify them.
func (ctx *context) SetCookieKV(name, value string) { // Alternatively: ctx.SetCookie(&http.Cookie{...})
//
// If you want to set custom the path:
// ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
//
// If you want to be visible only to current request path:
// (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible)
// ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath(""))
// More:
// iris.CookieExpires(time.Duration)
// iris.CookieHTTPOnly(false)
//
// Examples: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
func (ctx *context) SetCookieKV(name, value string, options ...CookieOption) {
c := &http.Cookie{} c := &http.Cookie{}
c.Path = "/"
c.Name = name c.Name = name
c.Value = url.QueryEscape(value) c.Value = url.QueryEscape(value)
c.HttpOnly = true c.HttpOnly = true
c.Expires = time.Now().Add(SetCookieKVExpiration) c.Expires = time.Now().Add(SetCookieKVExpiration)
c.MaxAge = int(SetCookieKVExpiration.Seconds()) c.MaxAge = int(SetCookieKVExpiration.Seconds())
ctx.SetCookie(c) ctx.SetCookie(c, options...)
} }
// GetCookie returns cookie's value by it's name // GetCookie returns cookie's value by it's name
// returns empty string if nothing was found. // returns empty string if nothing was found.
func (ctx *context) GetCookie(name string) string { //
// If you want more than the value then:
// cookie, err := ctx.Request().Cookie("name")
//
// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
func (ctx *context) GetCookie(name string, options ...CookieOption) string {
cookie, err := ctx.request.Cookie(name) cookie, err := ctx.request.Cookie(name)
if err != nil { if err != nil {
return "" return ""
} }
// TODO:
// Q: Why named as `CookieOption` and not like `CookieInterceptor`?
// A: Because an interceptor would be able to modify the cookie AND stop the 'x' operation, but we don't want to cancel anything.
//
// Q: Why "Cookie Options" here?
// A: Because of the future suport of cookie encoding like I did with sessions.
// Two impl ideas:
// - Do it so each caller of `GetCookie/SetCookieKV/SetCookie` can have each own encoding or share one, no limit.
// - Do it so every of the above three methods will use the same encoding, therefore to the Application's level, limit per Iris app.
// We'll see...
//
// Finally, I should not forget to add links for the new translated READMEs(2) and push a new version with the minor changes so far,
// API is stable, so relax and do it on the next commit tomorrow, need sleep.
for _, opt := range options {
opt(cookie)
}
value, _ := url.QueryUnescape(cookie.Value) value, _ := url.QueryUnescape(cookie.Value)
return value return value
} }
// RemoveCookie deletes a cookie by it's name. // SetCookieKVExpiration is 2 hours by-default
func (ctx *context) RemoveCookie(name string) { // you can change it or simple, use the SetCookie for more control.
//
// See `SetCookieKVExpiration` and `CookieExpires` for more.
var SetCookieKVExpiration = time.Duration(120) * time.Minute
// RemoveCookie deletes a cookie by it's name and path = "/".
// Tip: change the cookie's path to the current one by: RemoveCookie("name", iris.CookieCleanPath)
//
// Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
func (ctx *context) RemoveCookie(name string, options ...CookieOption) {
c := &http.Cookie{} c := &http.Cookie{}
c.Name = name c.Name = name
c.Value = "" c.Value = ""
c.Path = "/" c.Path = "/" // if user wants to change it, use of the CookieOption `CookiePath` is required if not `ctx.SetCookie`.
c.HttpOnly = true c.HttpOnly = true
// RFC says 1 second, but let's do it 1 minute to make sure is working // RFC says 1 second, but let's do it 1 to make sure is working
exp := time.Now().Add(-time.Duration(1) * time.Minute) exp := time.Now().Add(-time.Duration(1) * time.Minute)
c.Expires = exp c.Expires = exp
c.MaxAge = -1 c.MaxAge = -1
ctx.SetCookie(c) ctx.SetCookie(c, options...)
// delete request's cookie also, which is temporary available. // delete request's cookie also, which is temporary available.
ctx.request.Header.Set("Cookie", "") ctx.request.Header.Set("Cookie", "")
} }

10
go19.go
View File

@ -71,4 +71,14 @@ type (
// //
// See `ExecutionRules` and `core/router/Party#SetExecutionRules` for more. // See `ExecutionRules` and `core/router/Party#SetExecutionRules` for more.
ExecutionOptions = router.ExecutionOptions ExecutionOptions = router.ExecutionOptions
// CookieOption is the type of function that is accepted on
// context's methods like `SetCookieKV`, `RemoveCookie` and `SetCookie`
// as their (last) variadic input argument to amend the end cookie's form.
//
// Any custom or built'n `CookieOption` is valid,
// see `CookiePath`, `CookieCleanPath`, `CookieExpires` and `CookieHTTPOnly` for more.
//
// An alias for the `context/Context#CookieOption`.
CookieOption = context.CookieOption
) )

View File

@ -87,6 +87,7 @@ func New(t *testing.T, app *iris.Application, setters ...OptionSetter) *httpexpe
// set the logger or disable it (default) and disable the updater (for any case). // set the logger or disable it (default) and disable the updater (for any case).
app.Configure(iris.WithoutVersionChecker) app.Configure(iris.WithoutVersionChecker)
app.Logger().SetLevel(conf.LogLevel) app.Logger().SetLevel(conf.LogLevel)
if err := app.Build(); err != nil { if err := app.Build(); err != nil {
if conf.Debug && (conf.LogLevel == "disable" || conf.LogLevel == "disabled") { if conf.Debug && (conf.LogLevel == "disable" || conf.LogLevel == "disabled") {
app.Logger().Println(err.Error()) app.Logger().Println(err.Error())

22
iris.go
View File

@ -422,6 +422,28 @@ var (
// //
// A shortcut of the `cache#Cache304`. // A shortcut of the `cache#Cache304`.
Cache304 = cache.Cache304 Cache304 = cache.Cache304
// CookiePath is a `CookieOption`.
// Use it to change the cookie's Path field.
//
// A shortcut for the `context#CookiePath`.
CookiePath = context.CookiePath
// CookieCleanPath is a `CookieOption`.
// Use it to clear the cookie's Path field, exactly the same as `CookiePath("")`.
//
// A shortcut for the `context#CookieCleanPath`.
CookieCleanPath = context.CookieCleanPath
// CookieExpires is a `CookieOption`.
// Use it to change the cookie's Expires and MaxAge fields by passing the lifetime of the cookie.
//
// A shortcut for the `context#CookieExpires`.
CookieExpires = context.CookieExpires
// CookieHTTPOnly is a `CookieOption`.
// Use it to set the cookie's HttpOnly field to false or true.
// HttpOnly field defaults to true for `RemoveCookie` and `SetCookieKV`.
//
// A shortcut for the `context#CookieHTTPOnly`.
CookieHTTPOnly = context.CookieHTTPOnly
) )
// SPA accepts an "assetHandler" which can be the result of an // SPA accepts an "assetHandler" which can be the result of an