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  }