Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception Handling Section #137

Open
wants to merge 31 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ccde39
Create generic exception mapper
Jul 27, 2020
17cb996
Exception mapper for NotFoundException
Jul 27, 2020
2cea4f1
Exception handling section in README
Jul 27, 2020
4ef5b9c
Change status code to int
Jul 27, 2020
f1ba9ff
README updates
Jul 27, 2020
f50926b
Add file hotspot
Jul 27, 2020
dfe2460
Add gitkeep
Aug 31, 2020
d3c23b4
Merge branch 'master' of ssh://github.com/openliberty/guide-rest-intr…
Aug 31, 2020
774dcc9
Add hotspot
Aug 31, 2020
8142f90
Fix comment
Aug 31, 2020
6eb52be
Fix hotspot
Aug 31, 2020
2875ad2
Add file numbers to code commands
Aug 31, 2020
9c46dc4
Merge branch 'qa' into exception-handling
gkwan-ibm Oct 7, 2020
281bdae
gitignore update
Oct 8, 2020
a0bf96d
Suggested README changes made
Oct 8, 2020
5d55c19
Add endpoint to get individual properties to show
Oct 8, 2020
a0b98f1
README updates
Oct 8, 2020
4f64414
Fix class names in README
Oct 9, 2020
91d1901
Fix line too long for site
Oct 9, 2020
f6ad019
Mention creation of `PropertyNotFoundException.java`
Oct 9, 2020
8703afb
Fix file name in README
Oct 9, 2020
64c991f
Fix line too long for site
Oct 9, 2020
f622c39
Hotspot fixes
Oct 9, 2020
bd43aa4
Hotspot fix
Oct 9, 2020
ed6c2ae
Change output to JSON
Oct 9, 2020
2cf7c12
Merge branch 'qa' into exception-handling
gkwan-ibm Dec 10, 2020
ab34acb
Merge branch 'qa' into exception-handling
gkwan-ibm Dec 11, 2020
1eeb4fc
Merge branch 'qa' into exception-handling
gkwan-ibm Jan 14, 2021
a1e13a1
Merge branch 'qa' into exception-handling
gkwan-ibm Apr 1, 2021
e79e851
Merge branch 'prod' into exception-handling
gkwan-ibm Jun 25, 2021
594b666
Merge branch 'staging' into exception-handling
gkwan-ibm Jun 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 62 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,68 @@ include::{common-includes}/devmode-build.adoc[]
Check out the service that you created at the
http://localhost:9080/LibertyProject/System/properties[^] URL.

// =================================================================================================
// Exception Handling
// =================================================================================================

== Exception Handling

If a resource doesn't exist or the service experiences an exception, the client will receive either an empty response or an HTML error page respectively.
A client should not receive either of these.
Clients should receive JSON responses.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rephrase this section. I'm not sure that the client will always receive an empty response or an HTML error page. And ideally, the client would receive the error response in the same MediaType (MIME type) that they would receive a normal response in (i.e. it could be text, xml, etc.).

Maybe something like this:

Most application developers will want to ensure that when an exception occurs in the application (such as the client requesting a resource that does not exist or posting an invalid resource, etc.) that it is handled consistently and in a way that allows the client to fully understand the failure (i.e. bad input, server outage, etc.). This can be accomplished using the exception handling mechanisms in JAX-RS such as sending a Response via a WebApplicationException (or subclass) or by using ExceptionMapper providers.


This will be handled using an `ExceptionMapper` which maps exceptions to responses and builds appropriate JSON payloads.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This will be handled using an `ExceptionMapper` which maps exceptions to responses and builds appropriate JSON payloads.
The following example uses an `ExceptionMapper` which maps exceptions to responses and builds appropriate JSON payloads.


[role="code_command hotspot file=0", subs="quotes"]
----
#Create the `ErrorResponse` class.#
`src/main/java/io/openliberty/guides/rest/exceptions/ErrorResponse.java`
----

ErrorResponse.java
[source, Java, linenums, role="code_column hide_tags=comment"]
----
include::finish/src/main/java/io/openliberty/guides/rest/exceptions/ErrorResponse.java[]
----

The [hotspot file=0]`ErrorResponse` class will define the JSON contents of our response.
It contains fields for the error code and error message.

[role="code_command hotspot file=1", subs="quotes"]
----
#Create the `GenericExceptionMapper` class.#
`src/main/java/io/openliberty/guides/rest/exceptions/GenericExceptionMapper.java`
----

GenericExceptionMapper.java
[source, Java, linenums, role="code_column hide_tags=comment"]
----
include::finish/src/main/java/io/openliberty/guides/rest/exceptions/GenericExceptionMapper.java[]
----

When JAX-RS sees any `Throwable`, it will search for a matching `ExceptionMapper` interface that has been marked with the [hotspot=provider file=1]`@Provider` annotation.
It will pass the `Throwable` to the [hotspot=toResponse file=1]`toResponse()` method to construct a response using the [hotspot file=0]`ErrorResponse` class.

The [hotspot file=1]`GenericExceptionMapper` class will be used for any `Throwable` that does not have a more specific `ExceptionMapper` interface implemented.

[role="code_command hotspot file=2", subs="quotes"]
----
#Create the `NotFoundExceptionMapper` class.#
`src/main/java/io/openliberty/guides/rest/exceptions/NotFoundExceptionMapper.java`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`src/main/java/io/openliberty/guides/rest/exceptions/NotFoundExceptionMapper.java`
`src/main/java/io/openliberty/guides/rest/exceptions/PropertyNotFoundExceptionMapper.java`

----

NotFoundExceptionMapper.java
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed in slack that it might be better to use a different exception type here - ideally an application-specific, business exception (like WidgetNotFoundException) since NotFoundException is a subclass of WebApplicationException - and it could hold it's own Response object (and thus doesn't need to be mapped).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NotFoundExceptionMapper.java
PropertyNotFoundExceptionMapper.java

[source, Java, linenums, role="code_column hide_tags=comment"]
----
include::finish/src/main/java/io/openliberty/guides/rest/exceptions/NotFoundExceptionMapper.java[]
----

The [hotspot file=2]`NotFoundExceptionMapper` class is a more specific implementation of the `ExceptionMapper` interface.
The type of the generic inside is now [hotspot=generic file=2]`NotFoundException` instead of `Throwable`.
It will only map to instances of `NotFoundException` that the application throws.

See this error by visiting a URL for a resource that doesn't exist such as http://localhost:9080/LibertyProject/System/DoesNotExist[^].

// =================================================================================================
// Testing the service
// =================================================================================================
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.openliberty.guides.rest.exceptions;

public class ErrorResponse {
int errorCode;
String errorMessage;

public ErrorResponse(int errorCode, String errorMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}

public int getErrorCode() {
return this.errorCode;
}

public String getErrorMessage() {
return this.errorMessage;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.openliberty.guides.rest.exceptions;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

// tag::provider[]
@Provider
// end::provider[]
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

// tag::toResponse[]
@Override
public Response toResponse(Throwable t) {
ErrorResponse response = new ErrorResponse(500, t.getMessage());
return Response.serverError()
.entity(response)
.type(MediaType.APPLICATION_JSON)
.build();
}
// end::toResponse[]

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.openliberty.guides.rest.exceptions;

import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
// tag::generic[]
public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
// end::generic[]

@Override
public Response toResponse(NotFoundException ex) {
ErrorResponse response = new ErrorResponse(404, ex.getMessage());
return Response.status(Response.Status.NOT_FOUND)
.entity(response)
.type(MediaType.APPLICATION_JSON)
.build();
}

}
Empty file.