github.com/sotirispl/buffalo@v0.11.1/errors.go (about) 1 package buffalo 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "sort" 8 "strings" 9 10 "github.com/gobuffalo/plush" 11 "github.com/gobuffalo/x/httpx" 12 "github.com/pkg/errors" 13 ) 14 15 // HTTPError a typed error returned by http Handlers and used for choosing error handlers 16 type HTTPError struct { 17 Status int `json:"status"` 18 Cause error `json:"error"` 19 } 20 21 func (h HTTPError) Error() string { 22 return h.Cause.Error() 23 } 24 25 // ErrorHandler interface for handling an error for a 26 // specific status code. 27 type ErrorHandler func(int, error, Context) error 28 29 // ErrorHandlers is used to hold a list of ErrorHandler 30 // types that can be used to handle specific status codes. 31 /* 32 a.ErrorHandlers[500] = func(status int, err error, c buffalo.Context) error { 33 res := c.Response() 34 res.WriteHeader(status) 35 res.Write([]byte(err.Error())) 36 return nil 37 } 38 */ 39 type ErrorHandlers map[int]ErrorHandler 40 41 // Get a registered ErrorHandler for this status code. If 42 // no ErrorHandler has been registered, a default one will 43 // be returned. 44 func (e ErrorHandlers) Get(status int) ErrorHandler { 45 if eh, ok := e[status]; ok { 46 return eh 47 } 48 return defaultErrorHandler 49 } 50 51 // PanicHandler recovers from panics gracefully and calls 52 // the error handling code for a 500 error. 53 func (a *App) PanicHandler(next Handler) Handler { 54 return func(c Context) error { 55 defer func() { //catch or finally 56 r := recover() 57 var err error 58 if r != nil { //catch 59 switch t := r.(type) { 60 case error: 61 err = errors.WithStack(t) 62 case string: 63 err = errors.WithStack(errors.New(t)) 64 default: 65 err = errors.New(fmt.Sprint(t)) 66 } 67 eh := a.ErrorHandlers.Get(500) 68 eh(500, err, c) 69 } 70 }() 71 return next(c) 72 } 73 } 74 75 func defaultErrorHandler(status int, err error, c Context) error { 76 env := c.Value("env") 77 c.Logger().Error(err) 78 if env != nil && env.(string) == "production" { 79 c.Response().WriteHeader(status) 80 c.Response().Write([]byte(prodErrorTmpl)) 81 return nil 82 } 83 c.Response().WriteHeader(status) 84 85 msg := fmt.Sprintf("%+v", err) 86 ct := httpx.ContentType(c.Request()) 87 switch strings.ToLower(ct) { 88 case "application/json", "text/json", "json": 89 err = json.NewEncoder(c.Response()).Encode(map[string]interface{}{ 90 "error": msg, 91 "code": status, 92 }) 93 case "application/xml", "text/xml", "xml": 94 default: 95 err := c.Request().ParseForm() 96 routes := c.Value("routes") 97 if cd, ok := c.(*DefaultContext); ok { 98 delete(cd.data, "app") 99 delete(cd.data, "routes") 100 } 101 data := map[string]interface{}{ 102 "routes": routes, 103 "error": msg, 104 "status": status, 105 "data": c.Data(), 106 "params": c.Params(), 107 "posted_form": c.Request().Form, 108 "context": c, 109 "headers": inspectHeaders(c.Request().Header), 110 "inspect": func(v interface{}) string { 111 return fmt.Sprintf("%+v", v) 112 }, 113 } 114 ctx := plush.NewContextWith(data) 115 t, err := plush.Render(devErrorTmpl, ctx) 116 if err != nil { 117 return errors.WithStack(err) 118 } 119 res := c.Response() 120 _, err = res.Write([]byte(t)) 121 return err 122 } 123 return err 124 } 125 126 type inspectHeaders http.Header 127 128 func (i inspectHeaders) String() string { 129 130 bb := make([]string, 0, len(i)) 131 132 for k, v := range i { 133 bb = append(bb, fmt.Sprintf("%s: %s", k, v)) 134 } 135 sort.Strings(bb) 136 return strings.Join(bb, "\n\n") 137 } 138 139 var devErrorTmpl = ` 140 <html> 141 <head> 142 <title><%= status %> - ERROR!</title> 143 <style>html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}header{display:block}a{background-color:transparent}a:active,a:hover{outline:0}h1{margin:.67em 0;font-size:2em}img{border:0}pre{overflow:auto}code,pre{font-family:monospace,monospace;font-size:1em}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}@media print{*{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h3{orphans:3;widows:3}h3{page-break-after:avoid}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}img{vertical-align:middle}h1,h3{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1,h3{margin-top:20px;margin-bottom:10px}h1{font-size:36px}h3{font-size:24px}code,pre{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.row{margin-right:-15px;margin-left:-15px}.col-md-1,.col-md-10,.col-md-12,.col-sm-2,.col-sm-6,.col-xs-3,.col-xs-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-3,.col-xs-7{float:left}.col-xs-7{width:58.33333333%}.col-xs-3{width:25%}@media (min-width:768px){.col-sm-2,.col-sm-6{float:left}.col-sm-6{width:50%}.col-sm-2{width:16.66666667%}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-12{float:left}.col-md-12{width:100%}.col-md-10{width:83.33333333%}.col-md-1{width:8.33333333%}}table{background-color:transparent}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>thead:first-child>tr:first-child>th{border-top:0}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.container:after,.container:before,.row:after,.row:before{display:table;content:" "}.container:after,.row:after{clear:both}@-ms-viewport{width:device-width} 144 h1{margin-top:20px}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff;margin:0}h1{margin-bottom:10px;font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.table{margin-bottom:20px}h1{font-size:36px}a{color:#337ab7;text-decoration:none}a:hover{color:#23527c}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.table{width:100%;max-width:100%;background-color:transparent;border-spacing:0;border-collapse:collapse}.table-striped>tbody{background-color:#f9f9f9}.table>tbody>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{border-top:0;vertical-align:bottom;border-bottom:2px solid #ddd;text-align:left}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace}.row{margin-right:-15px;margin-left:-15px}.col-md-10{float:left;position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-md-10{width:83.33333333%}img{vertical-align:middle;border:0}.container{min-width:320px}body{font-family:helvetica}table{font-size:14px}table.table tbody tr td{border-top:0;padding:10px}pre{white-space:pre-line;margin-bottom:10px;max-height:275px;overflow:scroll}header{background-color:#ed605e;padding:10px 20px;box-sizing:border-box}.logo img{width:80px}.titles h1{font-size:30px;font-weight:300;color:#fff;margin:24px 0}.content h3{color:gray;margin:25px 0}.foot{padding:5px 0 20px;text-align:right;color:#c5c5c5;font-weight:300}.foot a{color:#8b8b8b;text-decoration:underline}.centered{text-align:center}@media all and (max-width:500px){.titles h1{font-size:25px;margin:26px 0}}@media all and (max-width:530px){.titles h1{font-size:20px;margin:24px 0}.logo{padding:0}.logo img{width:100%;max-width:80px}} 145 </style> 146 </head> 147 148 <body> 149 <header> 150 <div class="container"> 151 <div class="row"> 152 <div class="col-md-1 col-sm-2 col-xs-3 logo"> 153 <a href="/"><img src="https://gobuffalo.io/assets/images/logo_med.png" alt=""></a> 154 </div> 155 <div class="col-md-10 col-sm-6 col-xs-7 titles"> 156 <h1> 157 <%= status %> - ERROR! 158 </h1> 159 </div> 160 </div> 161 </div> 162 </header> 163 164 <div class="container content"> 165 <div class="row"> 166 <div class="col-md-12"> 167 <h3>Error Trace</h3> 168 <pre><%= error %></pre> 169 170 <h3>Context</h3> 171 <pre><%= inspect(context) %></pre> 172 173 <h3>Parameters</h3> 174 <pre><%= inspect(params) %></pre> 175 176 <h3>Headers</h3> 177 <pre><%= inspect(headers) %></pre> 178 179 <h3>Form</h3> 180 <pre><%= inspect(posted_form) %></pre> 181 182 <h3>Routes</h3> 183 <table class="table table-striped"> 184 <thead> 185 <tr text-align="left"> 186 <th class="centered">METHOD</th> 187 <th>PATH</th> 188 <th>NAME</th> 189 <th>HANDLER</th> 190 </tr> 191 </thead> 192 <tbody> 193 194 <%= for (r) in routes { %> 195 <tr> 196 <td class="centered"> 197 <%= r.Method %> 198 </td> 199 <td> 200 <%= if (r.Method != "GET" || r.Path ~= "{") { %> 201 <%= r.Path %> 202 <% } else { %> 203 <a href="<%= r.Path %>"><%= r.Path %></a> 204 <% } %> 205 </td> 206 <td> 207 <%= r.PathName %> 208 </td> 209 <td><code><%= r.HandlerName %></code></td> 210 </tr> 211 <% } %> 212 213 </tbody> 214 </table> 215 </div> 216 </div> 217 <div class="foot"> <span> Powered by <a href="http://gobuffalo.io/">gobuffalo.io</a></span></div> 218 </div> 219 </body> 220 </html> 221 ` 222 var prodErrorTmpl = ` 223 <h1>We're Sorry!</h1> 224 <p> 225 It looks like something went wrong! Don't worry, we are aware of the problem and are looking into it. 226 </p> 227 <p> 228 Sorry if this has caused you any problems. Please check back again later. 229 </p> 230 `