fix(feedback): repeated tags decode + inline error instead of bare 422 page #101

Merged
YousifShkara merged 1 commit from fix/feedback-form-tags-and-inline-error into main 2026-06-10 08:29:51 +02:00
Owner

The feedback form rendered four checkboxes named tags (Bug / Feature / Flow / Idea) backed by FeedbackForm.tags: Vec<String>, extracted via axum's stock Form extractor. The extractor uses serde_urlencoded, which has no special handling for repeated keys: as soon as the user ticked one box the request body became tags=Bug&... and serde_urlencoded failed with invalid type: string "Bug", expected a sequence. Worse, that failure surfaced as axum's bare 422 text - the user lost their typed message and there was no way to see what went wrong on the form page itself.

Two-part fix in one PR:

  1. Accept the raw body as axum::body::Bytes and decode with form_urlencoded::parse. A new parse_feedback_form(body) helper walks the (key, value) pairs and builds FeedbackInput directly, pushing each tags value into a Vec as it sees them. Empty / whitespace-only tag values are skipped. The natural HTML shape - one <input type="checkbox" name="tags"> per option - now just works without any form-side rename or hidden-field workaround.

  2. Because the handler accepts Bytes (which never rejects on shape), it always runs to render the feedback page. The "Please enter a message." branch already plumbed an inline error banner; reusing that surface means any future client-side hiccup also stays on-page instead of leaking the 422.

Dependency: form_urlencoded = "1" is promoted from a transitive (via url) to a direct dep on bunyip-web. The struct + serde derive + axum::Form / serde::Deserialize imports for the old FeedbackForm are all removed.

Reference: the saas frontend uses tags[] on its FormData; bunyip's checkboxes still use tags (no brackets) because the manual decoder accepts both.

The feedback form rendered four checkboxes named `tags` (Bug / Feature / Flow / Idea) backed by `FeedbackForm.tags: Vec<String>`, extracted via axum's stock `Form` extractor. The extractor uses `serde_urlencoded`, which has no special handling for repeated keys: as soon as the user ticked one box the request body became `tags=Bug&...` and `serde_urlencoded` failed with `invalid type: string "Bug", expected a sequence`. Worse, that failure surfaced as axum's bare 422 text - the user lost their typed message and there was no way to see what went wrong on the form page itself. Two-part fix in one PR: 1. Accept the raw body as `axum::body::Bytes` and decode with `form_urlencoded::parse`. A new `parse_feedback_form(body)` helper walks the (key, value) pairs and builds `FeedbackInput` directly, pushing each `tags` value into a Vec as it sees them. Empty / whitespace-only tag values are skipped. The natural HTML shape - one `<input type="checkbox" name="tags">` per option - now just works without any form-side rename or hidden-field workaround. 2. Because the handler accepts `Bytes` (which never rejects on shape), it always runs to render the feedback page. The "Please enter a message." branch already plumbed an inline error banner; reusing that surface means any future client-side hiccup also stays on-page instead of leaking the 422. Dependency: `form_urlencoded = "1"` is promoted from a transitive (via `url`) to a direct dep on bunyip-web. The struct + serde derive + axum::Form / serde::Deserialize imports for the old `FeedbackForm` are all removed. Reference: the saas frontend uses `tags[]` on its FormData; bunyip's checkboxes still use `tags` (no brackets) because the manual decoder accepts both.
fix(feedback): repeated tags decode + inline error instead of bare 422 page
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 1m21s
2cb8b99183
The feedback form rendered four checkboxes named `tags` (Bug / Feature / Flow / Idea) backed by `FeedbackForm.tags: Vec<String>`, extracted via axum's stock `Form` extractor. The extractor uses `serde_urlencoded`, which has no special handling for repeated keys: as soon as the user ticked one box the request body became `tags=Bug&...` and `serde_urlencoded` failed with `invalid type: string "Bug", expected a sequence`. Worse, that failure surfaced as axum's bare 422 text - the user lost their typed message and there was no way to see what went wrong on the form page itself.

Two-part fix in one PR:

1. Accept the raw body as `axum::body::Bytes` and decode with `form_urlencoded::parse`. A new `parse_feedback_form(body)` helper walks the (key, value) pairs and builds `FeedbackInput` directly, pushing each `tags` value into a Vec as it sees them. Empty / whitespace-only tag values are skipped. The natural HTML shape - one `<input type="checkbox" name="tags">` per option - now just works without any form-side rename or hidden-field workaround.

2. Because the handler accepts `Bytes` (which never rejects on shape), it always runs to render the feedback page. The "Please enter a message." branch already plumbed an inline error banner; reusing that surface means any future client-side hiccup also stays on-page instead of leaking the 422.

Dependency: `form_urlencoded = "1"` is promoted from a transitive (via `url`) to a direct dep on bunyip-web. The struct + serde derive + axum::Form / serde::Deserialize imports for the old `FeedbackForm` are all removed.

Reference: the saas frontend uses `tags[]` on its FormData; bunyip's checkboxes still use `tags` (no brackets) because the manual decoder accepts both.
YousifShkara deleted branch fix/feedback-form-tags-and-inline-error 2026-06-10 08:29:51 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
psa-systems/bunyip!101
No description provided.