Allow special characters in Wolverine.HTTP route templates (fixes #3282)#3297
Merged
Conversation
Wolverine derives the generated handler *type name* from an endpoint's route template, but only stripped Path.GetInvalidPathChars() when doing so. Characters that are legal in a URL path but not in a C# identifier — most notably '$' in a route like "/assets/$action" — survived into the generated type name and made codegen fail with CS1056 "Unexpected character '$'". Fixes: - Sanitize the generated type name to a valid C# identifier: any non letter/digit/underscore char becomes '_', collapsed underscore runs, and a leading digit is prefixed with '_'. Route templates with special characters now "just work". - Collision handling: two routes that sanitize to the same type name (e.g. "/a$b" and "/a-b" -> "a_b") get a short, deterministic FNV-1a suffix in HttpGraph so codegen stays valid. The suffix is stable across builds, which matters for TypeLoadMode.Static. - Escape hatch: WolverineHttpMethodAttribute (all [WolverineVerb] attributes) gains an optional TypeName property to set the generated type name explicitly for edge cases. The override is itself sanitized so it can never produce invalid code. Docs: new "Special Characters in Routes" section in guide/http/routing.md covering the automatic sanitization and the TypeName escape hatch. Tests: reproduce the CS1056 failure (route + real codegen), the TypeName override (used + sanitized), collision disambiguation (distinct names, both compile), and determinism of the suffix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 9, 2026
Merged
This was referenced Jul 14, 2026
Open
This was referenced Jul 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3282.
Problem
Wolverine derives the generated handler C# type name from an endpoint's route template. A route can legally contain characters that are valid in a URL path but not in a C# identifier — most notably
$in an RPC-style route like/assets/$action. The name-derivation only strippedPath.GetInvalidPathChars()(a filesystem concept), so$survived intoassembly.AddType(...)and codegen failed:Fix (A + B + disambiguator)
A — Sanitize to a valid C# identifier. In
HttpChain.Codegen.determineFileName, any character that isn't a valid identifier char (letter/digit/_) becomes_, underscore runs are collapsed, and a leading digit is prefixed with_. Routes with special characters now just work — no user action needed.B —
TypeNameescape hatch. All[WolverineVerb]attributes gain an optionalTypeNameproperty to set the generated type name explicitly for edge cases. The override is itself sanitized, so it can never smuggle invalid code into codegen.Collision disambiguator (built in from the start). Two routes that sanitize to the same type name (e.g.
/a$band/a-b→a_b) now receive a short, deterministic FNV-1a suffix inHttpGraph, applied only to chains that actually collide. Determinism matters forTypeLoadMode.Static, where codegen-time names must match what was written to disk.Tests (
special_characters_in_route_templates.cs)CS1056(route + real codegen viaInitializeSynchronously) — now passes.TypeNameoverride is used, and is still sanitized.Docs
New "Special Characters in Routes" section in
guide/http/routing.mdcovering the automatic sanitization and theTypeNameescape hatch.Verification
Full
Wolverine.Http.Tests: 802 passed / 0 failed / 10 skipped — no regressions (normal routes derive the same names as before; sanitization is a superset of the old replacements). Release build clean.🤖 Generated with Claude Code