[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# Context in HTTP Servers & Service Calls
|
||||
|
||||
## Context in HTTP Servers
|
||||
|
||||
`http.Request` carries a context that is cancelled when the client disconnects or the request handler returns. MUST use `r.Context()` — NEVER create a new `context.Background()` inside a handler.
|
||||
|
||||
```go
|
||||
func (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context() // this context is cancelled if the client disconnects
|
||||
|
||||
order, err := h.orderService.Get(ctx, r.PathValue("id"))
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
// Client disconnected, no point writing a response
|
||||
return
|
||||
}
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(order)
|
||||
}
|
||||
```
|
||||
|
||||
## Middleware enriching context
|
||||
|
||||
Middleware injects request-scoped values before handlers run. Use unexported key types to prevent collisions:
|
||||
|
||||
```go
|
||||
// Helpers for trace propagation
|
||||
type contextKey string
|
||||
const (
|
||||
traceIDKey contextKey = "trace_id"
|
||||
spanIDKey contextKey = "span_id"
|
||||
)
|
||||
|
||||
func TracingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
traceID := r.Header.Get("X-Trace-ID")
|
||||
if traceID == "" {
|
||||
traceID = generateTraceID()
|
||||
}
|
||||
spanID := r.Header.Get("X-Span-ID")
|
||||
if spanID == "" {
|
||||
spanID = generateSpanID()
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), traceIDKey, traceID)
|
||||
ctx = context.WithValue(ctx, spanIDKey, spanID)
|
||||
|
||||
w.Header().Set("X-Trace-ID", traceID)
|
||||
w.Header().Set("X-Span-ID", spanID)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
// Propagate trace context to downstream services
|
||||
func (c *HTTPClient) Do(ctx context.Context, method, url string, body io.Reader) (*http.Response, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, method, url, body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating request: %w", err)
|
||||
}
|
||||
|
||||
if traceID, ok := ctx.Value(traceIDKey).(string); ok {
|
||||
req.Header.Set("X-Trace-ID", traceID)
|
||||
}
|
||||
if spanID, ok := ctx.Value(spanIDKey).(string); ok {
|
||||
req.Header.Set("X-Span-ID", spanID)
|
||||
}
|
||||
return c.client.Do(req)
|
||||
}
|
||||
```
|
||||
|
||||
## Context in Calls to Other Services
|
||||
|
||||
Context MUST be propagated to all HTTP clients and databases using context-aware APIs: `http.NewRequestWithContext`, `QueryContext`, `ExecContext`, and `QueryRowContext`. This ensures that client disconnections cancel all downstream operations.
|
||||
|
||||
```go
|
||||
// ✗ Bad — downstream calls ignore the request context
|
||||
func (c *PaymentClient) Charge(ctx context.Context, amount int) error {
|
||||
req, _ := http.NewRequest("POST", c.url+"/charge", body)
|
||||
return c.client.Do(req) // not context-aware
|
||||
}
|
||||
|
||||
// ✓ Good — all downstream operations respect the context
|
||||
func (c *PaymentClient) Charge(ctx context.Context, amount int) error {
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", c.url+"/charge", body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating request: %w", err)
|
||||
}
|
||||
return c.client.Do(req)
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// ✗ Bad — downstream calls ignore the request context
|
||||
func (r *UserRepo) FindByID(ctx context.Context, id string) (*User, error) {
|
||||
row := r.db.QueryRow("SELECT * FROM users WHERE id = $1", id)
|
||||
// ...
|
||||
}
|
||||
|
||||
// ✓ Good — all downstream operations respect the context
|
||||
func (r *UserRepo) FindByID(ctx context.Context, id string) (*User, error) {
|
||||
row := r.db.QueryRowContext(ctx, "SELECT * FROM users WHERE id = $1", id)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user