Add problem-details: global exception handling with RFC 9457
Companion code for "Global Exception Handling with ProblemDetail (RFC 9457) in Spring Boot 4". Thirteen failures under five handling setups (Boot defaults, the Boot flag, a ResponseEntityExceptionHandler advice, advice plus an ErrorController, a catch-all ordered first), validation errors, i18n, content negotiation, Security's 401/403, silent 500s and decoding on the client. 16 tests pin the behaviour. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01C3TETMrqVUWeFkNtz3Jbo3
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# The article's first table: the same thirteen failures under each exception-handling setup.
|
||||
# Writes docs/output/matrix-<profile>.txt (full responses) and docs/output/matrix-summary.txt.
|
||||
set -uo pipefail
|
||||
source "$(dirname "$0")/env.sh"
|
||||
|
||||
SCENARIOS=(
|
||||
"domain exception (OrderNotFoundException)|GET|/orders/999||"
|
||||
"type mismatch (/orders/abc)|GET|/orders/abc||"
|
||||
"invalid body (@Valid)|POST|/orders|{\"sku\":\"\",\"quantity\":0,\"customerEmail\":\"nope\"}|"
|
||||
"invalid @RequestParam (@Max)|GET|/orders?limit=500||"
|
||||
"ErrorResponseException (OutOfStock)|POST|/orders|{\"sku\":\"SKU-2\",\"quantity\":3,\"customerEmail\":\"[email protected]\"}|"
|
||||
"ResponseStatusException|GET|/orders/legacy/7||"
|
||||
"unknown path|GET|/no-such-thing||"
|
||||
"wrong HTTP method|DELETE|/orders/1||"
|
||||
"malformed JSON|POST|/orders|{\"sku\":|"
|
||||
"unexpected exception|GET|/orders/boom||"
|
||||
"exception in a servlet filter|GET|/orders/1||X-Tenant: BAD!"
|
||||
"401 no credentials|GET|/admin/orders||"
|
||||
"403 wrong role|GET|/admin/orders||AUTH:user:user"
|
||||
)
|
||||
|
||||
classify() { # content-type, body -> shape label
|
||||
local ct="$1" body="$2"
|
||||
if [[ "$ct" == *problem+json* ]]; then echo "problem+json"
|
||||
elif [[ -z "$body" ]]; then echo "(empty body)"
|
||||
elif [[ "$body" == *'"timestamp"'* && "$body" == *'"error"'* ]]; then echo "Boot /error JSON"
|
||||
else echo "other: ${ct:-none}"; fi
|
||||
}
|
||||
|
||||
SUMMARY="$OUT/matrix-summary.txt"
|
||||
PROFILES=("" "boot-flag" "advice" "advice,errors" "catchall-first")
|
||||
declare -A CELL
|
||||
for profile in "${PROFILES[@]}"; do
|
||||
name="${profile:-defaults}"
|
||||
"$MODULE_DIR/scripts/run.sh" "$profile" || exit 1
|
||||
file="$OUT/matrix-$name.txt"
|
||||
{
|
||||
echo "# Profile: $name (spring-boot 4.1.1, spring-framework 7.0.9)"
|
||||
echo
|
||||
} > "$file"
|
||||
i=0
|
||||
for s in "${SCENARIOS[@]}"; do
|
||||
IFS='|' read -r label method path body extra <<< "$s"
|
||||
args=(-s -o /tmp/pd-body -D /tmp/pd-headers -X "$method" "$BASE$path")
|
||||
[ -n "$body" ] && args+=(-H 'Content-Type: application/json' --data "$body")
|
||||
if [[ "$extra" == AUTH:* ]]; then args+=(-u "${extra#AUTH:}");
|
||||
elif [ -n "$extra" ]; then args+=(-H "$extra"); fi
|
||||
curl "${args[@]}"
|
||||
status=$(head -1 /tmp/pd-headers | awk '{print $2}')
|
||||
ct=$(grep -i '^content-type:' /tmp/pd-headers | head -1 | cut -d' ' -f2- | tr -d '\r')
|
||||
resp=$(cat /tmp/pd-body)
|
||||
{
|
||||
echo "## $label"
|
||||
echo "\$ curl -X $method $path${body:+ -d '$body'}${extra:+ [$extra]}"
|
||||
echo "HTTP $status Content-Type: ${ct:-<none>}"
|
||||
[ -n "$resp" ] && echo "$resp"
|
||||
echo
|
||||
} >> "$file"
|
||||
CELL["$i|$name"]="$status $(classify "$ct" "$resp")"
|
||||
i=$((i+1))
|
||||
done
|
||||
done
|
||||
"$MODULE_DIR/scripts/stop.sh"
|
||||
|
||||
{
|
||||
echo "# Which error shape does each failure produce? (status + body shape)"
|
||||
echo "# Spring Boot 4.1.1 / Spring Framework 7.0.9. Full bodies in matrix-<profile>.txt"
|
||||
echo
|
||||
printf '%-42s | %-22s | %-22s | %-22s | %-22s | %-22s\n' "failure" "defaults" "boot-flag" "advice" "advice,errors" "catchall-first"
|
||||
printf '%s\n' "$(printf -- '-%.0s' {1..165})"
|
||||
i=0
|
||||
for s in "${SCENARIOS[@]}"; do
|
||||
label="${s%%|*}"
|
||||
printf '%-42s | %-22s | %-22s | %-22s | %-22s | %-22s\n' "$label" "${CELL["$i|defaults"]}" "${CELL["$i|boot-flag"]}" "${CELL["$i|advice"]}" "${CELL["$i|advice,errors"]}" "${CELL["$i|catchall-first"]}"
|
||||
i=$((i+1))
|
||||
done
|
||||
} > "$SUMMARY"
|
||||
cat "$SUMMARY"
|
||||
Reference in New Issue
Block a user