Usage

To signal a problem from a controller, build a Problem, attach any contextual info, and throw it:

@Controller("/pets")
public class PetController {

    Map<String, Pet> petRepository = new ConcurrentHashMap<>();

    @Get("/{name}")
    public Pet findPetByName(@PathVariable String name) {
        return petRepository.computeIfAbsent(
            name,
            (missing) -> {
                throw Problem.of(HttpStatus.NOT_FOUND.getCode(), PetProblemCode.PetNotFound)
                    .withInfo("name", missing)
                    .toThrowable();
            }
        );
    }

}

With the Micronaut integration in place, the thrown ThrowableProblem is translated into an application/problem+json HTTP response automatically — there’s no extra controller-level error handling to write.

The code and detail come from the ProblemCode enum referenced above. See Defining Problem Codes for how to define one.

Adding context

Use withInfo(…​) to attach the data that explains this specific occurrence of the problem:

throw Problem.of(HttpStatus.BAD_REQUEST.getCode(), PetProblemCode.InvalidName)
    .withInfo("name", suppliedName)
    .withInfo("reason", "must be lowercase")
    .toThrowable();

The info map is serialized as part of the response body, so callers can act on it programmatically.