func (clt *Client) updateToken(tk *Token, url string) (err error) { api.DebugPrintGetRequest(url) httpResp, err := clt.httpClient().Get(url) if err != nil { return } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { return fmt.Errorf("http.Status: %s", httpResp.Status) } var result struct { Error Token } if err = api.DecodeJSONHttpResponse(httpResp.Body, &result); err != nil { return } if result.ErrCode != ErrCodeOK { return &result.Error } // 由于网络的延时 和 分布式服务器之间的时间可能不是绝对同步, access_token 过期时间留了一个缓冲区 switch { case result.ExpiresIn > 31556952: // 60*60*24*365.2425 return errors.New("expires_in too large: " + strconv.FormatInt(result.ExpiresIn, 10)) case result.ExpiresIn > 60*60: result.ExpiresIn -= 60 * 20 case result.ExpiresIn > 60*30: result.ExpiresIn -= 60 * 10 case result.ExpiresIn > 60*15: result.ExpiresIn -= 60 * 5 case result.ExpiresIn > 60*5: result.ExpiresIn -= 60 case result.ExpiresIn > 60: result.ExpiresIn -= 20 default: return errors.New("expires_in too small: " + strconv.FormatInt(result.ExpiresIn, 10)) } tk.AccessToken = result.AccessToken tk.CreatedAt = time.Now().Unix() tk.ExpiresIn = result.ExpiresIn if result.RefreshToken != "" { tk.RefreshToken = result.RefreshToken } if result.OpenId != "" { tk.OpenId = result.OpenId } if result.UnionId != "" { tk.UnionId = result.UnionId } if result.Scope != "" { tk.Scope = result.Scope } return }
func httpGetJSON(clt *http.Client, url string, response interface{}) error { api.DebugPrintGetRequest(url) httpResp, err := clt.Get(url) if err != nil { return err } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { return fmt.Errorf("http.Status: %s", httpResp.Status) } return api.DecodeJSONHttpResponse(httpResp.Body, response) }
// GetUserInfo 获取用户信息. // accessToken: 网页授权接口调用凭证 // openId: 用户的唯一标识 // lang: 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语, 如果留空 "" 则默认为 zh_CN // httpClient: 如果不指定则默认为 http.DefaultClient func GetUserInfo(accessToken, openId, lang string, httpClient *http.Client) (info *UserInfo, err error) { switch lang { case "": lang = LanguageZhCN case LanguageZhCN, LanguageZhTW, LanguageEN: default: lang = LanguageZhCN } if httpClient == nil { httpClient = http.DefaultClient } _url := "https://api.weixin.qq.com/sns/userinfo?access_token=" + url.QueryEscape(accessToken) + "&openid=" + url.QueryEscape(openId) + "&lang=" + lang api.DebugPrintGetRequest(_url) httpResp, err := httpClient.Get(_url) if err != nil { return } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { err = fmt.Errorf("http.Status: %s", httpResp.Status) return } var result struct { oauth2.Error UserInfo } if err = api.DecodeJSONHttpResponse(httpResp.Body, &result); err != nil { return } if result.ErrCode != oauth2.ErrCodeOK { err = &result.Error return } info = &result.UserInfo return }
// DownloadToWriter 通过ticket换取二维码, 写入到 writer. // 如果 clt == nil 则默认用 http.DefaultClient. func DownloadToWriter(ticket string, writer io.Writer, clt *http.Client) (written int64, err error) { if clt == nil { clt = http.DefaultClient } url := QrcodePicURL(ticket) api.DebugPrintGetRequest(url) httpResp, err := clt.Get(url) if err != nil { return } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { err = fmt.Errorf("http.Status: %s", httpResp.Status) return } return io.Copy(writer, httpResp.Body) }
func httpDownloadToWriter(clt *http.Client, url string, writer io.Writer, errorResult *core.Error) (written int64, err error) { api.DebugPrintGetRequest(url) httpResp, err := clt.Get(url) if err != nil { return 0, err } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { return 0, fmt.Errorf("http.Status: %s", httpResp.Status) } ContentDisposition := httpResp.Header.Get("Content-Disposition") ContentType, _, _ := mime.ParseMediaType(httpResp.Header.Get("Content-Type")) if ContentDisposition != "" && ContentType != "text/plain" && ContentType != "application/json" { // 返回的是媒体流 return io.Copy(writer, httpResp.Body) } else { // 返回的是错误信息 return 0, api.DecodeJSONHttpResponse(httpResp.Body, errorResult) } }
// Auth 检验授权凭证 access_token 是否有效. // accessToken: 网页授权接口调用凭证 // openId: 用户的唯一标识 // httpClient: 如果不指定则默认为 http.DefaultClient func Auth(accessToken, openId string, httpClient *http.Client) (valid bool, err error) { if httpClient == nil { httpClient = http.DefaultClient } _url := "https://api.weixin.qq.com/sns/auth?access_token=" + url.QueryEscape(accessToken) + "&openid=" + url.QueryEscape(openId) api.DebugPrintGetRequest(_url) httpResp, err := httpClient.Get(_url) if err != nil { return } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { err = fmt.Errorf("http.Status: %s", httpResp.Status) return } var result oauth2.Error if err = api.DecodeJSONHttpResponse(httpResp.Body, &result); err != nil { return } switch result.ErrCode { case oauth2.ErrCodeOK: valid = true return case 42001, 40001, 40014, 40003: valid = false return default: err = &result return } }
// updateToken 从微信服务器获取新的 access_token 并存入缓存, 同时返回该 access_token. func (srv *DefaultAccessTokenServer) updateToken(currentToken string) (token *accessToken, cached bool, err error) { if currentToken != "" { if p := (*accessToken)(atomic.LoadPointer(&srv.tokenCache)); p != nil && currentToken != p.Token { return p, true, nil // 无需更改 p.ExpiresIn 参数值, cached == true 时用不到 } } url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + srv.appId + "&secret=" + srv.appSecret api.DebugPrintGetRequest(url) httpResp, err := srv.httpClient.Get(url) if err != nil { atomic.StorePointer(&srv.tokenCache, nil) return } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { atomic.StorePointer(&srv.tokenCache, nil) err = fmt.Errorf("http.Status: %s", httpResp.Status) return } var result struct { Error accessToken } if err = api.DecodeJSONHttpResponse(httpResp.Body, &result); err != nil { atomic.StorePointer(&srv.tokenCache, nil) return } if result.ErrCode != ErrCodeOK { atomic.StorePointer(&srv.tokenCache, nil) err = &result.Error return } // 由于网络的延时, access_token 过期时间留有一个缓冲区 switch { case result.ExpiresIn > 31556952: // 60*60*24*365.2425 atomic.StorePointer(&srv.tokenCache, nil) err = errors.New("expires_in too large: " + strconv.FormatInt(result.ExpiresIn, 10)) return case result.ExpiresIn > 60*60: result.ExpiresIn -= 60 * 10 case result.ExpiresIn > 60*30: result.ExpiresIn -= 60 * 5 case result.ExpiresIn > 60*5: result.ExpiresIn -= 60 case result.ExpiresIn > 60: result.ExpiresIn -= 10 default: atomic.StorePointer(&srv.tokenCache, nil) err = errors.New("expires_in too small: " + strconv.FormatInt(result.ExpiresIn, 10)) return } tokenCopy := result.accessToken atomic.StorePointer(&srv.tokenCache, unsafe.Pointer(&tokenCopy)) token = &tokenCopy return }