github.com/aldelo/common@v1.5.1/wrapper/gin/ginhelper.go (about) 1 package gin 2 3 import ( 4 "bytes" 5 "fmt" 6 util "github.com/aldelo/common" 7 "github.com/aldelo/common/rest" 8 "github.com/gin-gonic/gin" 9 ) 10 11 // ResponseBodyWriterInterceptor struct contains response body writer interceptor 12 type ResponseBodyWriterInterceptor struct { 13 gin.ResponseWriter 14 RespBody *bytes.Buffer 15 } 16 17 func (r ResponseBodyWriterInterceptor) Write(b []byte) (int, error) { 18 r.RespBody.Write(b) 19 return r.ResponseWriter.Write(b) 20 } 21 22 func (r ResponseBodyWriterInterceptor) WriteString(s string) (int, error) { 23 r.RespBody.WriteString(s) 24 return r.ResponseWriter.WriteString(s) 25 } 26 27 // ReCAPTCHAResponseIFace interface 28 type ReCAPTCHAResponseIFace interface { 29 GetReCAPTCHAResponse() string 30 } 31 32 // BindPostDataFailed will return a 412 response to caller via gin context 33 func BindPostDataFailed(c *gin.Context) { 34 if c != nil { 35 c.String(412, func() string{ 36 rawBody, _ := c.GetRawData() 37 return fmt.Sprintf(`{"errortype":"bind-post-data","error":"%s"}`, "Bind Post Data to Struct Failed: " + string(rawBody)) 38 }()) 39 } 40 } 41 42 // VerifyGoogleReCAPTCHAv2Failed will return a 412 response to caller via gin context 43 func VerifyGoogleReCAPTCHAv2Failed(c *gin.Context, errInfo string) { 44 if c != nil { 45 c.String(412, func() string{ 46 return fmt.Sprintf(`{"errortype":"verify-google-recaptcha-v2","error":"%s"}`, errInfo) 47 }()) 48 } 49 } 50 51 // MarshalQueryParametersFailed will return a 412 response to caller via gin context 52 func MarshalQueryParametersFailed(c *gin.Context, errInfo string) { 53 if c != nil { 54 c.String(412, func() string{ 55 return fmt.Sprintf(`{"errortype":"marshal-query-parameters","error":"%s"}`, errInfo) 56 }()) 57 } 58 } 59 60 // ActionServerFailed will return a 500 response to caller via gin context 61 func ActionServerFailed(c *gin.Context, errInfo string) { 62 if c != nil { 63 c.String(500, func() string{ 64 return fmt.Sprintf(`{"errortype":"action-server-failed","error":"%s"}`, errInfo) 65 }()) 66 } 67 } 68 69 // ActionStatusNotOK will return a 404 response to caller via gin context 70 func ActionStatusNotOK(c *gin.Context, errInfo string) { 71 if c != nil { 72 c.String(404, func() string{ 73 return fmt.Sprintf(`{"errortype":"action-status-not-ok","error":"%s"}`, errInfo) 74 }()) 75 } 76 } 77 78 // VerifyGoogleReCAPTCHAv2 is a helper for use gin web server, 79 // it will verify the given recaptcha response with the recaptcha secret pass in from gin context, 80 // and if verify successful, nil is returned 81 func VerifyGoogleReCAPTCHAv2(c *gin.Context, recaptchaResponse string, recaptchaRequired bool) (err error) { 82 if c == nil { 83 return fmt.Errorf("Verify Google ReCAPTCHA v2 Requires GIN Context") 84 } 85 86 if !recaptchaRequired { 87 if util.LenTrim(recaptchaResponse) == 0 { 88 return nil 89 } 90 } else { 91 if util.LenTrim(recaptchaResponse) == 0 { 92 return fmt.Errorf("Google ReCAPTCHA v2 Verification is Required") 93 } 94 } 95 96 if key, ok := c.Get("google_recaptcha_secret"); ok { 97 if success, _, _, e := util.VerifyGoogleReCAPTCHAv2(recaptchaResponse, key.(string)); e != nil { 98 return e 99 } else { 100 if success { 101 return nil 102 } else { 103 return fmt.Errorf("Verify Google ReCAPTCHA v2 Result = Not Successful") 104 } 105 } 106 } else { 107 return nil 108 } 109 } 110 111 // HandleReCAPTCHAv2 is a helper to simplify handler prep code to bind and perform recaptcha service, 112 // if false is returned, response is given to caller as failure, further processing stops 113 // if true is returned, continue with handler code 114 func HandleReCAPTCHAv2(c *gin.Context, bindingInputPtr interface{}) bool { 115 if c == nil { 116 return false 117 } 118 119 if bindingInputPtr == nil { 120 return false 121 } 122 123 if r, ok := bindingInputPtr.(ReCAPTCHAResponseIFace); !ok { 124 BindPostDataFailed(c) 125 return false 126 } else { 127 if e := VerifyGoogleReCAPTCHAv2(c, r.GetReCAPTCHAResponse(), true); e != nil { 128 VerifyGoogleReCAPTCHAv2Failed(c, e.Error()) 129 return false 130 } else { 131 // recaptcha verify success 132 return true 133 } 134 } 135 } 136 137 // PostDataToHost will post data from struct pointer object via gin context to target host, 138 // the tagName and excludeTagName is used for query parameters marshaling 139 func PostDataToHost(c *gin.Context, structPtr interface{}, tagName string, excludeTagName string, postUrl string) { 140 if c != nil { 141 if structPtr == nil { 142 MarshalQueryParametersFailed(c, "Post Data Struct is Nil") 143 } else if util.LenTrim(postUrl) == 0 { 144 MarshalQueryParametersFailed(c, "Post Target URL is Required") 145 } else { 146 if util.LenTrim(tagName) == 0 { 147 tagName = "json" 148 } 149 150 if qp, e := util.MarshalStructToQueryParams(structPtr, tagName, excludeTagName); e != nil { 151 MarshalQueryParametersFailed(c, e.Error()) 152 } else { 153 if status, resp, e := rest.POST(postUrl, nil, qp); e != nil { 154 ActionServerFailed(c, e.Error()) 155 } else if status != 200 { 156 ActionStatusNotOK(c, resp) 157 } else { 158 // success 159 c.String(200, resp) 160 } 161 } 162 } 163 } 164 }