2016-02-01 12:59:10 +01:00
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"fmt"
"html/template"
"net/http"
"reflect"
"runtime"
"strconv"
"strings"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/utils"
)
const (
errorTypeHandler = iota
errorTypeController
)
var tpl = `
< ! DOCTYPE html >
< html >
< head >
< meta http - equiv = "Content-Type" content = "text/html; charset=UTF-8" / >
< title > beego application error < / title >
< style >
html , body , body * { padding : 0 ; margin : 0 ; }
# header { background : # ffd ; border - bottom : solid 2 px # A31515 ; padding : 20 px 10 px ; }
# header h2 { }
# footer { border - top : solid 1 px # aaa ; padding : 5 px 10 px ; font - size : 12 px ; color : green ; }
# content { padding : 5 px ; }
# content . stack b { font - size : 13 px ; color : red ; }
# content . stack pre { padding - left : 10 px ; }
table { }
td . t { text - align : right ; padding - right : 5 px ; color : # 888 ; }
< / style >
< script type = "text/javascript" >
< / script >
< / head >
< body >
< div id = "header" >
< h2 > { { . AppError } } < / h2 >
< / div >
< div id = "content" >
< table >
< tr >
< td class = "t" > Request Method : < / td > < td > { { . RequestMethod } } < / td >
< / tr >
< tr >
< td class = "t" > Request URL : < / td > < td > { { . RequestURL } } < / td >
< / tr >
< tr >
< td class = "t" > RemoteAddr : < / td > < td > { { . RemoteAddr } } < / td >
< / tr >
< / table >
< div class = "stack" >
< b > Stack < / b >
< pre > { { . Stack } } < / pre >
< / div >
< / div >
< div id = "footer" >
< p > beego { { . BeegoVersion } } ( beego framework ) < / p >
< p > golang version : { { . GoVersion } } < / p >
< / div >
< / body >
< / html >
`
// render default application error page with error and stack string.
func showErr ( err interface { } , ctx * context . Context , stack string ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( tpl )
data := map [ string ] string {
"AppError" : fmt . Sprintf ( "%s:%v" , BConfig . AppName , err ) ,
"RequestMethod" : ctx . Input . Method ( ) ,
"RequestURL" : ctx . Input . URI ( ) ,
"RemoteAddr" : ctx . Input . IP ( ) ,
"Stack" : stack ,
"BeegoVersion" : VERSION ,
"GoVersion" : runtime . Version ( ) ,
}
ctx . ResponseWriter . WriteHeader ( 500 )
t . Execute ( ctx . ResponseWriter , data )
}
var errtpl = `
< ! DOCTYPE html >
< html lang = "en" >
< head >
< meta http - equiv = "Content-Type" content = "text/html; charset=UTF-8" >
< title > { { . Title } } < / title >
< style type = "text/css" >
* {
margin : 0 ;
padding : 0 ;
}
body {
background - color : # EFEFEF ;
font : .9 em "Lucida Sans Unicode" , "Lucida Grande" , sans - serif ;
}
# wrapper {
width : 600 px ;
margin : 40 px auto 0 ;
text - align : center ;
- moz - box - shadow : 5 px 5 px 10 px rgba ( 0 , 0 , 0 , 0.3 ) ;
- webkit - box - shadow : 5 px 5 px 10 px rgba ( 0 , 0 , 0 , 0.3 ) ;
box - shadow : 5 px 5 px 10 px rgba ( 0 , 0 , 0 , 0.3 ) ;
}
# wrapper h1 {
color : # FFF ;
text - align : center ;
margin - bottom : 20 px ;
}
# wrapper a {
display : block ;
font - size : .9 em ;
padding - top : 20 px ;
color : # FFF ;
text - decoration : none ;
text - align : center ;
}
# container {
width : 600 px ;
padding - bottom : 15 px ;
background - color : # FFFFFF ;
}
. navtop {
height : 40 px ;
background - color : # 24 B2EB ;
padding : 13 px ;
}
. content {
padding : 10 px 10 px 25 px ;
background : # FFFFFF ;
margin : ;
color : # 333 ;
}
a . button {
color : white ;
padding : 15 px 20 px ;
text - shadow : 1 px 1 px 0 # 00 A5FF ;
font - weight : bold ;
text - align : center ;
border : 1 px solid # 24 B2EB ;
margin : 0 px 200 px ;
clear : both ;
background - color : # 24 B2EB ;
border - radius : 100 px ;
- moz - border - radius : 100 px ;
- webkit - border - radius : 100 px ;
}
a . button : hover {
text - decoration : none ;
background - color : # 24 B2EB ;
}
< / style >
< / head >
< body >
< div id = "wrapper" >
< div id = "container" >
< div class = "navtop" >
< h1 > { { . Title } } < / h1 >
< / div >
< div id = "content" >
{ { . Content } }
< a href = "/" title = "Home" class = "button" > Go Home < / a > < br / >
< br > Powered by beego { { . BeegoVersion } }
< / div >
< / div >
< / div >
< / body >
< / html >
`
type errorInfo struct {
controllerType reflect . Type
handler http . HandlerFunc
method string
errorType int
}
// ErrorMaps holds map of http handlers for each error string.
// there is 10 kinds default error(40x and 50x)
var ErrorMaps = make ( map [ string ] * errorInfo , 10 )
// show 401 unauthorized error.
func unauthorized ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 401 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested can't be authorized." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>" )
t . Execute ( rw , data )
}
// show 402 Payment Required
func paymentRequired ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 402 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested Payment Required." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>" )
t . Execute ( rw , data )
}
// show 403 forbidden error.
func forbidden ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 403 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested is forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Your address may be blocked" +
"<br>The site may be disabled" +
"<br>You need to log in" +
"</ul>" )
t . Execute ( rw , data )
}
// show 404 notfound error.
func notFound ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 404 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested has flown the coop." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The page has moved" +
"<br>The page no longer exists" +
"<br>You were looking for your puppy and got lost" +
"<br>You like 404 pages" +
"</ul>" )
t . Execute ( rw , data )
}
// show 405 Method Not Allowed
func methodNotAllowed ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 405 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The method you have requested Not Allowed." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI" +
"<br>The response MUST include an Allow header containing a list of valid methods for the requested resource." +
"</ul>" )
t . Execute ( rw , data )
}
// show 500 internal server error.
func internalServerError ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 500 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested is down right now." +
"<br><br><ul>" +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>" )
t . Execute ( rw , data )
}
// show 501 Not Implemented.
func notImplemented ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 504 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested is Not Implemented." +
"<br><br><ul>" +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>" )
t . Execute ( rw , data )
}
// show 502 Bad Gateway.
func badGateway ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 502 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested is down right now." +
"<br><br><ul>" +
"<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." +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>" )
t . Execute ( rw , data )
}
// show 503 service unavailable error.
func serviceUnavailable ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 503 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested is unavailable." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br><br>The page is overloaded" +
"<br>Please try again later." +
"</ul>" )
t . Execute ( rw , data )
}
// show 504 Gateway Timeout.
func gatewayTimeout ( rw http . ResponseWriter , r * http . Request ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := map [ string ] interface { } {
"Title" : http . StatusText ( 504 ) ,
"BeegoVersion" : VERSION ,
}
data [ "Content" ] = template . HTML ( "<br>The page you have requested is unavailable." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<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." +
"<br>Please try again later." +
"</ul>" )
t . Execute ( rw , data )
}
// ErrorHandler registers http.HandlerFunc to each http err code string.
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
func ErrorHandler ( code string , h http . HandlerFunc ) * App {
ErrorMaps [ code ] = & errorInfo {
errorType : errorTypeHandler ,
handler : h ,
method : code ,
}
return BeeApp
}
// ErrorController registers ControllerInterface to each http err code string.
// usage:
// beego.ErrorController(&controllers.ErrorController{})
func ErrorController ( c ControllerInterface ) * App {
reflectVal := reflect . ValueOf ( c )
rt := reflectVal . Type ( )
ct := reflect . Indirect ( reflectVal ) . Type ( )
for i := 0 ; i < rt . NumMethod ( ) ; i ++ {
methodName := rt . Method ( i ) . Name
if ! utils . InSlice ( methodName , exceptMethod ) && strings . HasPrefix ( methodName , "Error" ) {
errName := strings . TrimPrefix ( methodName , "Error" )
ErrorMaps [ errName ] = & errorInfo {
errorType : errorTypeController ,
controllerType : ct ,
method : methodName ,
}
}
}
return BeeApp
}
// show error string as simple text message.
// if error string is empty, show 503 or 500 error as default.
func exception ( errCode string , ctx * context . Context ) {
atoi := func ( code string ) int {
v , err := strconv . Atoi ( code )
if err == nil {
return v
}
return 503
}
for _ , ec := range [ ] string { errCode , "503" , "500" } {
if h , ok := ErrorMaps [ ec ] ; ok {
executeError ( h , ctx , atoi ( ec ) )
return
}
}
//if 50x error has been removed from errorMap
ctx . ResponseWriter . WriteHeader ( atoi ( errCode ) )
ctx . WriteString ( errCode )
}
func executeError ( err * errorInfo , ctx * context . Context , code int ) {
if err . errorType == errorTypeHandler {
2016-06-27 11:38:50 +02:00
ctx . ResponseWriter . WriteHeader ( code )
2016-02-01 12:59:10 +01:00
err . handler ( ctx . ResponseWriter , ctx . Request )
return
}
if err . errorType == errorTypeController {
ctx . Output . SetStatus ( code )
//Invoke the request handler
vc := reflect . New ( err . controllerType )
execController , ok := vc . Interface ( ) . ( ControllerInterface )
if ! ok {
panic ( "controller is not ControllerInterface" )
}
//call the controller init function
execController . Init ( ctx , err . controllerType . Name ( ) , err . method , vc . Interface ( ) )
//call prepare function
execController . Prepare ( )
execController . URLMapping ( )
method := vc . MethodByName ( err . method )
method . Call ( [ ] reflect . Value { } )
//render template
if BConfig . WebConfig . AutoRender {
if err := execController . Render ( ) ; err != nil {
panic ( err )
}
}
// finish all runrouter. release resource
execController . Finish ( )
}
}