If you have docker installed, then simply run the application in docker profile by passing spring.profiles.active=docker
as run time argument from your IDE.
Depending on your current working directory in IDE, you may need to change spring.docker.compose.file=problem-handler-webflux-demo/compose.yml
to spring.docker.compose.file=compose.yml in application-docker.properties
Make sure the host ports mapped in Docker compose file are available or change the ports and
do the respective changes in database configurations application-docker.properties
Update following properties with your PostgresDB configurations
spring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhost:5432/problem_web_db}
spring.datasource.username=${POSTGRES_USER:postgres}
spring.datasource.password=${POSTGRES_PASSWORD:admin}Update following properties with your MongoDB configurations
spring.data.mongodb.uri=${MONGODB_URL:mongodb://localhost:27017/problem_web_db}Run the main class ProblemWebDemoApplication
and access Swagger Swagger at http://localhost:8080/swagger-ui.html
Select Application from dropdown Select a definition
- State management APIs are using MongoDB, to test database constraint violations.
- Employee management APIs are using PostgresDB, to test database constraint violations.
- Pet management APIs are validated by OpenAPI Specification.
- Problem Demo APIs throws exceptions explicitly.
Have a look at
DemoProblemController - State and Employee management APIs are secured, so need to pass a JWT token in
Authorizationheader. See the lock symbol against the API in Swagger
Click on Authorize button to pass the JWT Token. Use any valid JWT Token.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- Call the APIs providing invalid inputs to make it throw exception and have a look at response.
- Set
problem.debug-enabled=trueinapplication.propertiesto get the message resolvers and set the messages inerrors.propertiesto customize the error response attributes in response. - Test with setting
problem.stacktrace-enabled=trueandproblem.cause-chains-enabled=trueinapplication.propertiesto get Stacktrace and Cause in response. - Update
help.htmlwith any custom error description and follow thetypeurl in error response to see the error description on help page. - Follow http://localhost:8080/problems/help.html to see the description of errors.
Following are example error responses in different scenarios.
The error response attributes code, title and detail can be customized for each error by specifying
the same in errors.properties file for different error keys which you can get by setting problem.debug-enabled=true in application.properties file
Most common type of errors an application must handle
Code
@Valid
@Getter
@Setter
public static final class UserRequest {
@Size(min = 3, max = 10)
private String name;
@Size(min = 2, max = 5)
private String designation;
@NotNull
@Valid
private Address address;
}
@Getter
@Setter
@Valid
public static final class Address {
@NotEmpty
private String city;
@NotEmpty
private String state;
}Request
curl -X 'POST' \
'http://localhost:8080/problems/handler-constraint-violation' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '{
"name": "a",
"designation": "aaaaaaaaaaaaaaaaa",
"address": {
"city": "string"
}
}'
Response
{
"type": "http://localhost:8080/problems/help.html#constraint-violations",
"title": "Bad Request",
"status": 400,
"detail": "Constraint violations has happened, please correct the request and try again",
"instance": "/problems/handler-constraint-violation",
"method": "POST",
"timestamp": "2023-10-29T16:41:59.876471+05:30",
"code": "constraint-violations",
"violations": [
{
"code": "400",
"detail": "User name length should be between 3 and 10",
"propertyPath": "name"
},
{
"code": "400",
"detail": "Address state name is required",
"propertyPath": "address.state"
},
{
"code": "400",
"detail": "User designation length should be between 2 and 5",
"propertyPath": "designation"
}
]
}Make following request two time, 2nd time the exception will be thrown.
Request
curl -X 'POST' \
'http://localhost:8080/api/employees' \
-H 'accept: */*' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' \
-H 'Content-Type: application/json' \
-d '{
"name": "John Rambo",
"dob": "1983-06-06"
}'
Response
{
"type": "http://localhost:8080/problems/help.html#500",
"title": "Internal Server Error",
"status": 500,
"detail": "Employee name must be unique, a record with given name already exists",
"instance": "/api/employees",
"method": "POST",
"timestamp": "2023-10-29T16:44:10.917194+05:30",
"code": "500"
}Make following request two time, 2nd time the exception will be thrown.
Request
curl -X 'POST' \
'http://localhost:8080/api/states' \
-H 'accept: */*' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' \
-H 'Content-Type: application/json' \
-d '{
"code": "HR",
"name": "Haryana",
"gstCode": "6"
}'
Response
{
"type": "http://localhost:8080/problems/help.html#500",
"title": "Internal Server Error",
"status": 500,
"detail": "State name must be unique",
"instance": "/api/states",
"method": "POST",
"timestamp": "2023-10-29T16:44:44.806613+05:30",
"code": "500"
}Request
curl -X 'GET' \
'http://localhost:8080/problems/handler-invalid-query-strings?page=-1&size=1' \
-H 'accept: */*'
Response
{
"type": "http://localhost:8080/problems/help.html#constraint-violations",
"title": "Bad Request",
"status": 400,
"detail": "Constraint violations has happened, please correct the request and try again",
"instance": "/problems/handler-invalid-query-strings",
"method": "GET",
"timestamp": "2023-10-29T14:51:37.889537+05:30",
"code": "constraint-violations",
"violations": [
{
"code": "400",
"detail": "must be greater than or equal to 0",
"propertyPath": "page"
}
]
}Request
curl -X 'GET' \
'http://localhost:8080/problems/handler-datetime-conversion?dateTime=2023-13-18T10%3A12%3A12Z' \
-H 'accept: */*'
Response
{
"type": "http://localhost:8080/problems/help.html#400",
"title": "Bad Request",
"status": 400,
"detail": "Invalid date time value or format. Expected a valid date time in ISO format",
"instance": "/problems/handler-datetime-conversion",
"method": "GET",
"timestamp": "2023-10-29T16:05:09.953099+05:30",
"code": "400",
"propertyPath": "dateTime"
}Request
curl -X 'POST' \
'http://localhost:8080/problems/uploadfile' \
-H 'accept: */*' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@Large_File.pdf;type=application/pdf'
Response
{
"type": "http://localhost:8080/problems/help.html#400",
"title": "Bad Request",
"status": 400,
"detail": "Upload file size exceeded the maximum allowed limit: 10485760B",
"instance": "/problems/uploadfile",
"method": "POST",
"timestamp": "2023-10-29T14:31:33.073971+05:30",
"code": "400"
}Request
curl -X 'POST' \
'http://localhost:8080/problems/handler-json-body' \
-H 'accept: */*' \
-H 'Content-Type: application/xml' \
-d '{
"test": "string"
}'
Response
{
"type": "http://localhost:8080/problems/help.html#415",
"title": "Unsupported Media Type",
"status": 415,
"detail": "Media Type: application/xml Not Acceptable, Supported Media Types are: application/json",
"instance": "/problems/handler-json-body",
"method": "POST",
"timestamp": "2023-10-29T14:45:47.467268+05:30",
"code": "415"
}Request
curl -X 'POST' \
'http://localhost:8080/problems/handler-datetime-conversion?dateTime=2023-04-18T10%3A12%3A12Z' \
-H 'accept: */*'
Response
{
"type": "http://localhost:8080/problems/help.html#405",
"title": "Method Not Allowed",
"status": 405,
"detail": "Requested Method: POST not allowed, allowed methods are: GET",
"instance": "/problems/handler-datetime-conversion",
"method": "POST",
"timestamp": "2023-10-29T16:15:08.916369+05:30",
"code": "405"
}Code
throw new IllegalArgumentException("Expected argument invalid", new IllegalStateException("Dummy cause"));Request
curl -X 'GET' \
'http://localhost:8080/problems/handler-throwable' \
-H 'accept: */*'
Response
{
"type": "http://localhost:8080/problems/help.html#500",
"title": "Internal Server Error",
"status": 500,
"detail": "Expected argument invalid",
"instance": "/problems/handler-throwable",
"method": "GET",
"timestamp": "2023-10-29T14:49:40.998497+05:30",
"code": "500"
}Code
Problem problem = Problems.newInstance("3456", "Bad Request", "Invalid request received, Please retry with correct input")
.parameter("additional-attribute", "Some additional attribute").build();
throw Problems.throwAble(HttpStatus.BAD_REQUEST, problem);Request
curl -X 'GET' \
'http://localhost:8080/problems/throw-problem-with-additional-attribute' \
-H 'accept: */*'
Response
{
"type": "http://localhost:8080/problems/help.html#3456",
"title": "Bad Request",
"status": 400,
"detail": "Invalid request received, Please retry with correct input",
"instance": "/problems/throw-problem-with-additional-attribute",
"method": "GET",
"timestamp": "2023-10-29T16:24:37.976724+05:30",
"code": "3456",
"additional-attribute": "Some additional attribute"
}Code
ApplicationException problemOne = Problems.newInstance("sample.problem.one").throwAbleChecked();
ApplicationProblem problemTwo = Problems.newInstance(AppErrors.REMOTE_HOST_NOT_AVAILABLE).detailArgs("http://some.remote.host.com").throwAble();
MultiProblem problems = Problems.ofExceptions(HttpStatus.MULTI_STATUS, problemOne, problemTwo);
Problem problemThree = Problems.newInstance("3456", "Bad Request", "Invalid request received, Please retry with correct input")
.parameter("additional-attribute", "Some additional attribute").build();
problems.add(problemThree);
Exception exception = new IllegalStateException("Just for testing exception");
problems.add(exception);
Problem problem = Problems.newInstance("111", "Dummy", "Hardcode attributes broblem").build();
problems.add(problem);
throw problems;Request
curl -X 'GET' \
'http://localhost:8080/problems/throw-multiple-problems' \
-H 'accept: */*'
Response
{
"type": "http://localhost:8080/problems/help.html#207",
"title": "Multi-Status",
"status": 207,
"detail": "Multi-Status",
"instance": "/problems/throw-multiple-problems",
"method": "GET",
"timestamp": "2023-10-29T16:22:53.363785+05:30",
"code": "207",
"errors": [
{
"code": "500",
"title": "Internal Server Error",
"detail": "Sample error message defined in 'errors.properties'"
},
{
"code": "503",
"title": "Service Unavailable",
"detail": "Looks like something wrong with remote host: http://some.remote.host.com"
},
{
"code": "3456",
"title": "Bad Request",
"detail": "Invalid request received, Please retry with correct input",
"additional-attribute": "Some additional attribute"
},
{
"code": "500",
"title": "Internal Server Error",
"detail": "Just for testing exception"
},
{
"code": "111",
"title": "Dummy",
"detail": "Hardcode attributes broblem"
}
]
}Request
curl -X 'POST' \
'http://localhost:8080/api/pets' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '{
"id": 0,
"name": "string",
"category": "string",
"tags": [
"string"
],
"status": "AVAILABLE"
}'
Response
{
"type": "http://localhost:8080/problems/help.html#constraint-violations",
"title": "Bad Request",
"status": 400,
"detail": "Constraint violations has happened, please correct the request and try again",
"instance": "/api/pets",
"method": "POST",
"timestamp": "2023-10-29T16:06:18.335463+05:30",
"code": "constraint-violations",
"violations": [
{
"code": "400",
"detail": "[Path '/id'] Numeric instance is lower than the required minimum (minimum: 1, found: 0)",
"propertyPath": "id"
}
]
}Request
curl -X 'GET' \
'http://localhost:8080/api/employees/1' \
-H 'accept: */*'
Response
{
"type": "http://localhost:8080/problems/help.html#401",
"title": "Unauthorized",
"status": 401,
"detail": "Either Authorization header bearer token is missing or invalid",
"instance": "/api/employees/1",
"method": "GET",
"timestamp": "2023-10-29T16:08:40.466566+05:30",
"code": "401"
}