[Go] Google API 基本運用

程式語言:Go
Package:
Google APIs
google-api-go-client

功能:運用 google 各種服務

申請 API Key

  1. 進入 Google APIs
  2. 上方:新增專案
  3. 資訊頁面:點選 啟用 API 和服務
  4. 憑證頁面:依需求建立憑證

基本用法

  1. 建立 client,內建已含認證方式,自定義 client 可在 Transport 實作
  2. service.New(client) 建立 service 呼叫 API
  3. service.apiService.apiCall(...) 建立 call
  4. call.parameter(...) 設定各種參數
  5. call.Do() 送出 request
範例:
package main

import (
    "fmt"
    "google.golang.org/api/drive/v3"
)

func main() {
    auth := NewAuth(redirectURL)
    client := auth.GetClient(clientsecretPath, "drive-go.json")

    service, err := drive.New(client)
    if err != nil {
        panic(err)
    }

    call := service.Files.List()
    call = call.PageSize(2)

    response, err := call.Do()
    if err != nil {
        panic(err)
    }
}

oauth2 範例

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "net/url"
    "os"
    "os/user"
    "path/filepath"

    "github.com/pkg/errors"
    "golang.org/x/net/context"
    "golang.org/x/oauth2"
)

// This variable indicates whether the script should launch a web server to
// initiate the authorization flow or just display the URL in the terminal
// window. Note the following instructions based on this setting:
// * launchWebServer = true
//   1. Use OAuth2 credentials for a web application
//   2. Define authorized redirect URIs for the credential in the Google APIs
//      Console and set the RedirectURL property on the config object to one
//      of those redirect URIs. For example:
//      config.RedirectURL = "http://localhost:8090"
//   3. In the startWebServer function below, update the URL in this line
//      to match the redirect URI you selected:
//         listener, err := net.Listen("tcp", "localhost:8090")
//      The redirect URI identifies the URI to which the user is sent after
//      completing the authorization flow. The listener then captures the
//      authorization code in the URL and passes it back to this script.
// * launchWebServer = false
//   1. Use OAuth2 credentials for an installed application. (When choosing
//      the application type for the OAuth2 client ID, select "Other".)
//   2. Set the redirect URI to "urn:ietf:wg:oauth:2.0:oob", like this:
//      config.RedirectURL = "urn:ietf:wg:oauth:2.0:oob"
//   3. When running the script, complete the auth flow. Then copy the
//      authorization code from the browser and enter it on the command line.
const launchWebServer = true

const missingClientSecretsMessage = `
Please configure OAuth 2.0
To make this sample run, you need to populate the client_secrets.json file
found at:
   %v
with information from the {{ Google Cloud Console }}{{ https://cloud.google.com/console }}For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
`

// Auth for oauth2
type Auth struct {
    redirectURL *url.URL
    config      *oauth2.Config

    useTLS          bool
    tlsCert, tlsKey string
}

// NewAuth create Auth
func NewAuth(redirectURL string) *Auth {
    a := &Auth{}
    var err error
    a.redirectURL, err = url.Parse(redirectURL)
    if err != nil {
        log.Fatal(err)
    }

    return a
}

// SetTLS when redirectURL is TLS, it should be setted
func (a *Auth) SetTLS(TLSCertPath, TLSKeyPath string) {
    a.tlsCert = TLSCertPath
    a.tlsKey = TLSKeyPath
    a.useTLS = true
}

// GetClient generate a Client. It returns the generated Client.
// clientsecret file format reference to client_secret.json.sample
func (a *Auth) GetClient(clientsecretPath, tokenFile string) *http.Client {
    ctx := context.Background()

    b, err := ioutil.ReadFile(clientsecretPath)
    if err != nil {
        log.Fatalf("Unable to read client secret file: %v", err)
    }

    // If modifying the scope, delete your previously saved credentials
    // at ~/.credentials/tokenFile
    err = a.configFromJSON(b)
    if err != nil {
        log.Fatalf("Unable to parse client secret file to config: %v", err)
    }

    // Use a redirect URI like this for a web app. The redirect URI must be a
    // valid one for your OAuth2 credentials.
    a.config.RedirectURL = a.redirectURL.String()
    // Use the following redirect URI if launchWebServer=false in oauth2.go
    // config.RedirectURL = "urn:ietf:wg:oauth:2.0:oob"

    cacheFile, err := a.tokenCacheFile(tokenFile)
    if err != nil {
        log.Fatalf("Unable to get path to cached credential file. %v", err)
    }
    token, err := a.tokenFromFile(cacheFile)
    if err != nil {
        if launchWebServer {
            fmt.Println("Trying to get token from web")
            token, err = a.getTokenFromWeb()
        } else {
            fmt.Println("Trying to get token from prompt")
            token, err = a.getTokenFromPrompt()
        }
        if err == nil {
            a.saveToken(cacheFile, token)
        }
    }
    return a.config.Client(ctx, token)
}

func (a *Auth) configFromJSON(jsonKey []byte, scope ...string) error {
    type cred struct {
        ClientID     string   `json:"client_id,omitempty"`
        ClientSecret string   `json:"client_secret,omitempty"`
        RedirectURIs []string `json:"redirect_uris,omitempty"`
        AuthURI      string   `json:"auth_uri,omitempty"`
        TokenURI     string   `json:"token_uri,omitempty"`
    }
    var j struct {
        Web       *cred `json:"web,omitempty"`
        Installed *cred `json:"installed,omitempty"`
    }
    if err := json.Unmarshal(jsonKey, &j); err != nil {
        return err
    }
    var c *cred
    switch {
    case j.Web != nil:
        c = j.Web
    case j.Installed != nil:
        c = j.Installed
    default:
        return fmt.Errorf("oauth2: no credentials found")
    }
    if len(c.RedirectURIs) < 1 {
        return errors.New("oauth2: missing redirect URL in the client_credentials.json")
    }
    a.config = &oauth2.Config{
        ClientID:     c.ClientID,
        ClientSecret: c.ClientSecret,
        RedirectURL:  c.RedirectURIs[0],
        Scopes:       scope,
        Endpoint: oauth2.Endpoint{
            AuthURL:  c.AuthURI,
            TokenURL: c.TokenURI,
        },
    }

    return nil
}

// startWebServer starts a web server that listens on http://localhost:8080.
// The webserver waits for an oauth code in the three-legged auth flow.
func (a *Auth) startWebServer() (codeCh chan string, err error) {
    listener, err := net.Listen("tcp", a.redirectURL.Host)
    if err != nil {
        return nil, errors.Wrapf(err, "net.Listen")
    }
    codeCh = make(chan string)

    handFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        code := r.FormValue("code")
        codeCh <- code // send code to OAuth flow
        listener.Close()
        w.Header().Set("Content-Type", "text/plain")
        fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code)
    })
    if a.useTLS {
        log.Printf("create TLS Server\n")
        log.Printf("certPath: %s\n", a.tlsCert)
        log.Printf("keyPath: %s\n", a.tlsKey)
        go http.ServeTLS(listener, handFunc, a.tlsCert, a.tlsKey)
    } else {
        go http.Serve(listener, handFunc)
    }

    return codeCh, nil
}

// Exchange the authorization code for an access token
func (a *Auth) exchangeToken(code string) (*oauth2.Token, error) {
    // set access_type offline for refresh token
    token, err := a.config.Exchange(oauth2.NoContext, code, oauth2.AccessTypeOffline)
    if err != nil {
        log.Fatalf("Unable to retrieve token %v", err)
    }
    return token, nil
}

// getTokenFromPrompt uses Config to request a Token and prompts the user
// to enter the token on the command line. It returns the retrieved Token.
func (a *Auth) getTokenFromPrompt() (*oauth2.Token, error) {
    authURL := a.config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)

    var code string
    fmt.Printf("Go to the following link in your browser. After completing "+
        "the authorization flow, enter the authorization code on the command "+
        "line: \n%v\n", authURL)

    if _, err := fmt.Scan(&code); err != nil {
        log.Fatalf("Unable to read authorization code %v", err)
    }

    fmt.Println(authURL)
    return a.exchangeToken(code)
}

// getTokenFromWeb uses Config to request a Token.
// It returns the retrieved Token.
func (a *Auth) getTokenFromWeb() (*oauth2.Token, error) {
    codeCh, err := a.startWebServer()
    if err != nil {
        fmt.Printf("Unable to start a web server. %s", err)
        return nil, errors.Wrapf(err, "a.startWebServer")
    }

    authURL := a.config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
    err = openURL(authURL)
    if err != nil {
        log.Fatalf("Unable to open authorization URL in web server: %v", err)
    } else {
        fmt.Println("Your browser has been opened to an authorization URL.",
            "This program will resume once authorization has been provided.")
        fmt.Println(authURL)
    }

    // Wait for the web server to get the code.
    code := <-codeCh
    return a.exchangeToken(code)
}

// tokenCacheFile generates credential file path/filename.
// It returns the generated credential path/filename.
func (a *Auth) tokenCacheFile(fileName string) (string, error) {
    usr, err := user.Current()
    if err != nil {
        return "", err
    }
    tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
    os.MkdirAll(tokenCacheDir, 0700)
    return filepath.Join(tokenCacheDir,
        url.QueryEscape(fileName)), err
}

// tokenFromFile retrieves a Token from a given file path.
// It returns the retrieved Token and any read error encountered.
func (a *Auth) tokenFromFile(file string) (*oauth2.Token, error) {
    f, err := os.Open(file)
    if err != nil {
        return nil, err
    }
    t := &oauth2.Token{}
    err = json.NewDecoder(f).Decode(t)
    defer f.Close()
    return t, err
}

// saveToken uses a file path to create a file and store the
// token in it.
func (a *Auth) saveToken(file string, token *oauth2.Token) {
    fmt.Println("trying to save token")
    fmt.Printf("Saving credential file to: %s\n", file)
    f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        log.Fatalf("Unable to cache oauth token: %v", err)
    }
    defer f.Close()
    json.NewEncoder(f).Encode(token)
}

參考

取得 Google API Key(金鑰) 流程,啟用服務 + 瞭解配額限制
google 官方 oauth2 範例
Go實戰--golang中OAuth2.0的使用(使用google賬號進行登陸驗證)

留言