返回响应后关闭 HTTP 服务器

2023-12-06

我正在构建一个基于命令行的 Go 机器人,它与 Instagram API 交互。

Instagram API 基于 OAuth,因此不太适合基于命令行的应用程序。

为了解决这个问题,我在浏览器中打开适当的授权 URL,并使用我为重定向 URI 启动的本地服务器 - 这样我就可以捕获并优雅地显示访问令牌,而不是用户需要从手动输入网址。

到目前为止一切顺利,应用程序可以成功打开浏览器访问授权 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() 错误:http:服务器关闭

2017/11/23 16:02:03 http: 恐慌服务 [::1]:61793: 运行时错误: 无效的内存地址或 nil 指针取消引用

如果我在处理程序中注释掉这些行,那么它可以完美地工作,尽管当我点击回调路由时不会关闭服务器:

if err := srv.Shutdown(nil); err != nil {
    log.Fatal(err) // failure/timeout shutting down the server gracefully
}

我哪里出错了?我需要更改什么,以便在向用户显示文本后,当我点击回调路由时可以关闭服务器。


  1. 您可以使用context.WithCancel:
package main

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

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    http.HandleFunc("/quit", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Bye\n")
        cancel()
    })
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Hi\n")
    })

    srv := &http.Server{Addr: ":8080"}
    go func() {
        err := srv.ListenAndServe()
        if err != http.ErrServerClosed {
            log.Println(err)
        }
    }()

    <-ctx.Done() // wait for the signal to gracefully shutdown the server

    // gracefully shutdown the server:
    // waiting indefinitely for connections to return to idle and then shut down.
    err := srv.Shutdown(context.Background())
    if err != nil {
        log.Println(err)
    }

    log.Println("done.")
}

  1. 相同的上下文可以传递给在不同 goroutine 中运行的函数:

“上下文对于多个 goroutine 同时使用是安全的。”

You may 使用相同的context- 如果您不想额外等待:

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.")
}

服务器关闭:
关闭会正常关闭服务器,而不会中断任何活动连接。关闭的工作原理是首先关闭所有打开的侦听器,然后关闭所有空闲连接,然后等待无限期地让连接返回空闲状态然后关闭。如果提供的上下文在关闭完成之前过期,则关闭将返回上下文的错误,否则它将返回关闭服务器的底层侦听器时返回的任何错误。

当调用 Shutdown 时,Serve、ListenAndServe 和 ListenAndServeTLS 立即返回 ErrServerClosed。确保程序不会退出,而是等待关机返回。

Shutdown 不会尝试关闭或等待被劫持的连接,例如 WebSocket。如果需要,Shutdown 的调用者应单独通知此类长期连接关闭并等待它们关闭。有关注册关闭通知函数的方法,请参阅 RegisterOnShutdown。

一旦在服务器上调用 Shutdown,就不能重复使用;以后调用 Serve 等方法将返回 ErrServerClosed。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

返回响应后关闭 HTTP 服务器 的相关文章

  • Golang:使用像 Node.js 中那样的可读流从 PostgreSQL 数据库中选择几百万行

    我有大约 5000 万行的 PostgreSQL 表 我想编写 Go 代码来从该表中选择大约 100 万行 并以有效的方式处理它们 上次我使用了nodejs和这个NPM模块pg 查询流 https www npmjs com package
  • 限制 FormFile 中的文件大小

    我让用户使用 FormFile 上传文件 我应该在什么时候检查文件大小是否太大 当我做 file header fileErr r FormFile file 文件对象已经创建 那么我是否已经产生了读取整个文件的成本 https golan
  • 如何使用 go web 服务器提供静态 html 文件?

    如何使用 go web 服务器提供 index html 或其他静态 HTML 文件 我只想要一个基本的静态 HTML 文件 例如一篇文章 我可以从 Go Web 服务器提供该文件 HTML 应该可以在 go 程序之外进行修改 就像使用 H
  • golang导入结构体指针

    好的 我有一个主包和一个 http 处理程序包 本质上我想做的是设置一个全局结构 这样我就可以随时调用该结构中的信息 我尝试的示例的基本概要如下 主包导入处理函数 主包调用handlerfunc Handlerfunc 将 http Res
  • Bash脚本无法执行Go命令

    我正在尝试编写一个 bash 脚本来自动在不同的目录中运行 go get install 相关部分在这里 cd web go get cd web go install cd services go get cd services go i
  • Go API 在 html 中显示 swagger api 规范 (json) (Swagger UI)

    我有一个服务于特定端口的应用程序 gorilla mux 我也有一个 json 文件形式的 swagger API 规范 是否有任何 go API 可以像 spring boot 一样从 JSON 文件生成 swagger UI 定义 我正
  • Golang - 更改 Windows 上的构建工作路径

    我正在使用 SublimeText3 GoSublime 插件 在 Windows 8 上测试简单的 Go 程序 go run v example go 在运行之前它正在内部编译 应用程序数据 本地 温度 目录 我的防病毒程序认为这是病毒并
  • 如何在 Goji (Golang) 中使用不同的中间件创建单独的路由组?

    我正在使用Goji https github com zenazn goji https github com zenazn goji 并希望定义具有自己的中间件的路由组 例如 下面的所有路径 company应使用 LDAP 身份验证并定义
  • 给定方法值,获取接收者对象

    Go 有没有办法从方法值获取接收者对象 例如有没有这样的MagicFunc这将使以下程序输出字符串my info来自底层 Foo 实例 package main import fmt type Foo struct A string fun
  • Go 中的 WebP 编码器/解码器

    是否有一个完整的 WebP 编码器和解码器与当前每周 或可分叉 兼容 它的速度与标准 png 相当吗 这个人在 GitHub 上有一个包 其中包含 WebP 的编码器和解码器 https github com chai2010 webp h
  • 我们如何在 Golang 中组合多个错误字符串?

    我是 golang 新手 我的应用程序需要在循环中返回多个错误 稍后需要组合并作为单个错误字符串返回 我无法使用字符串函数来组合错误消息 在返回之前可以使用什么方法将这些错误合并为一个错误 package main import fmt s
  • nsq 无法通过连接到 nsqlookupd 来消费消息

    我尝试使用 docker compose 来运行 nsq docker compose yml如下 version 3 services nsqlookupd image nsqio nsq command nsqlookupd ports
  • 在 IntelliJ IDEA 中运行。多个文件和错误未定义:数据

    我想使用 IntelliJ IDE 社区版编写代码GO Go语言 我安装了正确的插件 并安装了构建应用程序所需的所有工具 我的应用程序包含以下两个文件 每个都在目录中 事件服务器 Main go Data go 如果我想使用 Run Ctl
  • 在处理程序之后访问 HTTP 请求上下文

    在我的日志记录中间件 链中的第一个 中 我需要访问一些在链下游的某些身份验证中间件中编写的上下文 并且仅在处理程序本身执行之后 旁注 需要首先调用日志记录中间件 因为我需要记录请求的持续时间 包括在中间件中花费的时间 此外 当权限不足时 身
  • 在golang中获取TTFB(第一个字节的时间)值

    我正在尝试获取 TTFB 值和 Connect 值 c exec Command curl w Connect time connect TTFB time starttransfer Total time time total o dev
  • Gorm 总是返回带有 nil 值的结构

    我正在使用 Gorm 构建 Go Web API 作为 Amazon RDS 中 Postgresql 数据库的 ORM 问题是 Gorm 总是返回一片结构 其值全部为零 尽管数据库已经填充了数据 切片中的结构体数量是否合适取决于LIMIT
  • 使用 OpenTelemetry 统一不同服务的范围

    我刚刚开始使用 OpenTelemetry 并为此创建了两个 微 服务 Standard and GeoMap 最终用户将请求发送到Standard服务 该服务又将请求发送到GeoMap在将结果返回给最终用户之前获取信息 我使用 gRPC
  • Go 的范围不能超过 (类型接口 {})

    我正处于尝试将我的注意力集中在 Go 上的婴儿阶段 目前 我正在模拟一个 API 请求 该请求返回包含对象数组的 JSON 格式的字符串 我试图找出迭代每个记录并访问每个字段的最合适的方法 最终 每个字段都将写入 Excel 电子表格 但现
  • 重新插入通道导致死锁

    我有稳定的入站 作业 流 将其输入到无缓冲通道中 我有一个for range循环来迭代项目并处理它们 如果处理该项目失败 我会将项目重新插入通道中 以便稍后重试 问题是当我将项目重新插入通道时 它陷入僵局 我明白为什么会发生这种情况 处理器
  • 编写每个处理程序中间件

    我希望从处理程序中提取一些重复的逻辑 并将其放入一些每个处理程序的中间件中 特别是 CSRF 检查 检查现有会话值 即身份验证或预览页面 等 我读了关于此的几篇文章 http justinas org writing http middle

随机推荐

  • 如何在 React/Redux 中取消挂起的异步操作

    考虑以下情况 当用户导航到页面时 会调度两个异步 Redux 操作来并行获取两组相关数据 如果这些提取中的任何一个失败 组件都会检测到它 该组件将在下一个周期呈现错误组件 进而调度clearState安装时的动作 然而 其他操作仍有待解决
  • 检查 Java TCP 服务器上的客户端断开连接 - 仅输出

    我有一个 Java TCP 服务器 当客户端连接到它时 它每 30 秒向客户端输出一条消息 严格要求客户端不向服务器发送任何消息 并且服务器不向客户端发送除30秒间隔消息之外的任何数据 当我断开客户端连接时 服务器直到下次尝试写入客户端时才
  • 区分大小写的 URL

    我推出了一个小网站 我发现用户遇到错误 经过调查发现他们尝试使用的网址都是小写的 而我已将它们声明为驼峰式 我不知道为什么这些用户应该尝试使用所有小写字母 我无法想象有人会真正花时间进行更改 www mysite com myAction
  • '' aria-label='Dataframe 上的 Pandas 条件返回 TypeError: 'str' 和 'int' 实例之间不支持 '>''> Dataframe 上的 Pandas 条件返回 TypeError: 'str' 和 'int' 实例之间不支持 '>'

    我正在使用 pandas 处理 DataFrame 我需要根据某些条件添加一个新列 我的数据框是 discount tax total subtotal productid 3 0 20 13 002 10 3 106 94 003 46
  • 将 .NET 刻度转换为 SQL Server DateTime

    我正在保存一个TimeSpan 来自 NET 我的数据库中的值为BIGINT在 SQL Server 中 保存 Ticks 属性 我想知道如何转换这个BIGINT值对一个DATETIMESQL Server 中的值 不是 NET 中的值 有
  • 空手道框架的 JSON 报告

    如何在使用空手道框架时生成 JSON 报告 以便我可以使用黄瓜报告插件在 JENKINS 中使用它 我的空手道版本是 空手道 apache 0 8 0 空手道 junit4 0 8 0 你真的应该阅读文档 https github com
  • 使用 if() 在 dplyr 管道链中使用 select()

    已经阅读了这两篇文章 dplyr 包可以用于条件变异吗 R 使用管道运算符时的条件评估 gt 我正在使用 Shiny input selector 如果用户选择了特定值 我希望我的数据框与其他值不同 这是一条链 filtered funne
  • 显示消息等待...当后台处理发生时

    我想将消息显示为 请稍候 直到我的 java 代码完成一些处理 page1 jsp 我的表单 其中有文本框和提交按钮 当单击提交按钮时 我正在执行表单提交并调用 page2 jsp 在页面 2 jsp 中 我从页面 1 jsp 请求参数并传
  • 如何使用 Java 发出 multipart/form-data POST 请求?

    在 Apache Commons HttpClient 版本 3 x 时代 可以进行 multipart form data POST 请求 2004年的例子 不幸的是 这在以下情况下不再可能HttpClient 4 0 版本 对于我们的核
  • 列出 R 向量中的不同值

    如何列出向量中具有重复值的不同值 我的意思是 类似于下面的 SQL 语句 SELECT DISTINCT product code FROM data 你的意思是unique R gt x c 1 1 2 3 4 4 4 R gt x 1
  • Gulp.js 任务,返回 src?

    我是 gulp 的新手 一直在研究示例设置 有些人有以下结构 gulp task XXXX function gulp src 其他人有这个 gulp task XXXX function return gulp src 我想知道有什么区别
  • 使用 getImageData、javascript、HTML5 canvas 会导致内存泄漏吗

    我正在使用 canvas 元素 并尝试在 FIrefox 4 中使用 Javascript 对图像进行一些基于像素的操作 以下代码泄漏内存 我想知道是否有人可以帮助识别泄漏的内容 使用的图像是预加载的 一旦加载 到 pImages 数组中
  • 如何使用 spring-data-couchbase 为特定 Couchbase 文档设置 TTL?

    如何使用 spring data couchbase 为特定的 couchbase 文档设置 TTL 生存时间 我知道有一种方法可以使用文档符号设置到期时间 如下所示 Document 过期 10 http docs spring io s
  • IIS FTP 7.5 扩展性(IFtpLogProvider 并将 FTP 故障记录到事件日志中)

    任何非常熟悉 IIS 中 FTP 7 5 可扩展性的人都知道我可能做错了什么吗 我在使 IFtpLogProvider 的实现正常工作以进行自定义日志记录时遇到严重困难 我想做的就是将超出静态阈值的故障记录到事件日志中 并经常进行垃圾收集
  • Python:tarfile 流

    我想从 tarball 中读取一些文件并将其保存到新的 tarball 中 这是我写的代码 archive dum 2164 archive tar Read input data input tar tarfile open archiv
  • ASP.NET:将数据从内容页传递到母版页[重复]

    这个问题在这里已经有答案了 我有一个名为headerLabel在我的母版页中 我想将其文本设置为内容页面中的标题 我该怎么做呢 在您的主页上创建一个公共属性 类似于 public string LabelValue get return t
  • 如何在openGL中纹理随机凸四边形

    好吧 为了制作一个 我的世界 模组 我开始查找 openGL 教程 我对此仍然不太了解 因为我认为在进行我想要的小修改时我真的不应该这样做 但这让我很头疼 我想要做的就是能够将纹理正确映射到不规则的凹四边形 像这样 在尝试在游戏中运行代码之
  • chrome.filesystem 保存文件而不提示位置

    我可以将文件保存在自定义位置 home Users user1 带名字file1 txt 我有这个代码 chrome fileSystem chooseEntry type openDirectory function entry chro
  • 在 VB.net 中打印外部 PDF 文档

    我知道这个问题以前曾被问过 但我的情况有点不稳定 基本上 我正在尝试打印使用以前的 Windows 窗体生成的 PDF 文件 我可以毫无问题地找到该文件 并且我使用了从 MSDN 帮助论坛找到的以下代码 Dim p As New Syste
  • 返回响应后关闭 HTTP 服务器

    我正在构建一个基于命令行的 Go 机器人 它与 Instagram API 交互 Instagram API 基于 OAuth 因此不太适合基于命令行的应用程序 为了解决这个问题 我在浏览器中打开适当的授权 URL 并使用我为重定向 URI