dihttp

package
v0.13.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package dihttp connects a di.Scope to net/http.

A Middleware gives every request its own child scope holding the *http.Request, so services that depend on the request are declared once in the application scope as Scoped and built per request. Handlers reach the scope through di.FromContext, or are made with Handle, which resolves a handler type from that scope and calls one of its methods. Module registers the middleware as a service, so a server's constructor takes it as a parameter; NewMiddleware makes one directly.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Handle added in v0.13.0

func Handle[H any](method func(H, http.ResponseWriter, *http.Request)) http.Handler

Handle serves each request with a method of H, resolved from the request's scope. A method expression names both, so no type argument is needed:

mux.Handle("GET /users/{id}", dihttp.Handle((*Users).Show))

H is a service like any other: declared Scoped when it needs the request, and once for the application when it does not; one type per resource, with a method per route, keeps the dependencies in one place. Resolution follows the lifetime either way. A wiring failure at request time panics with the error, which net/http recovers and logs with the request; checking the graph with Validate at startup is what keeps that from happening.

func Module added in v0.12.0

func Module(s *di.Scope)

Module registers a Middleware over the scope it is applied to, so that a constructor wired into that scope can take one as a parameter:

app.Use(dihttp.Module, api.Module)

func NewServer(cfg Config, mw dihttp.Middleware) *http.Server {
	return &http.Server{Addr: cfg.Addr, Handler: mw(mux)}
}

The middleware needs the scope itself, to open a child per request, which is why this is the one closure in the package rather than a wired constructor.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/floatdrop/di"
	"github.com/floatdrop/di/dihttp"
)

type Caller struct{ Name string }

type Server struct{ http.Handler }

// Greetings is a handler type: one per resource, a method per route, built
// per request because it depends on the caller.
type Greetings struct{ caller *Caller }

func NewGreetings(c *Caller) *Greetings { return &Greetings{caller: c} }

func (g *Greetings) Hello(w http.ResponseWriter, _ *http.Request) {
	fmt.Fprintln(w, "hello", g.caller.Name)
}

// NewServer is a plain constructor: the middleware arrives as a dependency,
// and each route resolves its handler from the request scope.
func NewServer(mw dihttp.Middleware) *Server {
	mux := http.NewServeMux()
	mux.Handle("GET /hello", dihttp.Handle((*Greetings).Hello))
	return &Server{mw(mux)}
}

func main() {
	app := di.New()
	app.Use(dihttp.Module)
	app.Wire[*Caller](func(r *http.Request) *Caller { return &Caller{Name: r.Header.Get("X-Caller")} }).Scoped()
	app.Wire[*Greetings](NewGreetings).Scoped()
	app.Wire[*Server](NewServer)

	// The graph is checked as a request scope would resolve it: the
	// middleware is provided by the module, the request by each request.
	fmt.Println(app.Validate(di.Provided[*http.Request]()).Err())

	rec := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/hello", nil)
	r.Header.Set("X-Caller", "ada")
	app.Get[*Server]().ServeHTTP(rec, r)
	fmt.Print(rec.Body.String())
}
Output:
<nil>
hello ada

Types

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware gives every request its own child scope of the application scope: the *http.Request is registered in it, the scope is attached to the request context, and it is stopped (and detached) when the handler returns. Stop failures reach the application scope's observers as EventStop with Err set.

It has the usual middleware shape, so it wraps a handler directly or goes into a router's Use. Take it as a dependency after Module has registered it, or make one with NewMiddleware.

func NewMiddleware added in v0.12.0

func NewMiddleware(s *di.Scope) Middleware

NewMiddleware makes a Middleware whose request scopes are children of s.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/floatdrop/di"
	"github.com/floatdrop/di/dihttp"
)

type Caller struct{ Name string }

func main() {
	app := di.New()
	// Declared once in the application scope, built once per request scope,
	// where the *http.Request exists.
	app.Provide(func(s *di.Scope) *Caller {
		return &Caller{Name: s.Get[*http.Request]().Header.Get("X-Caller")}
	}).Scoped()

	h := dihttp.NewMiddleware(app)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		req, _ := di.FromContext(r.Context())
		fmt.Fprintln(w, "hello", req.Get[*Caller]().Name)
	}))

	rec := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/", nil)
	r.Header.Set("X-Caller", "ada")
	h.ServeHTTP(rec, r)
	fmt.Print(rec.Body.String())
}
Output:
hello ada

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL