2017-11-23 17 views
1

私は、Instagram APIとやりとりする小さなコマンドラインベースのGo botを構築中です。応答を返した後のHTTPサーバーのシャットダウン

Instagram APIはOAuthベースであり、コマンドラインベースのアプリケーションにはあまり適していません。

これを回避するには、ブラウザで適切な認証URLを開き、ローカルサーバーを使用してリダイレクトURIをスピンアップします。この方法で、アクセストークンをキャプチャし、正常に表示することができますこれをURLから手動で取得します。

これまでのところ、アプリケーションはブラウザを認証URLに正常に開くことができ、認証してローカルのHTTPサーバーにリダイレクトします。

アクセストークンがユーザーに表示された後、HTTPサーバーが必要なくなり、この後、手動でサーバーをシャットダウンする必要があります。

は、これを行うために、私はこのanswerからインスピレーションを描き、下記まで叩い:

package main 

import (
    "fmt" 
    "io" 
    "log" 
    "net/http" 
    "os/exec" 
    "runtime" 
    "time" 
) 

var client_id = "my_client_id" 
var client_secret = "my_client_secret" 
var redirect_url = "http://localhost:8000/instagram/callback" 

func main() { 

    srv := startHttpServer() 

    openbrowser(fmt.Sprintf("https://api.instagram.com/oauth/authorize/?client_id=%v&redirect_uri=%v&response_type=code", client_id, redirect_url)) 

    // Backup to gracefully shutdown the server 
    time.Sleep(20 * time.Second) 
    if err := srv.Shutdown(nil); err != nil { 
     panic(err) // failure/timeout shutting down the server gracefully 
    } 
} 

func showTokenToUser(w http.ResponseWriter, r *http.Request, srv *http.Server) { 
    io.WriteString(w, fmt.Sprintf("Your access token is: %v", r.URL.Query().Get("code"))) 
    if err := srv.Shutdown(nil); err != nil { 
     log.Fatal(err) // failure/timeout shutting down the server gracefully 
    } 
} 

func startHttpServer() *http.Server { 
    srv := &http.Server{Addr: ":8000"} 

    http.HandleFunc("/instagram/callback", func(w http.ResponseWriter, r *http.Request) { 
     showTokenToUser(w, r, srv) 
    }) 

    go func() { 
     if err := srv.ListenAndServe(); err != nil { 
      // cannot panic, because this probably is an intentional close 
      log.Printf("Httpserver: ListenAndServe() error: %s", err) 
     } 
    }() 

    // returning reference so caller can call Shutdown() 
    return srv 
} 

func openbrowser(url string) { 
    var err error 

    switch runtime.GOOS { 
    case "linux": 
     err = exec.Command("xdg-open", url).Start() 
    case "windows": 
     err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() 
    case "darwin": 
     err = exec.Command("open", url).Start() 
    default: 
     err = fmt.Errorf("unsupported platform") 
    } 
    if err != nil { 
     log.Fatal(err) 
    } 
} 

しかし、上記の原因このエラー:

2017/11/23 16:02:03 Httpserver: ListenAndServe() error: http: Server closed

2017/11/23 16:02:03 http: panic serving [::1]:61793: runtime error: invalid memory address or nil pointer dereference

私はこれらの行をコメントアウトした場合私がコールバックルートをヒットしたときにサーバーをシャットダウンすることなく、完璧に動作します。

どこが間違っていますか?ユーザーにテキストを表示した後で、コールバックルートをたどったときにサーバーをシャットダウンできるようにするには、何を変更する必要がありますか。

答えて

3

用途:

package main 

import (
    "context" 
    "io" 
    "log" 
    "net/http" 
) 

func main() { 
    ctx, cancel := context.WithCancel(context.Background()) 
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 
     io.WriteString(w, "Hi\n") 
    }) 
    http.HandleFunc("/quit", func(w http.ResponseWriter, r *http.Request) { 
     io.WriteString(w, "Bye\n") 
     cancel() 
    }) 
    srv := &http.Server{Addr: ":8080"} 
    go func() { 
     if err := srv.ListenAndServe(); err != nil { 
      log.Printf("Httpserver: ListenAndServe() error: %s", err) 
     } 
    }() 
    <-ctx.Done() 
    if err := srv.Shutdown(ctx); err != nil && err != context.Canceled { 
     log.Println(err) 
    } 
    log.Println("done.") 
} 

をさらに参照:

ctx, cancel := context.WithCancel(context.Background()) 
err := srv.Shutdown(ctx); 

これを試してみてくださいhttps://blog.golang.org/context

+1

完全に動作します。 – James

1

Shutdown関数はパラメータctx context.Contextを受け入れます。それに空の文脈を渡してみてください。また

ctx := context.Background() 

When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return ErrServerClosed. Make sure the program doesn't exit and waits instead for Shutdown to return.

関連する問題