2020-07-22 14:50:08 +00: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.
2020-08-20 14:25:37 +00:00
package web
2020-07-22 14:50:08 +00:00
import (
"fmt"
"html/template"
"net/http"
"reflect"
"runtime"
"strconv"
"strings"
2020-09-08 13:54:35 +00:00
"github.com/astaxie/beego/pkg"
2020-08-20 14:25:37 +00:00
"github.com/astaxie/beego/pkg/infrastructure/utils"
"github.com/astaxie/beego/pkg/server/web/context"
2020-07-22 14:50:08 +00:00
)
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 ,
2020-09-08 13:54:35 +00:00
"BeegoVersion" : pkg . VERSION ,
2020-07-22 14:50:08 +00:00
"GoVersion" : runtime . Version ( ) ,
}
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 ) {
responseError ( rw , r ,
401 ,
"<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>" ,
)
}
// show 402 Payment Required
func paymentRequired ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
402 ,
"<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>" ,
)
}
// show 403 forbidden error.
func forbidden ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
403 ,
"<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>" ,
)
}
// show 422 missing xsrf token
func missingxsrf ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
422 ,
"<br>The page you have requested is forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>'_xsrf' argument missing from POST" +
"</ul>" ,
)
}
// show 417 invalid xsrf token
func invalidxsrf ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
417 ,
"<br>The page you have requested is forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>expected XSRF not found" +
"</ul>" ,
)
}
// show 404 not found error.
func notFound ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
404 ,
"<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>" ,
)
}
// show 405 Method Not Allowed
func methodNotAllowed ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
405 ,
"<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>" ,
)
}
// show 500 internal server error.
func internalServerError ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
500 ,
"<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>" ,
)
}
// show 501 Not Implemented.
func notImplemented ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
501 ,
"<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>" ,
)
}
// show 502 Bad Gateway.
func badGateway ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
502 ,
"<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>" ,
)
}
// show 503 service unavailable error.
func serviceUnavailable ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
503 ,
"<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>" ,
)
}
// show 504 Gateway Timeout.
func gatewayTimeout ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
504 ,
"<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>" ,
)
}
// show 413 Payload Too Large
func payloadTooLarge ( rw http . ResponseWriter , r * http . Request ) {
responseError ( rw , r ,
413 ,
` < br > The page you have requested is unavailable .
< br > Perhaps you are here because : < br > < br >
< ul >
< br > The request entity is larger than limits defined by server .
< br > Please change the request entity and try again .
< / ul >
` ,
)
}
func responseError ( rw http . ResponseWriter , r * http . Request , errCode int , errContent string ) {
t , _ := template . New ( "beegoerrortemp" ) . Parse ( errtpl )
data := M {
"Title" : http . StatusText ( errCode ) ,
2020-09-08 13:54:35 +00:00
"BeegoVersion" : pkg . VERSION ,
2020-07-22 14:50:08 +00:00
"Content" : template . HTML ( errContent ) ,
}
t . Execute ( rw , data )
}
// ErrorHandler registers http.HandlerFunc to each http err code string.
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
2020-09-09 14:55:18 +00:00
func ErrorHandler ( code string , h http . HandlerFunc ) * HttpServer {
2020-07-22 14:50:08 +00:00
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{})
2020-09-09 14:55:18 +00:00
func ErrorController ( c ControllerInterface ) * HttpServer {
2020-07-22 14:50:08 +00:00
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
}
// Exception Write HttpStatus with errCode and Exec error handler if exist.
func Exception ( errCode uint64 , ctx * context . Context ) {
exception ( strconv . FormatUint ( errCode , 10 ) , ctx )
}
// 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
}
if ctx . Output . Status == 0 {
return 503
}
return ctx . Output . Status
}
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 ) {
//make sure to log the error in the access log
LogAccess ( ctx , nil , code )
if err . errorType == errorTypeHandler {
ctx . ResponseWriter . WriteHeader ( code )
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 ( )
}
}