stano-axum
Axum HTTP layer: custom extractors with detailed error logging, a unified ApiError type that maps service errors to HTTP responses, and a standard error response format.
Install
[]
= { = "../stano-axum" }
= { = "../stano-common" }
API
Error Types
-
ApiError— HTTP response error type (implementsIntoResponse).JsonExtraction { status, body_text, rejection_type }— JSON deserialization failed.PathExtraction { status, body_text, rejection_type }— path parameter extraction failed.QueryExtraction { status, body_text, rejection_type }— query parameter extraction failed.Service(ServiceError)— service layer error (mapped to HTTP viaServiceErrorstatus code).Internal(String)— unhandled internal error (returns 500; details hidden from clients).
Behavior when converted to HTTP response:
ServiceError::NotFound→ 404 (no error logged).ServiceError::InvalidInput→ 400 (logged).ServiceError::Conflict→ 409 (logged).ServiceError::Unauthorized→ 401 (no error logged).ServiceError::Forbidden→ 403 (no error logged).ServiceError::InternalorApiError::Internal→ 500 (logged, details hidden from response).- Extraction errors (
JsonExtraction, etc.) → client error status withcode: "INVALID_JSON"/"INVALID_PATH"/"INVALID_QUERY"(logged).
-
ErrorResponse— standard JSON error shape:{ status, code, message, details?, request_id? }. Derivesutoipa::ToSchemaso it can be referenced directly in#[utoipa::path(responses(...))]blocks (see Usage Example).new(status: u16, code: impl Into<...>, message: impl Into<String>) -> Selfwith_details(mut self, details: impl Into<String>) -> Selfwith_request_id(mut self, request_id: impl Into<String>) -> Self
Extractors
-
AppJson<T>— JSON extractor (likeaxum::Json, but with detailed error logging).impl FromRequest<S> for AppJson<T> where T: DeserializeOwnedimpl IntoResponse for AppJson<T> where T: Serialize— also usable as a response type.
-
AppPath<T>— path parameter extractor (likeaxum::Path).impl FromRequestParts<S> for AppPath<T> where T: DeserializeOwned + Send
-
AppQuery<T>— query parameter extractor (likeaxum::Query).impl FromRequestParts<S> for AppQuery<T> where T: DeserializeOwned + Send
Middleware
error_logging_middleware— logsApiErrorwith request context (method, URI, request ID).- Reads
x-request-idfrom the request and logs anyApiErrorstored in response extensions.
- Reads
Usage Example
use ;
use ;
use ;
use State;
use ;
use Arc;
use ToSchema;
id_type!;
// Simulate a service.
async
// HTTP handler using custom extractors, annotated for OpenAPI generation.
// stano-launcher's #[post(...)]/#[get(...)]/etc. (see stano-launcher's README) replace
// #[utoipa::path(...)], inferring request_body/the 200 response/params(...) from the
// handler's signature — write #[post(...)] instead of #[utoipa::path], and the spec stays
// in sync automatically, with no separate `axum::Router::route` call.
async
// Another handler using path extraction, with a bearer-auth requirement declared explicitly.
// `params(("user_id" = UserId, Path))` is inferred from `AppPath<UserId>` + the single
// `{user_id}` placeholder.
async
Notes
- Error details privacy — 4xx responses include
detailsfield; 5xx responses do not (security: don't leak internals). - Request ID integration — the
error_logging_middlewarereadsx-request-idfrom incoming requests. For the ID to be set on the request itself, the middleware stack must includeSetRequestIdLayer(typically applied instano-launcher). The middleware still works if the ID is missing (just logsrequest_id: None). - No feature flags — all APIs available.