This guide proved examples for using the Auxiliaries for defining Error Responses.
Implementation Tips
The Error Responses defining can be improved via some implementation tips as follow:
Example: Error Handling Using the Error Description Utils
The following example demonstrates error handling using the ErrorDescriptionUtils:
@Path("/demo/bookstore/books")
public class BookStoreService {
/**
* Retrieves a {@link BookResource} representation of book with identifier {@code bookId}. The {@link BookResource}
* representation shall include all of the mandatory and any of the optional fields annotated with {@code GET}.
.....
* Possible HTTP error codes:
* <ul>
* <ol>
* {@link HttpConnection#HTTP_BAD_REQUEST} with application error codes
* {@link GeneralErrorCodes#INVALID_REPRESENTATION_CONSTRAINTS_SYNTAX},
* {@link BookStoreErrorCodes#ILLEGAL_BOOK_IDENTIFIER}
* </ol>
* <ol>
* {@link HttpConnection#HTTP_NOT_FOUND} with application error codes {@link BookStoreErrorCodes#BOOK_NOT_FOUND}
* </ol>
* <ol>
* {@link HttpConnection#HTTP_FORBIDDEN} with application error code
* {@link GeneralErrorCodes#INSUFFICIENT_PERMISSIONS}
* </ol>
* </ul>
.....
*/
@GET
@Path("{bookId}")
public Response getBook(@PathParam("bookId") long bookId) {
// get book, check if exists
Book book = findBook(bookId);
// initialize book resource fields, build response
return Response.ok(BookResourceFactory.getBookResource(book, constraints)).build();
}
// validate the provided book identifier and return appropriate error if identifier is invalid
private Book findBook(long bookId) {
if (bookId < 0) {
ErrorDescriptionUtils.throwWebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST,
BookStoreErrorCodes.ILLEGAL_BOOK_IDENTIFIER, "Book identifier is incorrect: " + bookId,
"Book identifier must be greater than 0!", BookResourceFields.BOOK_ID);
return null;
}
Book book = bookStore.get(bookId);
if (book == null) {
ErrorDescriptionUtils.throwWebApplicationException(HttpURLConnection.HTTP_NOT_FOUND,
BookStoreErrorCodes.BOOK_NOT_FOUND, "Book with identifier: " + bookId + " does not exist!",
"Book with such identifier was not found. Try another!", BookResourceFields.BOOK_ID);
return null;
}
return book;
}
.....
}
A sample error response reporting a book not found error containing an ErrorDescription representation in JSON:
GET /demo/bookstore/books/6 HTTP/1.1
Content-Type: application/json
Accept: application/json
Authorization: Basic YWRtaW46YWRtaW4=
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"status": 404,
"code": 40010,
"description": "Book with identifier: 6 does not exist!",
"hint": "Book with such identifier was not found. Try another!",
"source": "id"
}
A sample error response reporting an invalid book identifier error containing an ErrorDescription representation in JSON:
GET /demo/bookstore/books/-1 HTTP/1.1
Content-Type: application/json
Accept: application/json
Authorization: Basic YWRtaW46YWRtaW4=
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"status": 400,
"code": 40020,
"description": "Book identifier is incorrect: -1",
"hint": "Book identifier must be greater than 0!",
"source": "id"
}
Example: Error Handling Using the Error Description Utils for Composite Errors
The following example demonstrates error handling using the ErrorDescriptionUtils for generating composite errors:
@Path("/demo/bookstore/books")
public class BookStoreService {
/**
* Updates the book with identifier {@code bookId} based on the new data included in the {@link BookResource}
* representation transferred by the client. The {@link BookResource} representation may include all of the mandatory
* and any of the optional fields annotated with {@code PUT}.
.....
* <ul>
* <ol>
* {@link HttpURLConnection#HTTP_BAD_REQUEST} with application error codes
* {@link BookStoreErrorCodes#ILLEGAL_BOOK_RESOURCE_REPRESENTATION},
* {@link BookStoreErrorCodes#ILLEGAL_BOOK_IDENTIFIER}, {@link BookStoreErrorCodes#ILLEGAL_BOOK_TITLE},
* {@link BookStoreErrorCodes#ILLEGAL_BOOK_AUTHOR}, {@link BookStoreErrorCodes#ILLEGAL_BOOK_COUNT} or
* {@link GeneralErrorCodes#COMPOSITE_ERROR}
* </ol>
* <ol>
* {@link HttpURLConnection#HTTP_NOT_FOUND} with application error codes {@link BookStoreErrorCodes#BOOK_NOT_FOUND}
* </ol>
* <ol>
* {@link HttpURLConnection#HTTP_FORBIDDEN} with application error code
* {@link GeneralErrorCodes#INSUFFICIENT_PERMISSIONS}
* </ol>
* </ul>
.....
*/
@PUT
@Path("{bookId}")
public Response updateBook(@PathParam("bookId") long bookId, BookResource book) {
// get book, check if exists
Book storedBook = findBook(bookId);
// validate the book resource provided for update
validateBookResource(book);
// update the stored book based on the provided fields for update
if (book.getAuthor() != null) {
storedBook.setAuthor(book.getAuthor());
}
if (book.getTitle() != null) {
storedBook.setTitle(book.getTitle());
}
if (book.getCount() != null) {
storedBook.setCount(book.getCount());
}
// reuse the provided book resource instance, reinit the fields and build response
return Response.ok(BookResourceFactory.reinitBookResource(storedBook, book, constraints)).build();
}
// validate the provided book resource and return appropriate errors
private void validateBookResource(BookResource book) {
if (book == null) {
ErrorDescriptionUtils.throwWebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST,
BookStoreErrorCodes.ILLEGAL_BOOK_RESOURCE_REPRESENTATION, "Book resource representation is missing!",
"Book resource representation must be provided!", null);
} else {
List<ErrorDescription> errors = new LinkedList<ErrorDescription>();
// validate the book title
String title = book.getTitle();
if (title != null && title.trim().isEmpty()) {
errors.add(ErrorDescriptionUtils.getErrorDescription(HttpURLConnection.HTTP_BAD_REQUEST,
BookStoreErrorCodes.ILLEGAL_BOOK_TITLE, "Book title is missing or empty string!",
"Book title must be valid string!", BookResourceFields.TITLE));
}
// validate the book author
String author = book.getAuthor();
if (author != null && author.trim().isEmpty()) {
errors.add(ErrorDescriptionUtils.getErrorDescription(HttpURLConnection.HTTP_BAD_REQUEST,
BookStoreErrorCodes.ILLEGAL_BOOK_AUTHOR, "Book author is missing or empty string!",
"Book author must be valid string!", BookResourceFields.AUTHOR));
}
// validate the books count
Integer count = book.getCount();
if (count != null && count < 0) {
errors.add(ErrorDescriptionUtils.getErrorDescription(HttpURLConnection.HTTP_BAD_REQUEST,
BookStoreErrorCodes.ILLEGAL_BOOK_COUNT, "Book count is missing or negative!",
"Book count must be valid non negative integer!", BookResourceFields.COUNT));
}
// flatten the collection and throw WebApplicationException
throwWebApplicationException(errors);
}
}
private void throwWebApplicationException(List<ErrorDescription> errors) {
if (errors.size() > 0) {
if (errors.size() == 1) {
ErrorDescriptionUtils.throwWebApplicationException(errors.get(0));
} else {
ErrorDescriptionUtils.throwWebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST,
"Multiple validation errors occured while processing the book resource representation!",
"Please see the nested error descriptions for more details.",
null, errors);
}
}
}
.....
}
A sample error response reporting a composite validation error containing an ErrorDescription representation in JSON:
GET /demo/bookstore/books/6 HTTP/1.1
Content-Type: application/json
Accept: application/json
Authorization: Basic YWRtaW46YWRtaW4=
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"status": 400,
"code": 50010,
"description": "Multiple validation errors occured while processing the book resource representation!",
"hint": "Please see the nested error descriptions for more details.",
"errors": [
{
"status": 400,
"code": 40031,
"description": "Book title is missing or empty string!",
"hint": "Book title must be valid string!",
"source": "title"
},
{
"status": 400,
"code": 40032,
"description": "Book author is missing or empty string!",
"hint": "Book author must be valid string!",
"source": "author"
},
{
"status": 400,
"code": 40033,
"description": "Book count is missing or negative!",
"hint": "Book count must be valid non negative integer!",
"source": "count"
}
]
}