unknwon/the-way-to-go_ZH_CN · error

err.Error()

Error message

err.Error()

What it means

In the URL shortener (19.8), the Redirect handler calls store.Get(&key, &url) and on error replies http.Error(w, err.Error(), 500). After the RPC-backed URLStore refactor, Get returns an error when the key is unknown or when the store/RPC layer fails to fetch it — and the raw internal error goes to the visitor.

Source

Thrown at eBook/19.8.md:86

		return errors.New("key already exists")
	}
	s.urls[*key] = *url
	return nil
}
```

同样,当从 `load()` 调用 `Set()` 时,也必须做调整:
```go
s.Set(&r.Key, &r.URL)
```

还必须修改 HTTP 处理函数以适应 `URLStore` 上的更改。`Redirect()` 处理函数现在返回 `URLStore` 给出错误的字符串形式:
```go
func Redirect(w http.ResponseWriter, r *http.Request) {
	key := r.URL.Path[1:]
	var url string
	if err := store.Get(&key, &url); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, url, http.StatusFound)
}
```

`Add()` 处理函数也以基本相同的方式修改:

```go
func Add(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")
	if url == "" {
		fmt.Fprint(w, AddForm)
		return
	}
	var key string
	if err := store.Put(&url, &key); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)

View on GitHub (pinned to 7a54d34d36)

Solutions

  1. Distinguish 'key not found' from real failures — return http.NotFound for unknown keys instead of a 500 with internal text
  2. Keep the data-file path stable (make it a flag) so keys survive restarts
  3. If -rpc is enabled, verify the RPC server's hostname/port are reachable and the store is registered
  4. Log the error server-side; serve a friendly 'unknown short link' page to users

Example fix

// before
if err := store.Get(&key, &url); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

// after
if err := store.Get(&key, &url); err != nil {
    log.Println("get failed for key", key, ":", err)
    if err == ErrUnknownKey {
        http.NotFound(w, r)
    } else {
        http.Error(w, "shortener unavailable", http.StatusInternalServerError)
    }
    return
}
Defensive patterns

Strategy: fallback

Validate before calling

// cheap format check before hitting the store: generated keys are short and alphanumeric
var keyRe = regexp.MustCompile("^[a-zA-Z0-9]{1,10}$")

key := r.URL.Path[1:]
if !keyRe.MatchString(key) {
    http.NotFound(w, r) // clearly not one of our keys — 404, not 500
    return
}

Try / catch

// degrade gracefully when the store can't answer
if err := store.Get(&key, &url); err != nil {
    log.Println("store.Get:", err)
    if errors.Is(err, ErrUnknownKey) {
        http.NotFound(w, r)
    } else {
        http.Redirect(w, r, "/", http.StatusFound) // fall back to home page
    }
    return
}

Prevention

When it happens

Trigger: Requesting a path whose key was never stored (or whose mapping vanished after a restart because the gob data file did not persist it); the RPC client unable to reach the RPC server instance; a gob decode error on a corrupt store entry.

Common situations: Short links from an earlier run after the data file was reset or pointed elsewhere; starting an instance without -rpc while expecting a shared store; racing Put/Get against the persisted map on shutdown.

Related errors


AI-assisted analysis of unknwon/the-way-to-go_ZH_CN@7a54d34d36 (2026-08-15). Data as JSON: /api/errors/e2661af0a6c2ccd4. Report an issue: GitHub.