Backstopper is a framework-agnostic API error handling and (optional) model validation solution for Java 17 and greater.
(NOTE: The Backstopper 1.x branch contains a version of
Backstopper for Java 7+, and for the javax ecosystem. The current Backstopper supports Java 17+ and the jakarta
ecosystem. The Backstopper 1.x releases also contain support for Spring 4 and 5, and Springboot 1 and 2.)
This submodule contains a sample application based on Spring Boot 3 + Web MVC (Servlet) that fully integrates Backstopper.
- Build the sample by running the
./buildSample.shscript. - Launch the sample by running the
./runSample.shscript. It will bind to port 8080 by default.- You can override the default port by passing in a system property to the run script,
e.g. to bind to port 8181:
./runSample.sh -Dserver.port=8181
- You can override the default port by passing in a system property to the run script,
e.g. to bind to port 8181:
All examples here assume the sample app is running on port 8080, so you would hit each path by going to
http://localhost:8080/[endpoint-path]. It's recommended that you use a REST client like
Postman for making the requests so you can easily specify HTTP method, payloads,
headers, etc, and fully inspect the response.
Also note that all the following things to try are verified in a component test:
VerifyExpectedErrorsAreReturnedComponentTest. If you prefer to experiment via code you can run, debug, and otherwise
explore that test.
As you are doing the following you should check the logs that are output by the sample application and notice what is
included in the log messages. In particular notice how you can search for an error_id that came from an error
response and go directly to the relevant log message in the logs. Also notice how the ApiError.getName() value shows
up in the logs for each error represented in a returned error contract (there can be more than one per request).
GET /sample- Returns the JSON serialization for theSampleModelmodel object. You can copy this into aPOSTcall to experiment with triggering errors.POST /samplewithContent-Type: application/jsonheader - Using the JSON model retrieved by theGETcall, you can trigger numerous different types of errors, all of which get caught by the Backstopper system and converted into the appropriate error contract.- Omit the
foofield. - Set the value of the
range_0_to_42field to something outside of the allowed 0-42 range. - Set the value of the
rgb_colorfield to something besidesRED,GREEN, orBLUE, or omit it entirely. Note that the validation and deserialization of this enum field is done in a case insensitive manner - i.e. you can passred,Green, orbLuEif you want and it will not throw an error. - Set two or more invalid values for
foo,range_0_to_42, andrgb_colorto invalid values all at once - notice you get back all relevant errors at once in the same error contract. - Set
throw_manual_errorto true to trigger a manual exception to be thrown inside the normalPOST /sampleendpoint.- Note the extra response headers that are included when you do this, and how they relate to the
.withExtraResponseHeaders(...)method call on the builder of the exception that is thrown.
- Note the extra response headers that are included when you do this, and how they relate to the
- Pass in an empty JSON payload - you should receive a
"Missing expected content"error back. - Pass in a junk payload that is not valid JSON - you should receive a
"Malformed request"error back.
- Omit the
GET /sample/coreErrorWrapper- Triggers an error to be thrown that appears to the caller like a normal generic service exception, but theSOME_MEANINGFUL_ERROR_NAMEname from theApiErrorit represents shows up in the logs to help you disambiguate what the true cause was.GET /sample/triggerUnhandledError- Triggers an error that is caught by the unhandled exception handler portion of Backstopper and converted to a generic service exception.GET /sample/withRequiredQueryParam?requiredQueryParamValue=not-an-int- Triggers an error in the Spring Boot framework when it cannot coerce the query param value to the required type (an integer), which results in a Backstopper"Type conversion error".GET /sample/withRequiredHeaderwith arequiredHeaderValue: not-an-intheader - Similar to the query param example, this triggers an error in the Spring Boot framework when it cannot coerce the header value to the required type (an integer), which results in a Backstopper"Type conversion error".GET /does-not-exist- Triggers a framework 404 which Backstopper handles.DELETE /sample- Triggers a framework 405 which Backstopper handles.GET /samplewithAccept: application/octet-streamheader - Triggers a framework 406 which Backstopper handles.POST /samplewithContent-Type: text/plain- Triggers a framework 415 which Backstopper handles.- Any request with a
throw-servlet-filter-exceptionheader set totrue. This will trigger an exception in a Servlet filter before the request ever hits Spring. TheBackstopperSpringboot3ContainerErrorControllerfrom thebackstopper-spring-boot3-webmvcdependency registers itself to handle these non-Spring container errors, where it is passed off to Backstopper.
See the base project README.md, User Guide, and Backstopper repository source code and javadocs for all further information.
Backstopper is released under the Apache License, Version 2.0