Package-level declarations

Entry pointRole
respondProblemRespond with a problem document; fills status and instance from the call.
problemDetailsInside install(StatusPages): build the exception-to-problem catalog.
ProblemDetailsCatalogThat catalog — map<T>, forStatusCode, standardStatusCodes, onUnmapped, customize.
problemJsonInside install(ContentNegotiation): register the JSON codec.
ProblemJsonConverterThe converter behind it, if you need to register it yourself.
ProblemContentTypesapplication/problem+json and application/problem+xml as ContentType.

Five behaviours here are decisions rather than defaults, and each is pinned by a test:

  • A problem document is always labelled application/problem+json, even when it was matched under application/json. RFC 9457 §3 permits the override, and echoing back application/json would strip the only wire-level marker saying the body is a problem document.

  • instance is filled from request.path(), never request.uri — the query string is left out because problem documents get logged on both ends and query strings carry tokens often enough that echoing them back by default is the wrong trade. Set instance explicitly to include it.

  • The catch-all handler rethrows CancellationException unless the status map claims it. A cancellation means the client is gone; answering it writes a document to a dead socket and logs a stack trace per dropped connection. TimeoutCancellationException still becomes a 504.

  • A thrown ProblemException is answered with the document it carries, via an entry seeded into the catalog — so map<ProblemException> replaces it like any other. Its cause, when present, is logged server-side (error for a 5xx, debug otherwise) because StatusPages logs nothing it handles and the cause would otherwise be dropped silently.

  • customize runs once, after whichever of map, onUnmapped or forStatusCode produced the problem, rather than being folded into each of those. Enrichment that belongs on every document — a trace id read off the ApplicationCall, say — would otherwise have to be repeated in every mapping instead of registered once. Registration is additive, unlike onUnmapped: each call adds a step and steps run in registration order, so two unrelated concerns — a trace id and a redaction pass, say — cannot silently drop one another.

Types

Link copied to clipboard

The two media types RFC 9457 registers (§6.1): application/problem+json and application/problem+xml.

Link copied to clipboard

A declarative catalog of exception-to-problem and status-to-problem mappings.

Link copied to clipboard

Reads and writes application/problem+json bodies for ContentNegotiation.

Properties

Link copied to clipboard

An HttpStatusCode-typed view over ProblemType.status, which is a plain Int in core.

Functions

Link copied to clipboard

The about:blank problem for status, with the title taken from Ktor's own reason phrase.

Link copied to clipboard

Builds a ProblemDetailsCatalog as a standalone value.

Link copied to clipboard

Registers an already-built catalog against Ktor's own StatusPages configuration.

Registers a ProblemDetailsCatalog against Ktor's own StatusPages configuration.

Link copied to clipboard
fun ContentNegotiationConfig.problemJson(acceptPlainJson: Boolean = true, json: Json = Json)

Registers the application/problem+json converter on Ktor's own ContentNegotiation.

Link copied to clipboard
suspend fun ApplicationCall.respondProblem(problem: Problem)

Responds with problem, the problem itself deciding the HTTP status.

suspend fun ApplicationCall.respondProblem(status: HttpStatusCode, problem: Problem)

Responds with problem at status, the response deciding the status.