

goで書いたGAE上のアプリからスプレッドシートの内容を編集したいと思い、以下のようなコードを書いたのですが、  
スプレッドシートの認証で引っかかってしまいます。  
どなたか気づかれたことがありましたらご教示いただけないでしょうか？

前提として、  
- IAMのサービスアカウントを作成して、jsonの秘密鍵を習得しローカルにおいています。  
- また、client_emailはスプレッドシートとも共有設定をしました。  
- goは1.9を使っています

スプレッドシートのAPIや、  
<https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get?hl=ja>  
AppEngineのチュートリアルを参考に、  
<https://cloud.google.com/appengine/docs/standard/go/building-app/creating-
your-application>  
以下のようなコードを書きました。  
ここでは、アクセスできればいいので、ブラウザには適当に「success」だけを出力するようにしています。

[code]

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
    
        "golang.org/x/oauth2"
        "golang.org/x/oauth2/google"
        "google.golang.org/api/sheets/v4"
        "google.golang.org/appengine" // Required external App Engine library
    )
    
    func httpClient(credentialFilePath string) (*http.Client, error) {
        data, err := ioutil.ReadFile(credentialFilePath)
        if err != nil {
            return nil, err
        }
        conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/spreadsheets")
        if err != nil {
            return nil, err
        }
    
        return conf.Client(oauth2.NoContext), nil
    }                                                                                                       
    
    func main() {
        spreadsheetId := "スプレッドシートのID"
        credentialFilePath := "credential.json"
    
        client, err := httpClient(credentialFilePath)
        if err != nil {
            log.Fatal(err)
        }
    
        sheetService, err := sheets.New(client)
        if err != nil {
            log.Fatalf("Unable to retrieve Sheets Client %v", err)
        }
        _, err = sheetService.Spreadsheets.Get(spreadsheetId).Do()
        if err != nil {
            log.Fatalf("Unable to get Spreadsheets. %v", err)
        }
    
        code_str := "success"  // ブラウザに表示させる文字
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            indexHandler(w, r, code_str)
        })
        appengine.Main() // Starts the server to receive requests
    }
    
    func indexHandler(w http.ResponseWriter, r *http.Request, code string) {
        // if statement redirects all invalid URLs to the root homepage.
        // Ex: if URL is http://[YOUR_PROJECT_ID].appspot.com/FOO, it will be
        // redirected to http://[YOUR_PROJECT_ID].appspot.com.
        if r.URL.Path != "/" {
            http.Redirect(w, r, "/", http.StatusFound)
            return
        }
    
        //fmt.Fprintln(w, "Hello, Gopher Network!")
        fmt.Fprintln(w, code)
    }                                                                                   
    
[/code]

go run sample.goで実行すると「success」をブラウザから確認できました。  
つまりローカルで実行すると、秘密鍵を使ってスプレッドシートにアクセスすることができています。

ところが、エミュレータを使って dev_appserver.py app.yaml を実行すると、  
下記のようにoauth2: cannot fetch token: Post <https://oauth2.googleapis.com/token>:
not an App Engine contextと出てしまいます。

[code]

    ERROR    2018-08-20 15:23:41,150 http_runtime.py:414] bad runtime process port ['']
    INFO     2018-08-20 15:23:41,150 instance.py:294] Instance PID: 16732
    2018/08/20 15:23:41 Unable to get Spreadsheets. Get https://sheets.googleapis.com/v4/spreadsheets/スプレッドシートのID?alt=json: oauth2: cannot fetch token: Post https://oauth2.googleapis.com/token: not an App Engine context
    
[/code]

なにか気づかれた点があればどなたかご教示いただけないでしょうか？  
以上、よろしくお願いいたします。


CC BY-SA 4.0
2018-08-20T16:18:35.913
47670
2019-11-27T11:02:00.483
29684
question
google-app-engine google-cloud
GAEのエミュレータでサービスアカウントからスプレッドシートにアクセスできない


同じくGAE/GoでSpreadsheets読んでWebに吐く実験をしたことがあります  
（<https://lists.kogus.org/>）。

httpClient()がconf.Client(oauth2.NoContext)を返してますが、  
そこはconf.Client(ctx)として、context.Contextを返さないと。


CC BY-SA 4.0
2018-08-21T03:06:10.877
47684
2018-08-21T03:06:10.877
14529
47670
answer


【以下で解決しました】

<https://cloud.google.com/appengine/docs/standard/go/building-app/creating-
your-application>  
公式のチュートリアルのコードをもとに作ったものですが、  
これでスプレッドシートのデータを取得することができました。

@belbo ご教示いただきありがとうございました。  
大変感謝しております

[code]

    package main
    
    import (
            "fmt"
            "io/ioutil"
            "log"
            "net/http"
    
            "golang.org/x/oauth2/google"
            "google.golang.org/api/sheets/v4"
            "google.golang.org/appengine" // Required external App Engine library
    )
    
    func main() {
            http.HandleFunc("/", indexHandler)
            appengine.Main() // Starts the server to receive requests
    }
    
    func indexHandler(w http.ResponseWriter, r *http.Request) {
            if r.URL.Path != "/" {
                    http.Redirect(w, r, "/", http.StatusFound)
                    return
            }
    
            // 以下追記した部分
            credentialFilePath := "証明書json"
            data, err := ioutil.ReadFile(credentialFilePath)
            if err != nil {
                    return
            }
            conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/spreadsheets")
            if err != nil {
                    return
            }
            ctx := appengine.NewContext(r)
            client := conf.Client(ctx)
    
            sheetService, err := sheets.New(client)
            if err != nil {
                    log.Fatalf("Unable to retrieve Sheets Client %v", err)
            }
    
            spreadsheetId := "スプレッドシートのID"
            readRange := "ワークシート名"
            resp, err := sheetService.Spreadsheets.Values.Get(spreadsheetId, readRange).Do()
            if err != nil {
                    log.Fatalf("Unable to get Spreadsheets. %v", err)
            }
    
            //fmt.Fprintln(w, "Hello, Gopher Network!")
            fmt.Fprintln(w, resp.Values)  // スプレッドシートのデータをブラウザに表示
    }
    
[/code]

→ dev_appserver.py app.yaml の結果ブラウザに表示されました。


CC BY-SA 4.0
2018-08-22T15:27:20.183
47735
2018-08-22T15:27:20.183
29684
47670
answer