skip to content

How do server frameworks such as Spring Boot and ASP.NET Core produce application/problem+json responses out of the box, and what do you typically have to customise?

level: middleimportance: nice to knowfreq 28%

answer

  1. Spring: ProblemDetail type + opt-in switch
  2. ASP.NET Core: problem details on by default
  3. customise type URIs, detail text, extensions
  4. partial coverage: domain, security filters, gateway
  5. extension keys are strings — constants + tests

basics

~20 s

Both ship first-class support: Spring Boot has a ProblemDetail type and an opt-in switch to return problem+json for framework errors; ASP.NET Core returns problem documents by default for error statuses and validation failures. You customise the type URIs, extensions and detail text.

solid answer

~50 s

**Spring Boot** (6.x / Boot 3.x) provides a `ProblemDetail` type modelling RFC 9457's members plus a properties map for extensions. Framework-raised exceptions can be rendered as `application/problem+json` once you turn the behaviour on — it is opt-in via configuration rather than the default. You then customise by building `ProblemDetail` instances in your exception handling and setting `type`, extensions and status. **ASP.NET Core** produces problem documents by default for error status codes and for model-validation failures, through a problem-details service you can replace or wrap to inject your own members. What you almost always customise: the **`type` URIs** (defaults are generic or absent, and the type is the member clients branch on), the **`detail` text** (defaults can leak framework specifics), and **extension members** carrying actionable data plus a correlation id. The main trap is partial coverage — framework defaults handle framework-raised errors, while your domain exceptions and the gateway's own responses still need explicit mapping to the same shape.

go deeper

for a junior

Know that both frameworks can emit problem+json and that you generally configure the type URI and detail yourself.

for a middle

Name the concrete mechanisms — Spring's ProblemDetail plus the opt-in switch, ASP.NET Core's default problem-details behaviour — and what you customise.

for a senior

Focus on the coverage audit: domain exceptions, security middleware, gateway responses, and reviewing generated detail text for leakage.

for a principal

Standardise the customisation as shared middleware or a service template so every service inherits the same type-URI policy and correlation-id extension by default.

## Why built-in support matters The strongest practical argument for adopting RFC 9457 is that you don't have to build the envelope. Frameworks model the document, serialize it with the right media type, and hook it into their existing error paths, so the standard shape is the low-effort path rather than the disciplined one. ## Spring Boot / Spring Framework Spring Framework 6 (Spring Boot 3) introduced a `ProblemDetail` type that models the five RFC members directly, plus a map for extension members. Two ways it appears: - **Framework-raised errors.** Spring can render the exceptions it raises itself — unsupported media type, method not allowed, validation failures, and similar — as `application/problem+json`. This is **opt-in**: it is enabled through configuration rather than being on by default, which surprises people who expect it automatically. - **Your own handlers.** In exception-handling code you construct a `ProblemDetail`, set the status, type URI, detail and any extension properties, and return it. The base exception types Spring provides carry a `ProblemDetail` you can enrich rather than replace. Because extensions live in a property map rather than typed fields, extension keys are strings — worth putting in constants and covering with tests, since a typo is a silent contract break. ## ASP.NET Core ASP.NET Core leans further towards on-by-default. It returns problem documents for error status codes and, notably, produces a problem-shaped response for model-validation failures on controllers automatically. It exposes a problem-details service and factory abstraction, so you register a customisation hook that runs for every generated document — the natural place to stamp in a `type` URI, a trace identifier and any organisation-wide extension members. ## What you customise, in practice 1. **`type` URIs.** Defaults are generic (often status-code-derived or absent). Since `type` is the member clients branch on, generic defaults waste the standard's main benefit. Map each domain failure to a URI under a domain you control. 2. **`detail` text.** Framework-generated detail can name internal types, binding paths or exception text. Review what actually ships to clients; this is where trace-adjacent leakage sneaks in through a standard-looking envelope. 3. **Extension members.** Add the actionable data and — almost always — a correlation/trace id, injected centrally so every document carries it. 4. **Coverage of your own exceptions.** Framework defaults cover framework-raised errors. Domain exceptions still need explicit mapping, or you get a mix: standard documents for a 415 and a hand-rolled body for a business conflict. ## The partial-coverage trap The most common real-world outcome is an API that is *mostly* problem+json. Three gaps recur: - **Domain exceptions** falling through to a generic 500 in a different shape. - **Security-layer rejections** — authentication and authorization failures are often handled by filters or middleware that sit outside the MVC exception pipeline and emit their own body or an empty one. - **Gateway and proxy responses** — 502/504, request-size rejections, TLS-layer errors — which the application never sees and which frequently return HTML. Clients therefore still cannot rely on a single shape, which defeats the purpose. Auditing all three is what turns "we use problem details" into something a client can depend on. ## Client-side support HTTP clients and generated SDKs increasingly recognise `application/problem+json` and deserialize it into a typed error. That is the payoff of adopting the standard rather than a bespoke envelope: less client code to write per API. It also reinforces the earlier rule — configure the deserializer leniently so unknown extension members don't break parsing.

  • After enabling framework problem+json support, some endpoints still return a different error shape. Where do you look first?
    Three places: domain exceptions with no explicit mapping falling into a generic handler, security filters or middleware that reject requests before the MVC exception pipeline runs, and the gateway or reverse proxy generating its own 502/504 and request-size responses. Those layers never pass through the framework's problem-details machinery, so each needs its own configuration to emit the same shape.
  • Why review the framework-generated detail text before shipping?
    Default detail strings often embed framework specifics — exception type names, model binding paths, unsupported media type internals — which is information disclosure wearing a standards-compliant envelope. Replace them with messages that describe the caller's problem in the caller's terms and keep the diagnostics in server-side logs under the correlation id.

saying these in an interview costs you the question

  • Assuming Spring Boot emits problem+json for framework errors without enabling it
  • Shipping default detail text without checking what internal names it exposes
  • Leaving type as a generic default so clients have no stable value to branch on
  • Believing framework support alone gives full coverage, ignoring security filters and gateway responses
  • Hard-coding extension key strings in many places instead of constants covered by tests

context