From 4228dd8ea4ab307e1380424e6c8633d2dd13824e Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Tue, 18 Aug 2020 23:14:11 +0300 Subject: [PATCH] add some MVC error handle examples --- HISTORY.md | 5 +- README.md | 2 +- _examples/README.md | 4 + .../mvc/error-handler-custom-result/main.go | 75 +++++++++++++ .../views/404.html | 12 +++ .../views/500.html | 12 +++ _examples/mvc/error-handler-hijack/main.go | 60 +++++++++++ .../mvc/error-handler-hijack/views/404.html | 12 +++ .../mvc/error-handler-hijack/views/500.html | 12 +++ _examples/mvc/error-handler-preflight/main.go | 101 ++++++++++++++++++ .../error-handler-preflight/views/404.html | 12 +++ .../error-handler-preflight/views/500.html | 12 +++ hero/func_result.go | 3 + hero/func_result_test.go | 5 +- 14 files changed, 324 insertions(+), 3 deletions(-) create mode 100644 _examples/mvc/error-handler-custom-result/main.go create mode 100644 _examples/mvc/error-handler-custom-result/views/404.html create mode 100644 _examples/mvc/error-handler-custom-result/views/500.html create mode 100644 _examples/mvc/error-handler-hijack/main.go create mode 100644 _examples/mvc/error-handler-hijack/views/404.html create mode 100644 _examples/mvc/error-handler-hijack/views/500.html create mode 100644 _examples/mvc/error-handler-preflight/main.go create mode 100644 _examples/mvc/error-handler-preflight/views/404.html create mode 100644 _examples/mvc/error-handler-preflight/views/500.html diff --git a/HISTORY.md b/HISTORY.md index 27b0b46c..36ee96a5 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -242,7 +242,10 @@ func (r *response) Preflight(ctx iris.Context) error { r.Timestamp = time.Now().Unix() } - ctx.StatusCode(r.Code) + if r.Code > 0 { + ctx.StatusCode(r.Code) + } + return nil } ``` diff --git a/README.md b/README.md index 5a3a2a09..9b40c6e6 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ For a more detailed technical documentation you can head over to our [godocs](ht [![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=iris_framework) -[![follow Iris web framework on facebook](https://img.shields.io/badge/Follow%20%40Iris.framework-445-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework) +[![follow Iris web framework on facebook](https://img.shields.io/badge/Follow%20%40Iris.framework-450-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework) You can [request](https://iris-go.com/#book) a PDF version and online access of the **E-Book** today and be participated in the development of Iris. diff --git a/_examples/README.md b/_examples/README.md index 7754fdbf..ffbd3783 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -214,6 +214,10 @@ * [Login (Repository and Service layers)](mvc/login) * [Login (Single Responsibility)](mvc/login-mvc-single-responsibility) * [Vue.js Todo App](mvc/vuejs-todo-mvc) + * [Error Handler](mvc/error-handler) + * [Handle errors using mvc.Result](mvc/error-handler-custom-result) + * [Handle errors using PreflightResult](mvc/error-handler-preflight) + * [Handle errors by hijacking the result](mvc/error-handler-hijack) * [Bootstrapper](bootstrapper) * Desktop Applications * [The blink package](desktop/blink) diff --git a/_examples/mvc/error-handler-custom-result/main.go b/_examples/mvc/error-handler-custom-result/main.go new file mode 100644 index 00000000..c9805c8e --- /dev/null +++ b/_examples/mvc/error-handler-custom-result/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/mvc" +) + +func main() { + app := iris.New() + app.RegisterView(iris.HTML("./views", ".html")) + + m := mvc.New(app) + m.Handle(new(controller)) + + app.Listen(":8080") +} + +type errorResponse struct { + Code int + Message string +} + +/* +// Note: if a struct implements the standard go error, so it's an error +// and its Error() is not empty, then its text will be rendered instead, +// override any Dispatch method. +func (e errorResponse) Error() string { + return e.Message +} +*/ + +// implements mvc.Result. +func (e errorResponse) Dispatch(ctx iris.Context) { + // If u want to use mvc.Result on any method without an output return value + // go for it: + // + view := mvc.View{Code: e.Code, Data: e} // use Code and Message as the template data. + switch e.Code { + case iris.StatusNotFound: + view.Name = "404" + default: + view.Name = "500" + } + view.Dispatch(ctx) + + // Otherwise use ctx methods: + // + // ctx.StatusCode(e.Code) + // switch e.Code { + // case iris.StatusNotFound: + // // use Code and Message as the template data. + // ctx.View("404.html", e) + // default: + // ctx.View("500.html", e) + // } +} + +type controller struct{} + +type user struct { + ID uint64 `json:"id"` +} + +func (c *controller) GetBy(userid uint64) mvc.Result { + if userid != 1 { + return errorResponse{ + Code: iris.StatusNotFound, + Message: "User Not Found", + } + } + + return mvc.Response{ + Object: user{ID: userid}, + } +} diff --git a/_examples/mvc/error-handler-custom-result/views/404.html b/_examples/mvc/error-handler-custom-result/views/404.html new file mode 100644 index 00000000..39b8326d --- /dev/null +++ b/_examples/mvc/error-handler-custom-result/views/404.html @@ -0,0 +1,12 @@ + + + + + + Client Error Page + + +

{{.Code}}

+

{{.Message}}

+ + \ No newline at end of file diff --git a/_examples/mvc/error-handler-custom-result/views/500.html b/_examples/mvc/error-handler-custom-result/views/500.html new file mode 100644 index 00000000..38b44ab3 --- /dev/null +++ b/_examples/mvc/error-handler-custom-result/views/500.html @@ -0,0 +1,12 @@ + + + + + + Server Error Page + + +

{{.Code}}

+

{{.Message}}

+ + \ No newline at end of file diff --git a/_examples/mvc/error-handler-hijack/main.go b/_examples/mvc/error-handler-hijack/main.go new file mode 100644 index 00000000..f471e371 --- /dev/null +++ b/_examples/mvc/error-handler-hijack/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/mvc" +) + +func main() { + app := iris.New() + app.RegisterView(iris.HTML("./views", ".html")) + + // Hijack each output value of a method (can be used per-party too). + app.ConfigureContainer(). + UseResultHandler(func(next iris.ResultHandler) iris.ResultHandler { + return func(ctx iris.Context, v interface{}) error { + switch val := v.(type) { + case errorResponse: + return next(ctx, errorView(val)) + default: + return next(ctx, v) + } + } + }) + + m := mvc.New(app) + m.Handle(new(controller)) + + app.Listen(":8080") +} + +func errorView(e errorResponse) mvc.Result { + switch e.Code { + case iris.StatusNotFound: + return mvc.View{Code: e.Code, Name: "404.html", Data: e} + default: + return mvc.View{Code: e.Code, Name: "500.html", Data: e} + } +} + +type errorResponse struct { + Code int + Message string +} + +type controller struct{} + +type user struct { + ID uint64 `json:"id"` +} + +func (c *controller) GetBy(userid uint64) interface{} { + if userid != 1 { + return errorResponse{ + Code: iris.StatusNotFound, + Message: "User Not Found", + } + } + + return user{ID: userid} +} diff --git a/_examples/mvc/error-handler-hijack/views/404.html b/_examples/mvc/error-handler-hijack/views/404.html new file mode 100644 index 00000000..39b8326d --- /dev/null +++ b/_examples/mvc/error-handler-hijack/views/404.html @@ -0,0 +1,12 @@ + + + + + + Client Error Page + + +

{{.Code}}

+

{{.Message}}

+ + \ No newline at end of file diff --git a/_examples/mvc/error-handler-hijack/views/500.html b/_examples/mvc/error-handler-hijack/views/500.html new file mode 100644 index 00000000..38b44ab3 --- /dev/null +++ b/_examples/mvc/error-handler-hijack/views/500.html @@ -0,0 +1,12 @@ + + + + + + Server Error Page + + +

{{.Code}}

+

{{.Message}}

+ + \ No newline at end of file diff --git a/_examples/mvc/error-handler-preflight/main.go b/_examples/mvc/error-handler-preflight/main.go new file mode 100644 index 00000000..34289a95 --- /dev/null +++ b/_examples/mvc/error-handler-preflight/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "fmt" + "time" + + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/mvc" +) + +func main() { + app := iris.New() + app.RegisterView(iris.HTML("./views", ".html")) + + m := mvc.New(app) + m.Handle(new(controller)) + + app.Listen(":8080") +} + +type controller struct{} + +// Generic response type for JSON results. +type response struct { + ID uint64 `json:"id,omitempty"` + Data interface{} `json:"data,omitempty"` // {data: result } on fetch actions. + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` +} + +func (r response) Preflight(ctx iris.Context) error { + if r.ID > 0 { + r.Timestamp = time.Now().Unix() + } + + if code := r.Code; code > 0 { + // You can call ctx.View or mvc.Vew{...}.Dispatch + // to render HTML on Code != 200 + // but in order to not proceed with the response resulting + // as JSON you MUST return the iris.ErrStopExecution error. + // Example: + if code != 200 { + mvc.View{ + /* calls the ctx.StatusCode */ + Code: code, + /* use any r.Data as the template data + OR the whole "response" as its data. */ + Data: r, + /* automatically pick the template per error (just for the sake of the example) */ + Name: fmt.Sprintf("%d", code), + }.Dispatch(ctx) + + return iris.ErrStopExecution + } + + ctx.StatusCode(r.Code) + } + + return nil +} + +type user struct { + ID uint64 `json:"id"` +} + +func (c *controller) GetBy(userid uint64) response { + if userid != 1 { + return response{ + Code: iris.StatusNotFound, + Message: "User Not Found", + } + } + + return response{ + ID: userid, + Data: user{ID: userid}, + } +} + +/* + +You can use that `response` structure on non-mvc applications too, using handlers: + +c := app.ConfigureContainer() +c.Get("/{id:uint64}", getUserByID) +func getUserByID(id uint64) response { + if userid != 1 { + return response{ + Code: iris.StatusNotFound, + Message: "User Not Found", + } + } + + return response{ + ID: userid, + Data: user{ID: userid}, + } +} + +*/ diff --git a/_examples/mvc/error-handler-preflight/views/404.html b/_examples/mvc/error-handler-preflight/views/404.html new file mode 100644 index 00000000..39b8326d --- /dev/null +++ b/_examples/mvc/error-handler-preflight/views/404.html @@ -0,0 +1,12 @@ + + + + + + Client Error Page + + +

{{.Code}}

+

{{.Message}}

+ + \ No newline at end of file diff --git a/_examples/mvc/error-handler-preflight/views/500.html b/_examples/mvc/error-handler-preflight/views/500.html new file mode 100644 index 00000000..38b44ab3 --- /dev/null +++ b/_examples/mvc/error-handler-preflight/views/500.html @@ -0,0 +1,12 @@ + + + + + + Server Error Page + + +

{{.Code}}

+

{{.Message}}

+ + \ No newline at end of file diff --git a/hero/func_result.go b/hero/func_result.go index 74bd56a3..964cb6fe 100644 --- a/hero/func_result.go +++ b/hero/func_result.go @@ -72,6 +72,9 @@ type Result interface { // // The caller can manage it at the handler itself. However, // to reduce thoese type of duplications it's preferable to use such a standard interface instead. +// +// The Preflight method can return `iris.ErrStopExecution` to render +// and override any interface that the structure value may implement, e.g. mvc.Result. type PreflightResult interface { Preflight(*context.Context) error } diff --git a/hero/func_result_test.go b/hero/func_result_test.go index e89b80d8..dcb76dc9 100644 --- a/hero/func_result_test.go +++ b/hero/func_result_test.go @@ -223,7 +223,10 @@ func (r testPreflightResponse) Preflight(ctx iris.Context) error { return fmt.Errorf("custom error") } - ctx.StatusCode(r.Code) + if r.Code > 0 { + ctx.StatusCode(r.Code) + } + return nil }