mirror of
https://github.com/kataras/iris.git
synced 2025-03-15 17:36:29 +01:00
More on Transactions: Fallback on, unexpected, panics and able to send 'silent' error which stills reverts the changes but no output
This commit is contained in:
parent
1da8231abd
commit
88c98bb1e1
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
**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.1.1 -> 5.1.3
|
||||||
|
- **More on Transactions vol 3**: Recovery from any (unexpected error) panics inside `context.BeginTransaction` without loud, continue the execution as expected. Next version will have a little cleanup if I see that the transactions code is going very large or hard to understand the flow*
|
||||||
|
|
||||||
## 5.1.1 -> 5.1.2
|
## 5.1.1 -> 5.1.2
|
||||||
|
|
||||||
- **More on Transactions vol 2**: Added **iris.UseTransaction** and **iris.DoneTransaction** to register transactions as you register middleware(handlers). new named type **iris.TransactionFunc**, shortcut of `func(scope *iris.TransactionScope)`, that gives you a function which you can convert a transaction to a normal handler/middleware using its `.ToMiddleware()`, for more see the `test code inside context_test.go:TestTransactionsMiddleware`.
|
- **More on Transactions vol 2**: Added **iris.UseTransaction** and **iris.DoneTransaction** to register transactions as you register middleware(handlers). new named type **iris.TransactionFunc**, shortcut of `func(scope *iris.TransactionScope)`, that gives you a function which you can convert a transaction to a normal handler/middleware using its `.ToMiddleware()`, for more see the `test code inside context_test.go:TestTransactionsMiddleware`.
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%205.1.2%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.1.3%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>
|
||||||
|
|
||||||
|
@ -920,7 +920,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
|
||||||
Versioning
|
Versioning
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Current: **v5.1.2**
|
Current: **v5.1.3**
|
||||||
|
|
||||||
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)**
|
||||||
|
|
||||||
|
|
53
context.go
53
context.go
|
@ -1122,6 +1122,23 @@ func (ctx *Context) MaxAge() int64 {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrFallback is just an empty error but it is recognised from the TransactionScope.Complete,
|
||||||
|
// it reverts its changes and continue as normal, no error will be shown to the user.
|
||||||
|
//
|
||||||
|
// Usually it is used on recovery from panics (inside .BeginTransaction)
|
||||||
|
// but users can use that also to by-pass the error's response of your custom transaction pipe.
|
||||||
|
type ErrFallback struct{}
|
||||||
|
|
||||||
|
func (ne *ErrFallback) Error() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrFallback returns a new error wihch contains an empty error,
|
||||||
|
// look .BeginTransaction and context_test.go:TestTransactionRecoveryFromPanic
|
||||||
|
func NewErrFallback() *ErrFallback {
|
||||||
|
return &ErrFallback{}
|
||||||
|
}
|
||||||
|
|
||||||
// ErrWithStatus custom error type which is useful
|
// ErrWithStatus custom error type which is useful
|
||||||
// to send an error containing the http status code and a reason
|
// to send an error containing the http status code and a reason
|
||||||
type ErrWithStatus struct {
|
type ErrWithStatus struct {
|
||||||
|
@ -1131,6 +1148,11 @@ type ErrWithStatus struct {
|
||||||
message string // if it's empty then the already registered custom(or default) http error will be fired.
|
message string // if it's empty then the already registered custom(or default) http error will be fired.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Silent in case the user changed his/her mind and wants to silence this error
|
||||||
|
func (err *ErrWithStatus) Silent() error {
|
||||||
|
return NewErrFallback()
|
||||||
|
}
|
||||||
|
|
||||||
// Status sets the http status code of this error
|
// Status sets the http status code of this error
|
||||||
// if only status exists but no reason then
|
// if only status exists but no reason then
|
||||||
// custom http error of this staus (if any) will be fired (context.EmitError)
|
// custom http error of this staus (if any) will be fired (context.EmitError)
|
||||||
|
@ -1215,7 +1237,12 @@ func (r *TransactionScope) Complete(err error) {
|
||||||
ctx := r.Context
|
ctx := r.Context
|
||||||
statusCode := StatusInternalServerError // default http status code if not provided
|
statusCode := StatusInternalServerError // default http status code if not provided
|
||||||
reason := err.Error()
|
reason := err.Error()
|
||||||
|
if _, ok := err.(*ErrFallback); ok {
|
||||||
|
// revert without any log or response.
|
||||||
|
r.isFailure = true
|
||||||
|
ctx.Response.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
if errWstatus, ok := err.(*ErrWithStatus); ok {
|
if errWstatus, ok := err.(*ErrWithStatus); ok {
|
||||||
if errWstatus.statusCode > 0 {
|
if errWstatus.statusCode > 0 {
|
||||||
// get the status code from the custom error type
|
// get the status code from the custom error type
|
||||||
|
@ -1303,11 +1330,17 @@ func (pipe TransactionFunc) ToMiddleware() HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// non-detailed error log for transacton unexpected panic
|
||||||
|
var errTransactionInterrupted = errors.New("Transaction Interrupted, recovery from panic:\n%s")
|
||||||
|
|
||||||
// BeginTransaction starts a request scoped transaction.
|
// BeginTransaction starts a request scoped transaction.
|
||||||
// Transactions have their own middleware ecosystem also, look iris.go:UseTransaction.
|
// Transactions have their own middleware ecosystem also, look iris.go:UseTransaction.
|
||||||
//
|
//
|
||||||
// See https://github.com/iris-contrib/examples/tree/master/transactions for more
|
// See https://github.com/iris-contrib/examples/tree/master/transactions for more
|
||||||
func (ctx *Context) BeginTransaction(pipe TransactionFunc) {
|
func (ctx *Context) BeginTransaction(pipe func(scope *TransactionScope)) {
|
||||||
|
// SILLY NOTE: use of manual pipe type in order of TransactionFunc
|
||||||
|
// in order to help editors complete the sentence here...
|
||||||
|
|
||||||
// do NOT begin a transaction when the previous transaction has been failed
|
// do NOT begin a transaction when the previous transaction has been failed
|
||||||
// and it was requested scoped or SkipTransactions called manually.
|
// and it was requested scoped or SkipTransactions called manually.
|
||||||
if ctx.TransactionsSkipped() {
|
if ctx.TransactionsSkipped() {
|
||||||
|
@ -1318,9 +1351,15 @@ func (ctx *Context) BeginTransaction(pipe TransactionFunc) {
|
||||||
tempCtx := *ctx
|
tempCtx := *ctx
|
||||||
// get a transaction scope from the pool by passing the temp context/
|
// get a transaction scope from the pool by passing the temp context/
|
||||||
scope := acquireTransactionScope(&tempCtx)
|
scope := acquireTransactionScope(&tempCtx)
|
||||||
|
defer func() {
|
||||||
// run the worker with its context inside this scope.
|
if err := recover(); err != nil {
|
||||||
pipe(scope)
|
if ctx.framework.Config.IsDevelopment {
|
||||||
|
ctx.Log(errTransactionInterrupted.Format(err).Error())
|
||||||
|
}
|
||||||
|
// complete (again or not , doesn't matters) the scope without loud
|
||||||
|
scope.Complete(NewErrFallback())
|
||||||
|
// we continue as normal, no need to return here*
|
||||||
|
}
|
||||||
|
|
||||||
// if the transaction completed with an error then the transaction itself reverts the changes
|
// if the transaction completed with an error then the transaction itself reverts the changes
|
||||||
// and replaces the context's response with an error.
|
// and replaces the context's response with an error.
|
||||||
|
@ -1336,6 +1375,10 @@ func (ctx *Context) BeginTransaction(pipe TransactionFunc) {
|
||||||
|
|
||||||
// finally, release and put the transaction scope back to the pool.
|
// finally, release and put the transaction scope back to the pool.
|
||||||
releaseTransactionScope(scope)
|
releaseTransactionScope(scope)
|
||||||
|
}()
|
||||||
|
// run the worker with its context inside this scope.
|
||||||
|
pipe(scope)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log logs to the iris defined logger
|
// Log logs to the iris defined logger
|
||||||
|
|
|
@ -972,5 +972,69 @@ func TestTransactionsMiddleware(t *testing.T) {
|
||||||
ContentType("text/html", api.Config.Charset).
|
ContentType("text/html", api.Config.Charset).
|
||||||
Body().
|
Body().
|
||||||
Equal(expectedResponse)
|
Equal(expectedResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTransactionFailureCompletionButSilently(t *testing.T) {
|
||||||
|
iris.ResetDefault()
|
||||||
|
expectedBody := "I don't care for any unexpected panics, this response should be sent."
|
||||||
|
|
||||||
|
iris.Get("/panic_silent", func(ctx *iris.Context) {
|
||||||
|
ctx.BeginTransaction(func(scope *iris.TransactionScope) {
|
||||||
|
scope.Context.Write("blablabla this should not be shown because of 'unexpected' panic.")
|
||||||
|
panic("OMG, UNEXPECTED ERROR BECAUSE YOU ARE NOT A DISCIPLINED PROGRAMMER, BUT IRIS HAS YOU COVERED!")
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.WriteString(expectedBody)
|
||||||
|
})
|
||||||
|
|
||||||
|
iris.Get("/expected_error_but_silent_instead_of_send_the_reason", func(ctx *iris.Context) {
|
||||||
|
ctx.BeginTransaction(func(scope *iris.TransactionScope) {
|
||||||
|
scope.Context.Write("this will not be sent.")
|
||||||
|
// complete with a failure ( so revert the changes) but do it silently.
|
||||||
|
scope.Complete(iris.NewErrFallback())
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.WriteString(expectedBody)
|
||||||
|
})
|
||||||
|
|
||||||
|
iris.Get("/silly_way_expected_error_but_silent_instead_of_send_the_reason", func(ctx *iris.Context) {
|
||||||
|
ctx.BeginTransaction(func(scope *iris.TransactionScope) {
|
||||||
|
scope.Context.Write("this will not be sent.")
|
||||||
|
|
||||||
|
// or if you know the error will be silent from the beggining: err := &iris.ErrFallback{}
|
||||||
|
err := iris.NewErrWithStatus()
|
||||||
|
|
||||||
|
fail := true
|
||||||
|
|
||||||
|
if fail {
|
||||||
|
err.Status(iris.StatusBadRequest).Reason("we dont know but it was expected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// we change our mind we don't want to send the error to the user, so err.Silent to the .Complete
|
||||||
|
// complete with a failure ( so revert the changes) but do it silently.
|
||||||
|
scope.Complete(err.Silent())
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.WriteString(expectedBody)
|
||||||
|
})
|
||||||
|
|
||||||
|
e := httptest.New(iris.Default, t)
|
||||||
|
|
||||||
|
e.GET("/panic_silent").Expect().
|
||||||
|
Status(iris.StatusOK).
|
||||||
|
Body().
|
||||||
|
Equal(expectedBody)
|
||||||
|
|
||||||
|
e.GET("/expected_error_but_silent_instead_of_send_the_reason").
|
||||||
|
Expect().
|
||||||
|
Status(iris.StatusOK).
|
||||||
|
Body().
|
||||||
|
Equal(expectedBody)
|
||||||
|
|
||||||
|
e.GET("/silly_way_expected_error_but_silent_instead_of_send_the_reason").
|
||||||
|
Expect().
|
||||||
|
Status(iris.StatusOK).
|
||||||
|
Body().
|
||||||
|
Equal(expectedBody)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
2
iris.go
2
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.1.2"
|
Version = "5.1.3"
|
||||||
|
|
||||||
banner = ` _____ _
|
banner = ` _____ _
|
||||||
|_ _| (_)
|
|_ _| (_)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user