github.com/mayra-cabrera/buffalo@v0.9.4-0.20170814145312-66d2e7772f11/errors.go (about) 1 package buffalo 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 8 "github.com/gobuffalo/plush" 9 "github.com/pkg/errors" 10 ) 11 12 // HTTPError a typed error returned by http Handlers and used for choosing error handlers 13 type HTTPError struct { 14 Status int `json:"status"` 15 Cause error `json:"error"` 16 } 17 18 func (h HTTPError) Error() string { 19 return h.Cause.Error() 20 } 21 22 // ErrorHandler interface for handling an error for a 23 // specific status code. 24 type ErrorHandler func(int, error, Context) error 25 26 // ErrorHandlers is used to hold a list of ErrorHandler 27 // types that can be used to handle specific status codes. 28 /* 29 a.ErrorHandlers[500] = func(status int, err error, c buffalo.Context) error { 30 res := c.Response() 31 res.WriteHeader(status) 32 res.Write([]byte(err.Error())) 33 return nil 34 } 35 */ 36 type ErrorHandlers map[int]ErrorHandler 37 38 // Get a registered ErrorHandler for this status code. If 39 // no ErrorHandler has been registered, a default one will 40 // be returned. 41 func (e ErrorHandlers) Get(status int) ErrorHandler { 42 if eh, ok := e[status]; ok { 43 return eh 44 } 45 return defaultErrorHandler 46 } 47 48 // PanicHandler recovers from panics gracefully and calls 49 // the error handling code for a 500 error. 50 func (a *App) PanicHandler(next Handler) Handler { 51 return func(c Context) error { 52 defer func() { //catch or finally 53 r := recover() 54 var err error 55 if r != nil { //catch 56 switch t := r.(type) { 57 case error: 58 err = errors.WithStack(t) 59 case string: 60 err = errors.WithStack(errors.New(t)) 61 default: 62 err = errors.New(fmt.Sprint(t)) 63 } 64 eh := a.ErrorHandlers.Get(500) 65 eh(500, err, c) 66 } 67 }() 68 return next(c) 69 } 70 } 71 72 func defaultErrorHandler(status int, err error, c Context) error { 73 env := c.Value("env") 74 c.Logger().Error(err) 75 if env != nil && env.(string) == "production" { 76 c.Response().WriteHeader(status) 77 c.Response().Write([]byte(prodErrorTmpl)) 78 return nil 79 } 80 c.Response().WriteHeader(status) 81 82 msg := fmt.Sprintf("%+v", err) 83 ct := c.Request().Header.Get("Content-Type") 84 switch strings.ToLower(ct) { 85 case "application/json", "text/json", "json": 86 err = json.NewEncoder(c.Response()).Encode(map[string]interface{}{ 87 "error": msg, 88 "code": status, 89 }) 90 case "application/xml", "text/xml", "xml": 91 default: 92 err := c.Request().ParseForm() 93 data := map[string]interface{}{ 94 "routes": c.Value("routes"), 95 "error": msg, 96 "status": status, 97 "data": c.Data(), 98 "params": c.Params(), 99 "posted_form": c.Request().Form, 100 "context": c, 101 } 102 ctx := plush.NewContextWith(data) 103 t, err := plush.Render(devErrorTmpl, ctx) 104 if err != nil { 105 return errors.WithStack(err) 106 } 107 res := c.Response() 108 _, err = res.Write([]byte(t)) 109 return err 110 } 111 return err 112 } 113 114 var devErrorTmpl = ` 115 <html> 116 <head> 117 <title><%= status %> - ERROR!</title> 118 <link rel="stylesheet" href="/assets/application.css" type="text/css" media="all"> 119 <style> 120 .container { 121 min-width: 320px; 122 } 123 124 body { 125 font-family: helvetica; 126 } 127 128 table { 129 font-size: 14px; 130 } 131 132 table.table tbody tr td { 133 border-top: 0; 134 padding: 10px; 135 } 136 137 pre { 138 white-space: pre-line; 139 margin-bottom: 10px; 140 max-height: 275px; 141 overflow: scroll; 142 } 143 144 header { 145 background-color: #ed605e; 146 padding: 10px 20px; 147 box-sizing: border-box; 148 } 149 150 .logo img { 151 width: 80px; 152 } 153 154 .titles h1 { 155 font-size: 30px; 156 font-weight: 300; 157 color: white; 158 margin: 24px 0; 159 } 160 161 .content h3 { 162 color: gray; 163 margin: 25px 0; 164 } 165 166 .centered { 167 text-align: center; 168 } 169 170 .foot { 171 padding: 5px 0 20px; 172 text-align: right; 173 text-align: right; 174 color: #c5c5c5; 175 font-weight: 300; 176 } 177 178 .foot a { 179 color: #8b8b8b; 180 text-decoration: underline; 181 } 182 183 .centered { 184 text-align: center; 185 } 186 187 @media all and (max-width: 500px) { 188 .titles h1 { 189 font-size: 25px; 190 margin: 26px 0; 191 } 192 } 193 194 @media all and (max-width: 530px) { 195 .titles h1 { 196 font-size: 20px; 197 margin: 24px 0; 198 } 199 .logo { 200 padding: 0 201 } 202 .logo img { 203 width: 100%; 204 max-width: 80px; 205 } 206 } 207 </style> 208 </head> 209 210 <body> 211 <header> 212 <div class="container"> 213 <div class="row"> 214 <div class="col-md-1 col-sm-2 col-xs-3 logo"> 215 <a href="/"><img src="https://gobuffalo.io/assets/images/logo_med.png" alt=""></a> 216 </div> 217 <div class="col-md-10 col-sm-6 col-xs-7 titles"> 218 <h1> 219 <%= status %> - ERROR! 220 </h1> 221 </div> 222 </div> 223 </div> 224 </header> 225 226 <div class="container content"> 227 <div class="row"> 228 <div class="col-md-12"> 229 <h3>Error Trace</h3> 230 <pre><%= error %></pre> 231 232 <h3>Context</h3> 233 <pre><%= inspect(context) %></pre> 234 235 <h3>Parameters</h3> 236 <pre><%= inspect(params) %></pre> 237 238 <h3>Form</h3> 239 <pre><%= inspect(posted_form) %></pre> 240 241 <h3>Routes</h3> 242 <table class="table table-striped"> 243 <thead> 244 <tr text-align="left"> 245 <th class="centered">METHOD</th> 246 <th>PATH</th> 247 <th>NAME</th> 248 <th>HANDLER</th> 249 </tr> 250 </thead> 251 <tbody> 252 253 <%= for (r) in routes { %> 254 <tr> 255 <td class="centered"> 256 <%= r.Method %> 257 </td> 258 <td> 259 <%= r.Path %> 260 </td> 261 <td> 262 <%= r.PathName %> 263 </td> 264 <td><code><%= r.HandlerName %></code></td> 265 </tr> 266 <% } %> 267 268 </tbody> 269 </table> 270 </div> 271 </div> 272 <div class="foot"> <span> Powered by <a href="http://gobuffalo.io/">gobuffalo.io</a></span></div> 273 </div> 274 </body> 275 </html> 276 ` 277 var prodErrorTmpl = ` 278 <h1>We're Sorry!</h1> 279 <p> 280 It looks like something went wrong! Don't worry, we are aware of the problem and are looking into it. 281 </p> 282 <p> 283 Sorry if this has caused you any problems. Please check back again later. 284 </p> 285 `