On this page
Problem JSON
Micronaut Problem is a library which makes it easy to produce
application/problem+json responses from a Micronaut
application. It connects the Problem library
and Micronaut Error Formatting capabilities.
For this project, you can find a list of releases (with release notes) here:
Micronaut Problem JSON 3.0.0
Starting with Micronaut Framework 4.0.0, Micronaut Problem JSON is based on jakarta.validation rather than javax.validation as in previous versions.
It is necessary to specify application/problem+json as an additional type for the JSON codec.
Micronaut Problem JSON 2.2.3
The default Problem+JSON payload does not include the detail field to avoid accidental information disclosure if the exception root cause is not of type UnsatisfiedRouteException or ThrowableProblem to avoid accidental information disclosure since 2.2.3.
You can customize it to include always the detail or for some scenarios.
implementation("io.micronaut.problem:micronaut-problem-json")This library registers ProblemErrorResponseProcessor; an ErrorResponseProcessor for Problem. It sets the
response content type to application/problem+json and the response HTTP Status code to match the status field for Problem.
Moreover, it registers ThrowableProblemHandler. A Micronaut ErrorHandler for handling ThrowableProblem exception.
You can use Problem builder to create problem:
package io.micronaut.problem;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Status;
import org.zalando.problem.Problem;
import java.net.URI;
@Controller("/product")
public class ProductController {
@Get
@Status(HttpStatus.OK)
public void index() {
throw Problem.builder()
.withType(URI.create("https://example.org/out-of-stock"))
.withTitle("Out of Stock")
.withStatus(new HttpStatusType(HttpStatus.BAD_REQUEST))
.withDetail("Item B00027Y5QG is no longer available")
.with("product", "B00027Y5QG")
.build();
}
}The above snippet returns:
{
"status": 400,
"title": "Out of Stock",
"detail": "Item B00027Y5QG is no longer available",
"type": "https://example.org/out-of-stock",
"parameters": {"product": "B00027Y5QG"}
}You can create a custom problem by extending AbstractThrowableProblem
package io.micronaut.problem;
import io.micronaut.http.HttpStatus;
import org.zalando.problem.AbstractThrowableProblem;
import java.net.URI;
public class TaskNotFoundProblem extends AbstractThrowableProblem {
private static final URI TYPE = URI.create("https://example.org/not-found");
public TaskNotFoundProblem(Long taskId) {
super(TYPE, "Not found", new HttpStatusType(HttpStatus.NOT_FOUND), String.format("Task '%s' not found", taskId));
}
}If your logic throws such a Problem:
package io.micronaut.problem;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.PathVariable;
import io.micronaut.http.annotation.Status;
@Controller("/task")
public class TaskController {
@Get("/{taskId}")
@Status(HttpStatus.OK)
public void index(@PathVariable Long taskId) {
throw new TaskNotFoundProblem(taskId);
}
}You will get:
{
"status": 404,
"title": "Not found",
"detail": "Task '3' not found",
"type": "https://example.org/not-found"
}You can configure it via:
The default Problem+JSON payload does not include the detail field to avoid accidental information disclosure if the exception root cause is not of type UnsatisfiedRouteException or ThrowableProblem.
You can extend ProblemJsonErrorResponseBodyProvider to customize the behaviour:
@Replaces(ProblemJsonErrorResponseBodyProvider.class)
@Singleton
public class ProblemErrorResponseProcessorReplacement
extends ProblemJsonErrorResponseBodyProvider {
ProblemErrorResponseProcessorReplacement(ProblemConfiguration config) {
super(config);
}
@Override
protected boolean includeErrorMessage(@NonNull ErrorContext errorContext) {
return errorContext.getRootCause()
.map(t -> t instanceof FooException || t instanceof UnsatisfiedRouteException)
.orElse(false);
}
}You can find the source code of this project in this repository: