Documentation
¶
Overview ¶
Package arc provides a minimal, high-performance HTTP router for Go applications that want route parameters, middleware groups, subrouters, mounted handlers, host-based routing, and clear method handling while staying close to net/http.
Handlers are ordinary http.Handler and http.HandlerFunc values. Middleware is normal handler wrapping. A Router is itself an http.Handler, so it can be passed directly to http.ListenAndServe or http.Server.
Quick Start ¶
Create a router during application startup, register routes on it, then serve requests with net/http:
r := arc.New()
r.Get("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "ok")
})
r.Get("/users/{id}", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "user %s\n", req.PathValue("id"))
})
log.Fatal(http.ListenAndServe(":8080", r))
Build the router once before serving. After registration is complete, a Router is safe for concurrent requests.
Registering Routes ¶
Most applications use the method helpers. They accept http.HandlerFunc handlers and register one HTTP method for one path pattern:
r.Get("/users/{id}", getUser)
r.Post("/users", createUser)
r.Put("/users/{id}", updateUser)
r.Patch("/users/{id}", patchUser)
r.Delete("/users/{id}", deleteUser)
r.Head("/users/{id}", headUser)
r.Options("/users/{id}", optionsUser)
Use Router.Handle when you already have an http.Handler:
r.Handle(http.MethodGet, "/status", statusHandler)
Use Router.Any when a http.HandlerFunc should receive every method and decide what is acceptable:
r.Any("/healthz", health)
Use Router.HandleAny when you already have an http.Handler:
r.HandleAny("/healthz", healthHandler)
For the same path pattern, method-specific routes take precedence over an any-method route. Path specificity is considered before method handling, so a more specific path can win even when a less specific path has the exact method.
r.Get("/users/{id}", getUser)
r.Any("/users/me", currentUser)
// GET /users/me uses currentUser.
When a path matches but the method does not, Arc runs the method-not-allowed handler, returns 405 Method Not Allowed by default, and sets the Allow header.
Pattern Syntax ¶
Route, subrouter, and mount path patterns must be absolute paths beginning with "/". Host patterns match Request.Host instead of req.URL.Path and use DNS labels separated by ".".
Literal text matches exactly. A named parameter, written {name}, captures one non-empty path segment. The value is exposed through http.Request.PathValue.
r.Get("/users/{id}", getUser)
// GET /users/42 captures id = "42".
// GET /users/ does not match because the segment is empty.
Parameters can appear inside a segment with literal text around them. Each segment can contain at most one parameter.
r.Get("/files/{name}.json", getJSON)
// GET /files/report.json captures name = "report".
A catch-all parameter, written {*name}, captures the non-empty remainder of the path, including slashes. It must be at the end of the pattern.
r.Get("/assets/{*path}", serveAsset)
// GET /assets/css/app.css captures path = "css/app.css".
// GET /assets does not match because the catch-all value would be empty.
Host patterns match the whole normalized request host, not a suffix. A host parameter matches one non-empty DNS label, either as a whole label or with literal text around it; when literal text is present, only the parameter part is captured. Each host label can contain at most one parameter. A host catch-all parameter captures one or more leading labels and must appear in the leftmost label. IPv6 literals are matched as ordinary single-label hosts, so a pattern such as "{host}" can capture "::1".
r.Host("{tenant}.example.com")
// Host acme.example.com captures tenant = "acme".
// Host a.b.example.com does not match.
r.Host("api-{region}.example.com")
// Host api-us-west.example.com captures region = "us-west".
r.Host("{*subdomain}.example.com")
// Host a.b.example.com captures subdomain = "a.b".
// Host example.com does not match because the catch-all value would be empty.
Literal braces are escaped by doubling them:
r.Get("/files/{{name}}", literalName)
// GET /files/{name} uses literalName.
Parameter names must be non-empty. They cannot contain "/", and "*" is only valid at the start of a catch-all parameter. A single pattern cannot capture the same name more than once.
r.Get("/{tenant}/users/{id}", getTenantUser) // valid
r.Get("/{id}/users/{id}", bad) // invalid
Percent escapes in literal pattern text are decoded at registration. The patterns "/files/meta data" and "/files/meta%20data" describe the same literal path and conflict if both are registered.
Escaped slashes are treated as data inside a segment, not as path separators:
r.Get("/files/{name}", getFile)
r.Get("/files/a/b", getNestedFile)
// GET /files/a%2Fb uses getFile and captures name = "a/b".
// GET /files/a/b uses getNestedFile.
Arc does not clean request paths or issue http.ServeMux-style redirects for "." segments, ".." segments, or repeated slashes. They are matched as they appear in the request path.
Matching Order ¶
Arc chooses the most specific registered pattern that can match the request. Literal segments beat parameter segments. Parameter segments with more literal text beat looser parameter segments. Catch-all patterns are considered last. Ambiguous patterns are rejected at registration instead of being resolved by registration order.
r.Get("/users/me", currentUser)
r.Get("/users/{id}", getUser)
r.Get("/users/{*path}", usersCatchAll)
// GET /users/me uses currentUser.
// GET /users/42 uses getUser.
// GET /users/a/b uses usersCatchAll.
Routes, subrouters, and mounted handlers registered on the same Router share the same path matcher. A direct route can therefore handle a specific path below a subrouter or mount, while the child still owns the rest of that prefix.
api := r.SubRouter("/api")
api.Get("/users/{id}", getUser)
r.Get("/api/healthz", healthz)
// GET /api/healthz uses the parent route.
// GET /api/users/42 uses the subrouter route.
Host routers are checked before ordinary path dispatch. Literal host labels are more specific than parameter labels, and catch-all host patterns are considered after finite host patterns. Ambiguous host patterns that cannot be ordered deterministically are rejected at registration. If no host pattern matches, Arc falls through to the parent router's path routes.
Request Parameters ¶
Captured route, subrouter, mount, and host parameters are stored as request path values:
r.Host("{tenant}.example.com").
SubRouter("/api/{version}").
Get("/users/{id}", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, req.PathValue("tenant"))
fmt.Fprintln(w, req.PathValue("version"))
fmt.Fprintln(w, req.PathValue("id"))
})
When the same name is captured at multiple levels, the most specific value wins:
host params < subrouter params < route params
For mounted Arc routers, parameters captured by the outer mount remain available to the inner router and its handlers.
Matched Patterns ¶
Arc sets http.Request.Pattern before calling a matched route, mounted handler, or method-not-allowed fallback. The value is the full path pattern, including subrouter or mount prefixes.
api := r.SubRouter("/api/{version}")
api.Get("/users/{id}", func(w http.ResponseWriter, req *http.Request) {
log.Print(req.Pattern) // "/api/{version}/users/{id}"
})
Host patterns are not included in Request.Pattern; host captures are still available through Request.PathValue.
Middleware can read Request.Pattern once the route, mount, or method-not-allowed fallback it wraps has been selected. Middleware inherited by host routers and subrouters runs after the child router selects its final route or method-not-allowed fallback, so it sees the final path pattern.
Not-found fallback handlers receive an empty Request.Pattern, even when a host or subrouter prefix matched and contributed parameters.
Method Handling ¶
GET routes handle HEAD requests by default when no explicit HEAD route or any-method route matches the same path.
r.Get("/resource", getResource)
// HEAD /resource uses getResource by default.
Explicit HEAD routes and any-method routes take precedence over implicit GET handling. Use Router.SetImplicitHead(false) when your application needs exact method matching.
r := arc.New()
r.SetImplicitHead(false)
r.Get("/resource", getResource)
// HEAD /resource returns 405 Method Not Allowed.
For method-not-allowed responses, the Allow header lists the registered methods for the matched path. When implicit HEAD matching is enabled, HEAD is included for paths that have a GET route.
Trailing Slashes ¶
Trailing slashes are significant by default.
r.Get("/users/{id}", getUser)
// GET /users/42 matches.
// GET /users/42/ does not match.
Use Router.SetStrictSlash(false) to allow a request ending in "/" to match a route registered without that final slash.
r := arc.New()
r.SetStrictSlash(false)
r.Get("/users/{id}", getUser)
// GET /users/42 and GET /users/42/ both match.
Exact matches still win. If both "/resource" and "/resource/" are registered, "/resource/" uses the explicit trailing-slash route.
Middleware ¶
Middleware has the standard net/http shape: a function that wraps one handler with another.
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Printf("%s %s", req.Method, req.URL.Path)
next.ServeHTTP(w, req)
})
}
Register middleware with Router.Use:
r := arc.New()
r.Use(logging)
r.Get("/healthz", health)
Middleware runs in the order it is registered and applies to routes, subrouters, host routers, and mounted handlers registered after the Use call. Fallback handlers use the current middleware stack for the router that owns the fallback.
r.Get("/healthz", health) // no auth middleware
r.Use(requireAuth)
r.Get("/account", account) // uses requireAuth
Middleware registered on a parent before creating a child router is inherited by the child. Middleware added to the child applies only inside that child.
Subrouters ¶
Use Router.SubRouter when a section of an application shares a path prefix, middleware, fallback handlers, or settings.
r := arc.New()
api := r.SubRouter("/api/{version}")
api.Use(requireAuth)
api.Get("/users/{id}", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "%s user %s\n",
req.PathValue("version"),
req.PathValue("id"),
)
})
A subrouter matches the remaining path after its mount point. A child mounted at "/api" matches "/users" for a request to "/api/users". Both "/api" and "/api/" are dispatched to the child's "/" route.
The original request URL is not rewritten for subrouters. Parent middleware, child middleware, and child handlers all see the original req.URL.Path.
Subrouter prefixes are matched on whole path segments. A subrouter mounted at "/api" does not match "/apix".
An empty subrouter pattern is treated as "/"; a non-root subrouter pattern has trailing slashes trimmed before registration.
Mounted Handlers ¶
Use Router.Mount when an existing http.Handler should own everything below a path. This is useful for file servers, third-party handlers, and other routers.
r := arc.New()
r.Mount("/assets", http.FileServerFS(assets))
Mounted handlers receive the remaining path as req.URL.Path. Parent middleware sees the original request path before the mounted handler receives the rewritten path.
r.Mount("/assets", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Print(req.URL.Path)
}))
// GET /assets/app.css logs "/app.css".
// GET /assets and GET /assets/ log "/".
Mount parameters are available through Request.PathValue.
r.Mount("/tenants/{tenant}/assets", assetHandler)
// GET /tenants/acme/assets/app.css exposes tenant = "acme".
Like subrouters, mount prefixes are matched on whole path segments. A mount at "/assets" does not match "/assets-old".
Host Routers ¶
Use Router.Host when one application serves different routes for different domains or subdomains.
r := arc.New()
api := r.Host("api.example.com")
api.Get("/users/{id}", getUser)
tenant := r.Host("{tenant}.example.com")
tenant.Get("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "tenant %s\n", req.PathValue("tenant"))
})
Host patterns are matched against the whole normalized host. A pattern such as "example.com" does not match "www.example.com", and "{tenant}.example.com" captures exactly one DNS label before ".example.com"; use another parameter label or a leftmost catch-all to match more subdomain levels. "api-{region}" captures one label with a required literal prefix, while "api-{*subdomain}.example.com" captures leading labels after the "api-" prefix. Literal host text is matched case-insensitively, while parameter names keep their original case. Captured host parameter values come from the normalized host, so ASCII letter case is preserved and IDNs are punycode.
Trailing dots are ignored, IDNs are normalized to punycode, and a numeric port in Request.Host is ignored before matching. Brackets around colon-form hosts are also ignored, so "[::1]" and "[::1]:8080" match the host pattern "::1". Host patterns themselves must not include a port.
Literal labels are more specific than parameter labels. If both "api.example.com" and "{tenant}.example.com" are registered, "api.example.com" uses the literal host router. Finite host patterns are more specific than catch-all host patterns. Overlapping dynamic patterns with no deterministic winner, such as "{tenant}.example.com" and "{account}.example.com", conflict.
If no host pattern matches, Arc continues dispatching through the parent router's ordinary routes, subrouters, and mounts.
Fallback Handlers ¶
By default, unmatched requests receive the same response as http.NotFoundHandler, and paths registered for a different method receive 405 Method Not Allowed.
Customize fallback handlers during startup:
r := arc.New() r.SetNotFound(http.HandlerFunc(notFound)) r.SetMethodNotAllowed(http.HandlerFunc(methodNotAllowed))
Fallback handlers receive any parameters captured before the fallback was selected.
api := r.SubRouter("/api/{version}")
api.SetNotFound(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Print(req.PathValue("version"))
http.NotFound(w, req)
}))
Fallback handlers run through middleware for the router that owns the fallback. For a subrouter or host router, that includes middleware inherited from the parent plus middleware registered on the child.
Passing nil to Router.SetNotFound or Router.SetMethodNotAllowed leaves the existing fallback handler unchanged.
Registration Errors ¶
The non-Try registration methods panic on invalid, duplicate, or ambiguous patterns. That is convenient for fixed application routes registered at startup. Use the Try variants when patterns come from configuration, plugins, or any runtime source.
if err := r.TryHandle(http.MethodGet, "/users/{id}", http.HandlerFunc(getUser)); err != nil {
return err
}
if err := r.TryHandleAny("/healthz", http.HandlerFunc(health)); err != nil {
return err
}
api, err := r.TrySubRouter("/api/{version}")
if err != nil {
return err
}
if err := r.TryMount("/assets", http.FileServerFS(assets)); err != nil {
return err
}
tenant, err := r.TryHost("{tenant}.example.com")
if err != nil {
return err
}
Route methods that are not valid HTTP tokens return ErrInvalidMethod. Extension methods are accepted and method matching is case-sensitive. Route, subrouter, and mount path patterns that do not begin with "/" return ErrInvalidPathPattern. Empty host patterns, host patterns with invalid DNS characters, host patterns with ports, invalid host catch-all placement, and invalid host parameter syntax return ErrInvalidHostPattern. Patterns that capture the same parameter name more than once return ErrDuplicateParamName. Other registration errors include invalid parameter syntax, duplicate registrations, and ambiguous patterns that could match the same requests.
Child Router Configuration ¶
Subrouters and host routers copy the parent router's current settings when they are created. Configure shared behavior first.
r := arc.New()
r.SetStrictSlash(false)
r.SetImplicitHead(false)
r.SetNotFound(http.HandlerFunc(notFound))
r.SetMethodNotAllowed(http.HandlerFunc(methodNotAllowed))
api := r.SubRouter("/api")
api.Get("/users/{id}", getUser)
Later changes on the parent do not affect existing children. Middleware follows the same registration-order model: middleware already registered on the parent is inherited by the child, while later parent middleware is not.
Child routers can still be configured independently after creation.
api := r.SubRouter("/api")
api.SetNotFound(http.HandlerFunc(apiNotFound))
api.SetStrictSlash(true)
Concurrency ¶
Register routes and configure the router before serving requests. A Router is safe for concurrent serving after registration is complete. Registration and configuration methods are not safe to call concurrently with ServeHTTP or with each other.
Index ¶
- Variables
- type Middleware
- type Router
- func (r *Router) Any(pattern string, h http.HandlerFunc)
- func (r *Router) Delete(pattern string, h http.HandlerFunc)
- func (r *Router) Get(pattern string, h http.HandlerFunc)
- func (r *Router) Handle(method, pattern string, h http.Handler)
- func (r *Router) HandleAny(pattern string, h http.Handler)
- func (r *Router) Head(pattern string, h http.HandlerFunc)
- func (r *Router) Host(pattern string) *Router
- func (r *Router) Mount(pattern string, h http.Handler)
- func (r *Router) Options(pattern string, h http.HandlerFunc)
- func (r *Router) Patch(pattern string, h http.HandlerFunc)
- func (r *Router) Post(pattern string, h http.HandlerFunc)
- func (r *Router) Put(pattern string, h http.HandlerFunc)
- func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (r *Router) SetImplicitHead(enabled bool)
- func (r *Router) SetMethodNotAllowed(h http.Handler)
- func (r *Router) SetNotFound(h http.Handler)
- func (r *Router) SetStrictSlash(strict bool)
- func (r *Router) SubRouter(pattern string) *Router
- func (r *Router) TryHandle(method, pattern string, h http.Handler) error
- func (r *Router) TryHandleAny(pattern string, h http.Handler) error
- func (r *Router) TryHost(pattern string) (*Router, error)
- func (r *Router) TryMount(pattern string, h http.Handler) error
- func (r *Router) TrySubRouter(pattern string) (*Router, error)
- func (r *Router) Use(mw ...Middleware)
Constants ¶
This section is empty.
Variables ¶
var ErrDuplicateParamName = fmt.Errorf("%w: duplicate parameter names are not allowed within one pattern", match.ErrInvalidParam)
ErrDuplicateParamName reports a single registered pattern that captures the same parameter name more than once.
A duplicate name is invalid even when the captures come from different segments or when one capture is a catch-all parameter.
var ErrInvalidHostPattern = fmt.Errorf("%w: host patterns must be valid host names or host parameters", match.ErrInvalidParam)
ErrInvalidHostPattern reports a host router pattern that is empty, contains characters outside supported host syntax, includes a port, or uses parameter syntax that Arc host patterns do not support.
Host patterns registered with Router.Host and Router.TryHost must be a valid DNS host name, a colon-form host literal such as "::1", or a host name with parameters such as "{tenant}.example.com", "api-{region}.example.com", or "{*subdomain}.example.com". Host patterns are matched against the whole normalized request host.
var ErrInvalidMethod = errors.New("HTTP methods must be valid tokens")
ErrInvalidMethod reports a route method that is not a valid HTTP method token.
Methods registered with Router.Handle and Router.TryHandle must use the HTTP token syntax. Extension methods are allowed, and method matching remains case-sensitive.
var ErrInvalidPathPattern = errors.New("path patterns must begin with /")
ErrInvalidPathPattern reports a route, subrouter, or mount path pattern that is not an absolute HTTP path.
Path patterns registered with Router.Handle, Router.HandleAny, Router.SubRouter, and Router.Mount must begin with "/". SubRouter and Mount treat an empty pattern as "/"; route registrations do not.
Functions ¶
This section is empty.
Types ¶
type Middleware ¶
Middleware wraps an HTTP handler on a Router.
Middleware uses the same shape as standard net/http middleware. If a router uses middleware a, b, and c, requests flow through:
a -> b -> c -> matched handler or fallback handler
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is an http.Handler that dispatches application requests by host, path, and method.
Build a router during startup by configuring fallback handlers and registering middleware, host routers, subrouters, mounted handlers, and routes. After it is built, pass it to http.Server or http.ListenAndServe.
A Router is safe for concurrent serving after registration is complete. The registration and configuration methods are not safe to call concurrently with ServeHTTP or with other registration and configuration methods.
func New ¶
func New() *Router
New creates a Router with defaults suitable for a typical net/http application.
By default:
- unmatched requests receive the same response as http.NotFoundHandler;
- paths registered for a different method receive 405 Method Not Allowed;
- GET routes handle HEAD requests unless an explicit HEAD or any-method route matches.
Child routers and host routers copy the parent router's current settings when they are created.
func (*Router) Any ¶ added in v0.5.0
func (r *Router) Any(pattern string, h http.HandlerFunc)
Any registers h for pattern and lets it handle any request method.
Any is the http.HandlerFunc convenience form of Router.HandleAny.
func (*Router) Delete ¶
func (r *Router) Delete(pattern string, h http.HandlerFunc)
Delete registers h for DELETE requests matching pattern.
func (*Router) Get ¶
func (r *Router) Get(pattern string, h http.HandlerFunc)
Get registers h for GET requests matching pattern.
By default, the route also handles HEAD requests when no explicit HEAD or any-method route matches the same path. Use Router.SetImplicitHead with false to require an explicit HEAD route.
func (*Router) Handle ¶
Handle registers h for one HTTP method and path pattern.
Use Handle when you have an http.Handler value. For http.HandlerFunc handlers, the method helpers such as Router.Get and Router.Post are usually shorter.
The method must be a valid HTTP token. Extension methods are accepted and method matching is case-sensitive.
The pattern must begin with "/" and may contain named parameters such as "/users/{id}" or catch-all parameters such as "/assets/{*path}". Invalid, duplicate, or ambiguous patterns panic. Use Router.TryHandle to receive the registration error instead.
func (*Router) HandleAny ¶ added in v0.5.0
HandleAny registers h for pattern and lets it handle any request method.
Use HandleAny when you have an http.Handler value. For http.HandlerFunc handlers, Router.Any is usually shorter.
Any-method routes are useful for endpoints such as health checks or webhooks where the handler should decide which methods are acceptable:
r.HandleAny("/healthz", healthHandler)
The path pattern follows Arc's route syntax. For example, "/users/{id}" captures one segment and "/assets/{*path}" captures the remaining path.
Routes, subrouters, and mounted handlers share one path matcher on the same router. The most specific path wins, so a direct route below a mounted prefix, such as /api/healthz when /api is a subrouter or mount, handles that path.
Invalid, duplicate, or ambiguous patterns panic. Use Router.TryHandleAny to receive the registration error instead.
func (*Router) Head ¶
func (r *Router) Head(pattern string, h http.HandlerFunc)
Head registers h for HEAD requests matching pattern.
func (*Router) Host ¶
Host registers and returns a child router for requests whose host matches pattern.
Use Host when one application serves different routes for different domains or subdomains:
api := r.Host("api.example.com")
api.Get("/users/{id}", getUser)
tenant := r.Host("{tenant}.example.com")
tenant.Get("/", tenantHome)
Host patterns are DNS-label patterns matched against the whole normalized request host. For example, "api.example.com" matches one literal host and "{tenant}.example.com" captures exactly one DNS label before ".example.com" as "tenant". Host parameters can occupy an entire label or appear with literal text around them; "api-{region}.example.com" captures "us-west" from "api-us-west.example.com". Each host label can contain at most one parameter. A catch-all parameter such as "{*subdomain}.example.com" captures one or more leading labels and must appear in the leftmost label.
Literal host labels are matched case-insensitively and are more specific than parameter labels. If "api.example.com" and "{tenant}.example.com" are both registered, "api.example.com" handles that host. Finite host patterns are more specific than catch-all host patterns. Overlapping host patterns with no deterministic winner are rejected as ambiguous.
Request hosts are normalized before matching: trailing dots are ignored, IDNs are normalized to punycode, a numeric port in Request.Host is ignored, and brackets around colon-form hosts are ignored. Host patterns themselves must not include a port. IPv6 literals are matched as ordinary single-label hosts, so a pattern such as "{host}" can capture "::1".
Parameters captured by the host pattern are available to handlers registered on the returned router through http.Request.PathValue. If no host pattern matches, dispatch falls through to the parent router's subrouters and routes.
Middleware already registered on the parent is inherited by the host router. It runs after the host router selects its final route or fallback, so route and method-not-allowed middleware can read the final Request.Pattern. Middleware added to the returned router applies only inside that host router, including host-router fallback handlers. The returned router copies the parent's current strict slash, implicit HEAD, and fallback handler settings when it is created.
Invalid, duplicate, or ambiguous host patterns panic. Use Router.TryHost to receive the registration error instead.
func (*Router) Mount ¶
Mount registers h below pattern and lets that handler own the remaining path.
Use Mount for file servers, another router, or any existing http.Handler that should handle everything below a path:
r.Mount("/assets", http.FileServerFS(assets))
The pattern follows Arc's path pattern syntax. Parameters captured by the mount pattern are available to middleware and the mounted handler through http.Request.PathValue.
The mounted handler receives the remaining path after the mount point as req.URL.Path. For example, a handler mounted at /assets receives /app.css for a request to /assets/app.css, while both /assets and /assets/ are dispatched as /. Middleware already registered on the parent sees the original request path and wraps the mounted handler.
Mounted handlers and direct parent routes share one path matcher. The most specific path wins, so a parent route below the mounted prefix, such as /assets/healthz, handles that exact path. Other paths under the mounted prefix are owned by the mounted handler.
An empty pattern is treated as "/"; non-root patterns have trailing slashes trimmed before registration. Invalid, duplicate, or ambiguous mount patterns panic. Use Router.TryMount to receive the registration error instead.
func (*Router) Options ¶
func (r *Router) Options(pattern string, h http.HandlerFunc)
Options registers h for OPTIONS requests matching pattern.
func (*Router) Patch ¶
func (r *Router) Patch(pattern string, h http.HandlerFunc)
Patch registers h for PATCH requests matching pattern.
func (*Router) Post ¶
func (r *Router) Post(pattern string, h http.HandlerFunc)
Post registers h for POST requests matching pattern.
func (*Router) Put ¶
func (r *Router) Put(pattern string, h http.HandlerFunc)
Put registers h for PUT requests matching pattern.
func (*Router) ServeHTTP ¶
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP dispatches req to the best matching host router, route, subrouter, or mounted handler.
Dispatch checks host routers first. Inside a host or ordinary router, routes, subrouters, and mounted handlers share one path matcher. The most specific path wins, so a direct route can handle a path below a subrouter or mounted prefix; other paths below that prefix are still owned by the child, including not-found and method-not-allowed handling.
Route and subrouter matching uses req.URL.Path unless req.URL.RawPath preserves an escaped slash. In that case, Arc matches an internal decoded path where the escaped slash stays inside its segment and restores captured parameters before exposing them through http.Request.PathValue. Arc does not perform net/http.ServeMux path cleaning redirects.
ServeHTTP satisfies http.Handler. It should usually be called by net/http rather than directly.
func (*Router) SetImplicitHead ¶
SetImplicitHead controls whether HEAD requests may use GET routes when no explicit HEAD or any-method route matches the same path.
Implicit HEAD matching is enabled by default. Explicit HEAD and any-method routes take precedence when present.
func (*Router) SetMethodNotAllowed ¶
SetMethodNotAllowed sets the handler used when a request path matches a route pattern, but the request method was not registered for that pattern.
Arc sets the Allow header before calling the handler. The handler runs through the router's current middleware stack.
Passing nil leaves the router's existing method-not-allowed handler unchanged.
func (*Router) SetNotFound ¶
SetNotFound sets the handler used when no host router, subrouter, mounted handler, or route matches a request.
The handler runs through the router's current middleware stack.
Passing nil leaves the router's existing not-found handler unchanged.
func (*Router) SetStrictSlash ¶
SetStrictSlash controls whether route matching treats a trailing slash as significant.
Strict slash matching is enabled by default. When disabled, a request path ending in "/" may match a route registered without that final slash:
r.SetStrictSlash(false)
r.Get("/users/{id}", getUser) // matches /users/42 and /users/42/
Exact route matches still take precedence.
Subrouters and host routers copy this setting when they are created. Later changes on the parent do not affect existing children.
func (*Router) SubRouter ¶
SubRouter registers and returns a child router mounted at pattern.
Use a subrouter when several routes share a path prefix, middleware, fallback handlers, or slash/method settings:
api := r.SubRouter("/api/{version}")
api.Use(requireAuth)
api.Get("/users/{id}", getUser)
The pattern follows Arc's path pattern syntax. Parameters captured by the mount pattern are available to child middleware and handlers through http.Request.PathValue.
The child matches against the remaining path after the mount point. For example, a child mounted at /api matches /users for a request to /api/users, while both /api and /api/ are dispatched to the child's / route. The request URL is not rewritten; middleware and handlers still see the original req.URL.Path.
Subrouters and direct parent routes share one path matcher. The most specific path wins, so a parent route such as /api/healthz handles that exact path even when /api is a subrouter. Other paths under the subrouter prefix are owned by the child, including child not-found and method-not-allowed handling. Register routes such as /api/healthz on the child as /healthz when they should use the child's middleware and fallback settings.
Middleware already registered on the parent is inherited by the child router. It runs after the child selects its final route or fallback, so route and method-not-allowed middleware can read the final Request.Pattern. Middleware added to the child applies only inside the child router, including child fallback handlers. The child copies the parent's current strict slash, implicit HEAD, and fallback handler settings when it is created.
An empty pattern is treated as "/"; non-root patterns have trailing slashes trimmed before registration. Invalid, duplicate, or ambiguous mount patterns panic. Use Router.TrySubRouter to receive the registration error instead.
func (*Router) TryHandle ¶ added in v0.3.0
TryHandle registers h for one HTTP method and path pattern and returns registration errors.
The method must be a valid HTTP token. Extension methods are accepted and method matching is case-sensitive.
Registration errors include non-absolute path patterns, invalid parameter syntax, duplicate parameter names within the pattern, invalid HTTP methods, duplicate registrations, and ambiguous patterns that could match the same requests. A nil handler is treated as http.NotFoundHandler.
func (*Router) TryHandleAny ¶ added in v0.5.0
TryHandleAny registers h for pattern, lets it handle any request method, and returns registration errors.
Registration errors include non-absolute path patterns, invalid parameter syntax, duplicate parameter names within the pattern, duplicate registrations, and ambiguous patterns that could match the same requests. A nil handler is treated as http.NotFoundHandler.
Routes, subrouters, and mounted handlers share one path matcher on the same router. The most specific path wins, so a direct route below a mounted prefix handles that path.
func (*Router) TryHost ¶ added in v0.3.0
TryHost registers and returns a child router for requests whose host matches pattern, and returns registration errors.
Registration errors include ErrInvalidHostPattern, invalid parameter syntax, duplicate parameter names within the pattern, duplicate host patterns, and ambiguous host patterns that could match the same requests.
func (*Router) TryMount ¶ added in v0.3.0
TryMount registers h below pattern and returns registration errors.
An empty pattern is treated as "/"; non-root patterns have trailing slashes trimmed before registration. Registration errors include non-absolute path patterns, invalid parameter syntax, duplicate parameter names within the pattern, duplicate mounts, and ambiguous mount patterns that could match the same requests. A nil handler is treated as http.NotFoundHandler.
func (*Router) TrySubRouter ¶ added in v0.3.0
TrySubRouter registers and returns a child router mounted at pattern and returns registration errors.
An empty pattern is treated as "/"; non-root patterns have trailing slashes trimmed before registration. Registration errors include non-absolute path patterns, invalid parameter syntax, duplicate parameter names within the pattern, duplicate mounts, and ambiguous mount patterns that could match the same requests.
func (*Router) Use ¶
func (r *Router) Use(mw ...Middleware)
Use appends middleware to the router.
Middleware applies only to routes, subrouters, host routers, and mounted handlers registered after the call to Use. Fallback handlers use the router's current middleware stack. This lets applications build separate sections of a router with different middleware stacks:
r.Get("/healthz", health) // no auth middleware
r.Use(requireAuth)
r.Get("/account", account) // uses requireAuth
Middleware is executed in the order it is added. Use panics if any middleware is nil.