feat!: axum 0.8, plus the extractors and router methods it was hiding - #73
Merged
Conversation
**axum 0.7 -> 0.8.** The headline change is that a captured path segment is now written `{id}`
rather than `:id`, and axum rejects the old form at startup instead of matching it literally. That
is a breaking change for every application's routes, so it belongs in this release rather than
after it.
It also simplifies gotcha: `{id}` is what OpenAPI already used, so the framework no longer
translates between the two. `replace_path_variable` is the identity, kept as documentation of that
fact, and `path_variable_names` (plus the `ParameterProvider` regexes) now read `{name}`.
Other adjustments the upgrade forced:
- axum-core 0.5 dropped `#[async_trait]` from its extractor traits, so `Valid`, `Header` and
`Cookie` use plain `async fn` — reversing the attribute that E0195 required under 0.7.
- `Router::layer` gained `Sync` bounds, mirrored on `GotchaRouter::layer` and the builder.
- axum-extra 0.10, axum-macros 0.5, axum-prometheus 0.8; the two examples with their own `axum`
dependency move too, and `http = "0.2"` is dropped (it was unused, and two major versions behind).
**Capabilities axum had that gotcha did not surface**, now re-exported so they no longer need
`gotcha::axum::…`: `Form` and `Multipart` (whose features were already enabled and whose schema
support already existed), `Sse`/`Event`/`KeepAlive`, `WebSocketUpgrade`/`WebSocket`, `middleware`
for `from_fn`, and `MatchedPath`/`OriginalUri`. The `ws`, `matched-path` and `original-uri`
features are turned on for those. The websocket frame type stays behind `ws::Message`, since
`Message` is already the message-system trait.
**`GotchaRouter::fallback_service`** handles unmatched requests with a `Service` rather than a
handler — serving an SPA's index with `ServeFile`, say.
A pass test exercises all of it, and MIGRATION.md leads with the route-path change since it is the
edit every application has to make.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merged
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.
Three things at once, because they belong in the same breaking release.
1. axum 0.7 → 0.8
The headline change is path syntax: a captured segment is written
{id}, not:id, and axum refuses the old form at startup rather than matching it literally:That is a breaking change for every application's routes, which is exactly why it belongs here rather than in 0.5 — asking users to migrate their config in 0.4 and their routes in 0.5 is two disruptions where one will do.
It also simplifies gotcha:
{id}is what OpenAPI already used, so the framework no longer translates between the two syntaxes.replace_path_variablebecomes the identity (kept, documented, and tested as such), and the parameter-name extraction inParameterProviderreads{name}.Other adjustments the upgrade forced:
#[async_trait]from its extractor traits, soValid,HeaderandCookieuse a plainasync fn— reversing the attribute that E0195 demanded under 0.7.Router::layergainedSyncbounds, mirrored onGotchaRouter::layerand the builder.axumdependency move too.http = "0.2"dropped — unused, and two major versions behind what axum itself uses.2.
fallback_serviceUnmatched requests can now go to a
Servicerather than a handler — serving an SPA'sindex.htmlwithServeFile, or forwarding to a proxy, wherefallbackwould need a wrapper.3. The extractors that were there but unreachable
Re-exported, so none of these need
gotcha::axum::…any more:Form,MultipartMultipartalready had aParameterProviderimpl — they were simply never re-exportedSse,Event,KeepAliveWebSocketUpgrade,WebSocketwsfeaturemiddlewaremiddleware::from_fnMatchedPath,OriginalUrimatched-path/original-uriThe websocket frame type stays behind
ws::Message, becauseMessageis already the message-system trait — re-exporting it bare would have been an ambiguity.Tests
tests/pass/handler/axum_surface.rsexercises all of it: a urlencoded body, a multipart upload, an SSE stream, a websocket upgrade that echoes a frame,MatchedPath/OriginalUri,middleware::from_fn, andfallback_service.Migration
MIGRATION.mdnow leads the 0.4 section with the route-path change, since it is the edit every application must make — ahead of the configuration change it previously led with.Verification
Workspace builds with
--all-features, every feature combination tests,clippy --all-features --workspace,fmt.🤖 Generated with Claude Code