github.com/astaxie/beego@v1.12.3/error.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package beego 16 17 import ( 18 "fmt" 19 "html/template" 20 "net/http" 21 "reflect" 22 "runtime" 23 "strconv" 24 "strings" 25 26 "github.com/astaxie/beego/context" 27 "github.com/astaxie/beego/utils" 28 ) 29 30 const ( 31 errorTypeHandler = iota 32 errorTypeController 33 ) 34 35 var tpl = ` 36 <!DOCTYPE html> 37 <html> 38 <head> 39 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 40 <title>beego application error</title> 41 <style> 42 html, body, body * {padding: 0; margin: 0;} 43 #header {background:#ffd; border-bottom:solid 2px #A31515; padding: 20px 10px;} 44 #header h2{ } 45 #footer {border-top:solid 1px #aaa; padding: 5px 10px; font-size: 12px; color:green;} 46 #content {padding: 5px;} 47 #content .stack b{ font-size: 13px; color: red;} 48 #content .stack pre{padding-left: 10px;} 49 table {} 50 td.t {text-align: right; padding-right: 5px; color: #888;} 51 </style> 52 <script type="text/javascript"> 53 </script> 54 </head> 55 <body> 56 <div id="header"> 57 <h2>{{.AppError}}</h2> 58 </div> 59 <div id="content"> 60 <table> 61 <tr> 62 <td class="t">Request Method: </td><td>{{.RequestMethod}}</td> 63 </tr> 64 <tr> 65 <td class="t">Request URL: </td><td>{{.RequestURL}}</td> 66 </tr> 67 <tr> 68 <td class="t">RemoteAddr: </td><td>{{.RemoteAddr }}</td> 69 </tr> 70 </table> 71 <div class="stack"> 72 <b>Stack</b> 73 <pre>{{.Stack}}</pre> 74 </div> 75 </div> 76 <div id="footer"> 77 <p>beego {{ .BeegoVersion }} (beego framework)</p> 78 <p>golang version: {{.GoVersion}}</p> 79 </div> 80 </body> 81 </html> 82 ` 83 84 // render default application error page with error and stack string. 85 func showErr(err interface{}, ctx *context.Context, stack string) { 86 t, _ := template.New("beegoerrortemp").Parse(tpl) 87 data := map[string]string{ 88 "AppError": fmt.Sprintf("%s:%v", BConfig.AppName, err), 89 "RequestMethod": ctx.Input.Method(), 90 "RequestURL": ctx.Input.URI(), 91 "RemoteAddr": ctx.Input.IP(), 92 "Stack": stack, 93 "BeegoVersion": VERSION, 94 "GoVersion": runtime.Version(), 95 } 96 t.Execute(ctx.ResponseWriter, data) 97 } 98 99 var errtpl = ` 100 <!DOCTYPE html> 101 <html lang="en"> 102 <head> 103 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 104 <title>{{.Title}}</title> 105 <style type="text/css"> 106 * { 107 margin:0; 108 padding:0; 109 } 110 111 body { 112 background-color:#EFEFEF; 113 font: .9em "Lucida Sans Unicode", "Lucida Grande", sans-serif; 114 } 115 116 #wrapper{ 117 width:600px; 118 margin:40px auto 0; 119 text-align:center; 120 -moz-box-shadow: 5px 5px 10px rgba(0,0,0,0.3); 121 -webkit-box-shadow: 5px 5px 10px rgba(0,0,0,0.3); 122 box-shadow: 5px 5px 10px rgba(0,0,0,0.3); 123 } 124 125 #wrapper h1{ 126 color:#FFF; 127 text-align:center; 128 margin-bottom:20px; 129 } 130 131 #wrapper a{ 132 display:block; 133 font-size:.9em; 134 padding-top:20px; 135 color:#FFF; 136 text-decoration:none; 137 text-align:center; 138 } 139 140 #container { 141 width:600px; 142 padding-bottom:15px; 143 background-color:#FFFFFF; 144 } 145 146 .navtop{ 147 height:40px; 148 background-color:#24B2EB; 149 padding:13px; 150 } 151 152 .content { 153 padding:10px 10px 25px; 154 background: #FFFFFF; 155 margin:; 156 color:#333; 157 } 158 159 a.button{ 160 color:white; 161 padding:15px 20px; 162 text-shadow:1px 1px 0 #00A5FF; 163 font-weight:bold; 164 text-align:center; 165 border:1px solid #24B2EB; 166 margin:0px 200px; 167 clear:both; 168 background-color: #24B2EB; 169 border-radius:100px; 170 -moz-border-radius:100px; 171 -webkit-border-radius:100px; 172 } 173 174 a.button:hover{ 175 text-decoration:none; 176 background-color: #24B2EB; 177 } 178 179 </style> 180 </head> 181 <body> 182 <div id="wrapper"> 183 <div id="container"> 184 <div class="navtop"> 185 <h1>{{.Title}}</h1> 186 </div> 187 <div id="content"> 188 {{.Content}} 189 <a href="/" title="Home" class="button">Go Home</a><br /> 190 191 <br>Powered by beego {{.BeegoVersion}} 192 </div> 193 </div> 194 </div> 195 </body> 196 </html> 197 ` 198 199 type errorInfo struct { 200 controllerType reflect.Type 201 handler http.HandlerFunc 202 method string 203 errorType int 204 } 205 206 // ErrorMaps holds map of http handlers for each error string. 207 // there is 10 kinds default error(40x and 50x) 208 var ErrorMaps = make(map[string]*errorInfo, 10) 209 210 // show 401 unauthorized error. 211 func unauthorized(rw http.ResponseWriter, r *http.Request) { 212 responseError(rw, r, 213 401, 214 "<br>The page you have requested can't be authorized."+ 215 "<br>Perhaps you are here because:"+ 216 "<br><br><ul>"+ 217 "<br>The credentials you supplied are incorrect"+ 218 "<br>There are errors in the website address"+ 219 "</ul>", 220 ) 221 } 222 223 // show 402 Payment Required 224 func paymentRequired(rw http.ResponseWriter, r *http.Request) { 225 responseError(rw, r, 226 402, 227 "<br>The page you have requested Payment Required."+ 228 "<br>Perhaps you are here because:"+ 229 "<br><br><ul>"+ 230 "<br>The credentials you supplied are incorrect"+ 231 "<br>There are errors in the website address"+ 232 "</ul>", 233 ) 234 } 235 236 // show 403 forbidden error. 237 func forbidden(rw http.ResponseWriter, r *http.Request) { 238 responseError(rw, r, 239 403, 240 "<br>The page you have requested is forbidden."+ 241 "<br>Perhaps you are here because:"+ 242 "<br><br><ul>"+ 243 "<br>Your address may be blocked"+ 244 "<br>The site may be disabled"+ 245 "<br>You need to log in"+ 246 "</ul>", 247 ) 248 } 249 250 // show 422 missing xsrf token 251 func missingxsrf(rw http.ResponseWriter, r *http.Request) { 252 responseError(rw, r, 253 422, 254 "<br>The page you have requested is forbidden."+ 255 "<br>Perhaps you are here because:"+ 256 "<br><br><ul>"+ 257 "<br>'_xsrf' argument missing from POST"+ 258 "</ul>", 259 ) 260 } 261 262 // show 417 invalid xsrf token 263 func invalidxsrf(rw http.ResponseWriter, r *http.Request) { 264 responseError(rw, r, 265 417, 266 "<br>The page you have requested is forbidden."+ 267 "<br>Perhaps you are here because:"+ 268 "<br><br><ul>"+ 269 "<br>expected XSRF not found"+ 270 "</ul>", 271 ) 272 } 273 274 // show 404 not found error. 275 func notFound(rw http.ResponseWriter, r *http.Request) { 276 responseError(rw, r, 277 404, 278 "<br>The page you have requested has flown the coop."+ 279 "<br>Perhaps you are here because:"+ 280 "<br><br><ul>"+ 281 "<br>The page has moved"+ 282 "<br>The page no longer exists"+ 283 "<br>You were looking for your puppy and got lost"+ 284 "<br>You like 404 pages"+ 285 "</ul>", 286 ) 287 } 288 289 // show 405 Method Not Allowed 290 func methodNotAllowed(rw http.ResponseWriter, r *http.Request) { 291 responseError(rw, r, 292 405, 293 "<br>The method you have requested Not Allowed."+ 294 "<br>Perhaps you are here because:"+ 295 "<br><br><ul>"+ 296 "<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI"+ 297 "<br>The response MUST include an Allow header containing a list of valid methods for the requested resource."+ 298 "</ul>", 299 ) 300 } 301 302 // show 500 internal server error. 303 func internalServerError(rw http.ResponseWriter, r *http.Request) { 304 responseError(rw, r, 305 500, 306 "<br>The page you have requested is down right now."+ 307 "<br><br><ul>"+ 308 "<br>Please try again later and report the error to the website administrator"+ 309 "<br></ul>", 310 ) 311 } 312 313 // show 501 Not Implemented. 314 func notImplemented(rw http.ResponseWriter, r *http.Request) { 315 responseError(rw, r, 316 501, 317 "<br>The page you have requested is Not Implemented."+ 318 "<br><br><ul>"+ 319 "<br>Please try again later and report the error to the website administrator"+ 320 "<br></ul>", 321 ) 322 } 323 324 // show 502 Bad Gateway. 325 func badGateway(rw http.ResponseWriter, r *http.Request) { 326 responseError(rw, r, 327 502, 328 "<br>The page you have requested is down right now."+ 329 "<br><br><ul>"+ 330 "<br>The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request."+ 331 "<br>Please try again later and report the error to the website administrator"+ 332 "<br></ul>", 333 ) 334 } 335 336 // show 503 service unavailable error. 337 func serviceUnavailable(rw http.ResponseWriter, r *http.Request) { 338 responseError(rw, r, 339 503, 340 "<br>The page you have requested is unavailable."+ 341 "<br>Perhaps you are here because:"+ 342 "<br><br><ul>"+ 343 "<br><br>The page is overloaded"+ 344 "<br>Please try again later."+ 345 "</ul>", 346 ) 347 } 348 349 // show 504 Gateway Timeout. 350 func gatewayTimeout(rw http.ResponseWriter, r *http.Request) { 351 responseError(rw, r, 352 504, 353 "<br>The page you have requested is unavailable"+ 354 "<br>Perhaps you are here because:"+ 355 "<br><br><ul>"+ 356 "<br><br>The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI."+ 357 "<br>Please try again later."+ 358 "</ul>", 359 ) 360 } 361 362 // show 413 Payload Too Large 363 func payloadTooLarge(rw http.ResponseWriter, r *http.Request) { 364 responseError(rw, r, 365 413, 366 `<br>The page you have requested is unavailable. 367 <br>Perhaps you are here because:<br><br> 368 <ul> 369 <br>The request entity is larger than limits defined by server. 370 <br>Please change the request entity and try again. 371 </ul> 372 `, 373 ) 374 } 375 376 func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) { 377 t, _ := template.New("beegoerrortemp").Parse(errtpl) 378 data := M{ 379 "Title": http.StatusText(errCode), 380 "BeegoVersion": VERSION, 381 "Content": template.HTML(errContent), 382 } 383 t.Execute(rw, data) 384 } 385 386 // ErrorHandler registers http.HandlerFunc to each http err code string. 387 // usage: 388 // beego.ErrorHandler("404",NotFound) 389 // beego.ErrorHandler("500",InternalServerError) 390 func ErrorHandler(code string, h http.HandlerFunc) *App { 391 ErrorMaps[code] = &errorInfo{ 392 errorType: errorTypeHandler, 393 handler: h, 394 method: code, 395 } 396 return BeeApp 397 } 398 399 // ErrorController registers ControllerInterface to each http err code string. 400 // usage: 401 // beego.ErrorController(&controllers.ErrorController{}) 402 func ErrorController(c ControllerInterface) *App { 403 reflectVal := reflect.ValueOf(c) 404 rt := reflectVal.Type() 405 ct := reflect.Indirect(reflectVal).Type() 406 for i := 0; i < rt.NumMethod(); i++ { 407 methodName := rt.Method(i).Name 408 if !utils.InSlice(methodName, exceptMethod) && strings.HasPrefix(methodName, "Error") { 409 errName := strings.TrimPrefix(methodName, "Error") 410 ErrorMaps[errName] = &errorInfo{ 411 errorType: errorTypeController, 412 controllerType: ct, 413 method: methodName, 414 } 415 } 416 } 417 return BeeApp 418 } 419 420 // Exception Write HttpStatus with errCode and Exec error handler if exist. 421 func Exception(errCode uint64, ctx *context.Context) { 422 exception(strconv.FormatUint(errCode, 10), ctx) 423 } 424 425 // show error string as simple text message. 426 // if error string is empty, show 503 or 500 error as default. 427 func exception(errCode string, ctx *context.Context) { 428 atoi := func(code string) int { 429 v, err := strconv.Atoi(code) 430 if err == nil { 431 return v 432 } 433 if ctx.Output.Status == 0 { 434 return 503 435 } 436 return ctx.Output.Status 437 } 438 439 for _, ec := range []string{errCode, "503", "500"} { 440 if h, ok := ErrorMaps[ec]; ok { 441 executeError(h, ctx, atoi(ec)) 442 return 443 } 444 } 445 //if 50x error has been removed from errorMap 446 ctx.ResponseWriter.WriteHeader(atoi(errCode)) 447 ctx.WriteString(errCode) 448 } 449 450 func executeError(err *errorInfo, ctx *context.Context, code int) { 451 //make sure to log the error in the access log 452 LogAccess(ctx, nil, code) 453 454 if err.errorType == errorTypeHandler { 455 ctx.ResponseWriter.WriteHeader(code) 456 err.handler(ctx.ResponseWriter, ctx.Request) 457 return 458 } 459 if err.errorType == errorTypeController { 460 ctx.Output.SetStatus(code) 461 //Invoke the request handler 462 vc := reflect.New(err.controllerType) 463 execController, ok := vc.Interface().(ControllerInterface) 464 if !ok { 465 panic("controller is not ControllerInterface") 466 } 467 //call the controller init function 468 execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface()) 469 470 //call prepare function 471 execController.Prepare() 472 473 execController.URLMapping() 474 475 method := vc.MethodByName(err.method) 476 method.Call([]reflect.Value{}) 477 478 //render template 479 if BConfig.WebConfig.AutoRender { 480 if err := execController.Render(); err != nil { 481 panic(err) 482 } 483 } 484 485 // finish all runrouter. release resource 486 execController.Finish() 487 } 488 }