我正在构建一个基于Go bot的小命令行,它与Instagram API交互.
Instagram API是基于OAuth的,因此对于基于命令行的应用程序来说并不是太好.
为了解决这个问题,我在浏览器中打开了相应的授权URL,并使用本地服务器启动了重定向URI - 这样我可以捕获并优雅地显示访问令牌,而不是需要从中获取此权限的用户URL手动.
到目前为止,应用程序可以成功打开浏览器到授权URL,您授权它并将它重定向到本地HTTP服务器.
现在,在向用户显示访问令牌之后我不需要HTTP服务器,因此我想在执行此操作后手动关闭服务器.
为此,我从这个答案中汲取灵感并鼓励以下内容:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
但是,上面会导致此错误:
2017/11/23 16:02:03 Httpserver:ListenAndServe()错误:http:服务器关闭
2017/11/23 16:02:03 http:panic serving [:: 1]:61793:运行时错误:无效的内存地址或nil指针取消引用
如果我在处理程序中注释掉这些行,那么它可以完美地工作,虽然在我点击回调路径时没有关闭服务器:
if err := srv.Shutdown(nil); err != nil {
log.Fatal(err) // failure/timeout shutting down the server gracefully
}
Run Code Online (Sandbox Code Playgroud)
我哪里错了?我需要更改什么才能在向用户显示文本后点击回调路由时关闭服务器.
使用:
ctx, cancel := context.WithCancel(context.Background())
err := srv.Shutdown(ctx);
Run Code Online (Sandbox Code Playgroud)
试试这个:
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.")
}
Run Code Online (Sandbox Code Playgroud)
请参阅:https://blog.golang.org/context