Go Web 编程快速入门 01 - 环境准备与第一个 Web 应用

这第一篇不求复杂,只要跑起来。你能在本机启动一个小小的 HTTP 服务,返回一段文本或 JSON,就算开了个好头。下面的内容重点是环境准备与第一个 Web 应用的编码与运行,过程尽量顺畅,代码有注释,读完即可上手。

1 开始之前

用 Go 写 Web 服务,有两个要点:环境干净,项目结构简单。其它都可以后面慢慢加。

1.1 安装与验证 Go

  • 安装:前往 https://go.dev/dl/ 下载并安装 Windows 版 Go(建议 1.20+)。
  • 验证:打开 PowerShell,运行:
powershell 复制代码
go version
go env GOPATH GOMODCACHE GOBIN

如果你在国内网络环境,为减少下载阻塞,可以设置代理:

powershell 复制代码
go env -w GOPROXY=https://proxy.golang.com.cn,direct

说明:go version 输出版本;go env 看关键变量;GOPROXY 加速依赖下载。

1.2 推荐工具与目录结构

  • IDE:VS Code + Go 扩展(自动安装 gopls,支持跳转、补全、格式化)。
  • 目录建议:一个模块一个文件夹,简洁到能看一眼就明白。

示例结构(本文代码用到):

复制代码
webapp-01/
  go.mod
  main.go

2 创建项目与模块

Go 用模块来管理依赖与构建。初始化一次,后面就省心了。

2.1 初始化 go.mod

在你存放示例代码的位置(如 C:\Users\Administrator\Desktop\webapp-01),打开 PowerShell:

powershell 复制代码
New-Item -ItemType Directory webapp-01 | Out-Null
Set-Location webapp-01
go mod init example.com/webapp-01

说明:go mod init 的模块名可以用你的域名或占位名(示例里用 example.com/...)。

3 第一个 Web 应用

我们先用最直接的方式启动一个 HTTP 服务,提供两个接口:/hello/healthz

3.1 代码:简洁的 HTTP 服务(main.go)

go 复制代码
// 文件:main.go
// 目标:启动一个最简 HTTP 服务,具备基本路由与日志
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "time"
)

// helloHandler 返回问候语,支持通过查询参数自定义名称:/hello?name=Go
func helloHandler(w http.ResponseWriter, r *http.Request) {
    start := time.Now()

    // 读取查询参数 name,默认值为 "World"
    name := r.URL.Query().Get("name")
    if name == "" {
        name = "World"
    }

    // 设置响应头(内容类型为纯文本,也可改为 application/json)
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")

    // 写入响应内容
    _, _ = fmt.Fprintf(w, "Hello, %s!\n", name)

    // 简单的请求日志(方法、路径、耗时)
    log.Printf("%s %s -> %s", r.Method, r.URL.Path, time.Since(start))
}

// healthHandler 返回健康检查结果,常用于探活
func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    _, _ = w.Write([]byte(`{"status":"ok"}`))
}

func main() {
    // 端口可通过环境变量 PORT 设置,默认 8080
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    mux := http.NewServeMux()
    mux.HandleFunc("/hello", helloHandler)
    mux.HandleFunc("/healthz", healthHandler)

    addr := ":" + port
    log.Printf("starting server at http://localhost%s", addr)

    // 直接用默认的 ListenAndServe 即可跑起来
    if err := http.ListenAndServe(addr, mux); err != nil {
        log.Fatalf("server error: %v", err)
    }
}

运行与测试:

powershell 复制代码
# 编译并运行(任选其一)
go run .
# 或
go build -o webapp.exe; ./webapp.exe

# 发起请求(PowerShell)
Invoke-WebRequest -Uri "http://localhost:8080/hello?name=Gopher" -UseBasicParsing
Invoke-WebRequest -Uri "http://localhost:8080/healthz" -UseBasicParsing

# 或者用 curl(如已安装)
curl "http://localhost:8080/hello?name=Gopher"
curl "http://localhost:8080/healthz"

4 更稳一点的服务骨架

生产场景会需要:超时、优雅退出。加一点结构,让服务更"抗造"。

4.1 http.Server + 超时 + 优雅退出

go 复制代码
// 文件:main.go(增强版骨架)
package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        name := r.URL.Query().Get("name")
        if name == "" { name = "World" }
        w.Header().Set("Content-Type", "text/plain; charset=utf-8")
        _, _ = w.Write([]byte("Hello, " + name + "!\n"))
    })
    mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        _, _ = w.Write([]byte(`{"status":"ok"}`))
    })

    port := os.Getenv("PORT")
    if port == "" { port = "8080" }

    srv := &http.Server{
        Addr:              ":" + port,
        Handler:           mux,
        ReadTimeout:       5 * time.Second,  // 读取请求的超时
        WriteTimeout:      10 * time.Second, // 写响应的超时
        IdleTimeout:       60 * time.Second, // keep-alive 的空闲超时
        ReadHeaderTimeout: 2 * time.Second,  // 读请求头的超时
    }

    // 启动服务(协程中运行)
    go func() {
        log.Printf("server listening at http://localhost:%s", port)
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("listen error: %v", err)
        }
    }()

    // 捕获中断信号,执行优雅退出
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    log.Println("shutting down...")

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Printf("shutdown error: %v", err)
    }
    log.Println("server stopped")
}

说明:http.Server 提供更细的超时控制;Shutdown 会等待正在处理的请求结束,避免直接"掐断"。

5 常见问题与排查

5.1 端口占用

  • 报错 address already in use:换个端口(设置 PORT=8081),或关闭占用进程。

5.2 依赖下载慢

  • 设置代理:go env -w GOPROXY=https://proxy.golang.com.cn,direct
  • 重试:go clean -modcache 后重新 go mod tidy

5.3 请求卡住或报超时

  • 本文的增强版设置了超时,确认是否被触发;如果是内网请求,检查防火墙或代理。

6 小结与下一步

你已经完成了两步:装好 Go、跑起第一个 Web 服务。别急着加太多功能,先把基本的路由、响应、日志跑顺手。

下一篇将进入主题:net/httpHandler 接口的工作方式,为什么一个函数就能变成处理器,以及怎么组织路由更清晰。

相关推荐
卓码软件测评3 小时前
第三方CMA软件测试机构:页面JavaScript动态渲染生成内容对网站SEO的影响
开发语言·前端·javascript·ecmascript
Mintopia3 小时前
📚 Next.js 分页 & 模糊搜索:在无限数据海里优雅地翻页
前端·javascript·全栈
Mintopia3 小时前
⚖️ AIGC版权确权技术:Web内容的AI生成标识与法律适配
前端·javascript·aigc
*才华有限公司*3 小时前
《爬虫进阶之路:从模拟浏览器到破解动态加载的实战指南》
开发语言·python
周家大小姐.4 小时前
vue实现模拟deepseekAI功能
前端·javascript·vue.js
敲代码的嘎仔4 小时前
JavaWeb零基础学习Day4——Maven
java·开发语言·学习·算法·maven·javaweb·学习方法
小张成长计划..4 小时前
前端7:综合案例--品优购项目(HTML+CSS)
前端·css·html
W.Buffer4 小时前
设计模式-工厂模式:解耦对象创建的设计艺术
java·开发语言·设计模式
梵得儿SHI4 小时前
Java 注解与反射实战:自定义注解从入门到精通
java·开发语言·注解·自定义注解·元注解·控制注解的作用·声明式编程思想