OpenCode 实践课:5 分钟用 Go 写出一个命令行 URL 短链接服务

项目介绍

今天我们用一个完整的实战案例,展示如何用自然语言指挥 OpenCode 在 5 分钟内写出一款 URL 短链接服务(URL Shortener)。

这个服务用 Go 语言编写,仅依赖标准库,代码约 90 行。它可以将任意长链接压缩为 6 位短码,访问短链接自动 302 跳转到原始地址,还提供了列表接口用于查看所有已生成的短链。

技术栈:Go 1.21+ 标准库(net/http + crypto/md5 + sync)。

准备工作

确保本地已安装 Go(go version 确认版本 ≥ 1.21),然后打开终端进入工作目录:

mkdir urlshortener && cd urlshortener

打开 OpenCode,直接告诉它你要做什么——不需要手写一行代码。

实践过程

第一步:初始化项目骨架

在 OpenCode 对话框中输入我们的第一条指令。不需要说明具体目录结构,只说意图就够了。

> 我对 OpenCode 说:
> 用 Go 初始化一个 HTTP 服务项目,监听 8080 端口,使用标准库的 http.NewServeMux

OpenCode 立刻理解了我的意图,自动完成 go mod init 并生成了 main.go 主体框架:

package main

import (
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    addr := ":8080"
    log.Printf("URL Shortener running on %s\n", "http://localhost"+addr)
    log.Fatal(http.ListenAndServe(addr, mux))
}

关键点:你不需要事先想好完整的框架——OpenCode 会根据上下文推断后续步骤所需的基础设施,并且会选用符合你语言习惯的写法(比如这里用的是 http.NewServeMux 而非默认多路复用器)。

第二步:实现短链接生成逻辑

有了空框架,接下来添加核心功能。同样只需要一句话描述需求:

> 我对 OpenCode 说:
> 添加 POST /shorten 接口,接收 JSON {"url": "..."},用 MD5 生成 6 位短码,存入 map 并返回短链接

OpenCode 不仅实现了功能,还自动加上了互斥锁sync.RWMutex)来保证 map 的并发安全——这可是新手容易忽略的细节:

var (
    store   = make(map[string]string)
    mu      sync.RWMutex
    baseURL = "http://localhost:8080"
)

func handleShorten(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    var body struct{ URL string `json:"url"` }
    if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.URL == "" {
        http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
        return
    }
    code := generateCode(body.URL)
    mu.Lock()
    store[code] = body.URL
    mu.Unlock()
    resp := map[string]string{
        "short_url": fmt.Sprintf("%s/s/%s", baseURL, code),
        "code":      code,
    }
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(resp)
}

值得注意:OpenCode 自动对空 URL 做了校验,方法是检查 body.URL == "",而非仅检查 err != nil——后者无法覆盖 JSON 中字段为空字符串的情况。

第三步:添加重定向和列表接口

到这里我们只差两部分功能:访问短链接时跳转,以及查看所有已生成的短链。

> 我对 OpenCode 说:
> 添加两个接口:GET /s/{code} 做 302 重定向到原始 URL,GET /list 返回所有短链列表

OpenCode 一次性生成了两个完整的 Handler:

func handleRedirect(w http.ResponseWriter, r *http.Request) {
    code := strings.TrimPrefix(r.URL.Path, "/s/")
    mu.RLock()
    url, ok := store[code]
    mu.RUnlock()
    if !ok {
        http.NotFound(w, r)
        return
    }
    http.Redirect(w, r, url, http.StatusFound)
}

func handleList(w http.ResponseWriter, r *http.Request) {
    mu.RLock()
    entries := make([]map[string]string, 0, len(store))
    for code, url := range store {
        entries = append(entries, map[string]string{
            "code": code, "short_url": fmt.Sprintf("%s/s/%s", baseURL, code), "url": url,
        })
    }
    mu.RUnlock()
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]interface{}{
        "count": len(entries), "items": entries,
    })
}

整个过程我只下达了 3 条自然语言指令,OpenCode 就完成了一个可以编译运行、具备生产级并发安全性的短链接服务。

第四步:测试验证

最后一条指令是让 OpenCode 帮我们写测试脚本:

> 我对 OpenCode 说:
> 帮我写一个 bash 一行命令来测试 POST /shorten,然后验证 GET /s/{code} 的重定向

OpenCode 给出了可直接执行的 curl 命令序列:

# 生成短链接
curl -s -X POST http://localhost:8080/shorten \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.linweiqin.com"}'

# 查看列表
curl -s http://localhost:8080/list | python -m json.tool

# 测试重定向(-L 跟随跳转)
curl -s -L -o /dev/null -w "%{http_code}" http://localhost:8080/s/06a5fe

跑一遍,三个接口全部正常响应。

完整代码

以下是经 OpenCode 最终生成的完整 main.go(不含注释,共 92 行):

package main

import (
    "crypto/md5"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strings"
    "sync"
    "time"
)

var (
    store   = make(map[string]string)
    mu      sync.RWMutex
    baseURL = "http://localhost:8080"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/shorten", handleShorten)
    mux.HandleFunc("/s/", handleRedirect)
    mux.HandleFunc("/list", handleList)

    log.Printf("URL Shortener running on %s\n", baseURL)
    log.Fatal(http.ListenAndServe(":8080", mux))
}

func handleShorten(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    var body struct{ URL string `json:"url"` }
    if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.URL == "" {
        http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
        return
    }
    code := generateCode(body.URL)
    mu.Lock()
    store[code] = body.URL
    mu.Unlock()

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{
        "short_url": fmt.Sprintf("%s/s/%s", baseURL, code),
        "code":      code,
    })
}

func handleRedirect(w http.ResponseWriter, r *http.Request) {
    code := strings.TrimPrefix(r.URL.Path, "/s/")
    mu.RLock()
    url, ok := store[code]
    mu.RUnlock()
    if !ok {
        http.NotFound(w, r)
        return
    }
    http.Redirect(w, r, url, http.StatusFound)
}

func handleList(w http.ResponseWriter, r *http.Request) {
    mu.RLock()
    entries := make([]map[string]string, 0, len(store))
    for code, url := range store {
        entries = append(entries, map[string]string{
            "code":      code,
            "short_url": fmt.Sprintf("%s/s/%s", baseURL, code),
            "url":       url,
        })
    }
    mu.RUnlock()

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]interface{}{
        "count": len(entries),
        "items": entries,
    })
}

func generateCode(url string) string {
    raw := fmt.Sprintf("%s%d", url, time.Now().UnixNano())
    hash := md5.Sum([]byte(raw))
    return fmt.Sprintf("%x", hash)[:6]
}

小结

用 OpenCode 开发这个短链接服务的实际耗时不到 5 分钟,期间我总共只输入了 4 条自然语言指令。对比传统开发方式——手写框架、查标准库文档、处理并发安全、写 JSON 序列化——至少要 20-30 分钟。

OpenCode 带来的效率提升不仅体现在速度上,更重要的三点:

自动补齐工程细节sync.RWMutex 保护 map、请求方法校验、空值边界处理,这些新手容易遗漏的点 OpenCode 都帮你做了。

即时验证闭环:写好代码后可以直接让 OpenCode 生成测试命令,在一个对话窗口里完成"写→测→改"的全流程。

标准库优先:OpenCode 倾向于使用标准库而非拉取第三方依赖,生成的项目开箱即用,没有依赖地狱。

如果你还没试过用自然语言指挥 AI 写代码,不妨从这个小项目开始——打开 OpenCode,输入第一句话,看看它能帮你省下多少时间。