# 中介層

本頁說明如何把 go-ip-sentry 接到 Gin 或 `net/http`、如何排除特定路徑，以及不經中介層直接呼叫 `Check` 的寫法。

## 三種接法

| 方式 | 簽章 | 失敗時 |
|---|---|---|
| `HTTPMiddleware` | `func (i *IPGuardian) HTTPMiddleware(next http.Handler) http.Handler` | 設定 `Content-Type: application/json`、寫入狀態碼與 `{"error": "..."}`，不呼叫 `next` |
| `GinMiddleware` | `func (i *IPGuardian) GinMiddleware() gin.HandlerFunc` | `c.JSON(status, gin.H{"error": ...})` 後 `c.Abort()` |
| `Check` | `func (i *IPGuardian) Check(r *http.Request, w http.ResponseWriter) IPGuardianResult` | 由呼叫端決定 |

三者在通過時都已寫入 Session 與裝置 Cookie，必須在 handler 寫出 body 之前執行。

## net/http

```go
mux := http.NewServeMux()
mux.HandleFunc("/api/orders", ordersHandler)

log.Fatal(http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", sentry.HTTPMiddleware(mux)))
```

## Gin

```go
r := gin.New()
r.Use(gin.Recovery())

api := r.Group("/api")
api.Use(sentry.GinMiddleware())
api.GET("/orders", listOrders)

if err := r.RunTLS(":8443", "cert.pem", "key.pem"); err != nil {
	log.Fatal(err)
}
```

`gin.Recovery()` 建議保留：Redis 在評分階段失敗時 `Check` 會 panic（見 [已知問題](/zh/known-issues)），Recovery 會把它轉成 `500`。

## 排除路徑

健康檢查、指標收集與固定週期輪詢會累積規律間隔與速率計數，應排除在外：

```go
func protect(sentry *golangIPSentry.IPGuardian, next http.Handler) http.Handler {
	guarded := sentry.HTTPMiddleware(next)
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/healthz", "/metrics":
			next.ServeHTTP(w, r)
		default:
			guarded.ServeHTTP(w, r)
		}
	})
}
```

## 直接呼叫 Check

需要自訂回應格式（例如 HTML 錯誤頁）時：

```go
func handler(sentry *golangIPSentry.IPGuardian) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		result := sentry.Check(r, w)
		if !result.Success {
			http.Error(w, http.StatusText(result.StatusCode), result.StatusCode)
			return
		}
		w.Write([]byte("OK"))
	}
}
```

`IPGuardianResult.Error` 包含用戶端 IP，直接回給用戶端前先確認是否符合隱私需求。
