The task: Instead of receiving general HTTP 500 Internal Server Error
in my stacktrace and the same horrible stacktrace on the client side I want to see my customized message with another statuscode (403
for example), that it will be much clearer for the developer, what has happend. And add some message to User about the Exception.
Here are couple of changed classes from my application:
SERVER PART:
AppException.class
- all my Server Response exceptions (before giving back to client) I want to transform into this exception. Kinda standard entity class
public class AppException extends WebApplicationException {
Integer status;
/** application specific error code */
int code;
/** link documenting the exception */
String link;
/** detailed error description for developers */
String developerMessage;
public AppException(int status, int code, String message, String developerMessage, String link) {
super(message);
this.status = status;
this.code = code;
this.developerMessage = developerMessage;
this.link = link;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDeveloperMessage() {
return developerMessage;
}
public void setDeveloperMessage(String developerMessage) {
this.developerMessage = developerMessage;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public AppException() {
}
public AppException(String message) {
super("Something went wrong on the server");
}
}
ÀppExceptionMapper.class
- mapps my AppException to the JAX-RS Runtime, instead standard exception, client receives AppException.
@Provider
public class AppExceptionMapper implements ExceptionMapper<AppException> {
@Override
public Response toResponse(AppException exception) {
return Response.status(403)
.entity("toResponse entity").type("text/plain").build();
}
}
ApplicationService.class
- my Service class that throws AppException
@Path("/applications")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface ApplicationService {
@DELETE
@Path("/deleteById")
void deleteById(@NotNull Long id) throws AppException;
}
CLIENT PART:
ErrorHandlingFilter.class
- my Response catcher of the AppException. Here I want to transform each Response exception to another exception depending on the status.
@Provider
public class ErrorHandlingFilter implements ClientResponseFilter {
private static ObjectMapper _MAPPER = new ObjectMapper();
@Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
if (responseContext.getStatus() != Response.Status.OK.getStatusCode()) {
if(responseContext.hasEntity()) {
Error error = _MAPPER.readValue(responseContext.getEntityStream(), Error.class);
String message = error.getMessage();
Response.Status status = Response.Status.fromStatusCode(responseContext.getStatus());
AppException clientException;
switch (status) {
case INTERNAL_SERVER_ERROR:
clientException = new PermissionException(message);
break;
case NOT_FOUND:
clientException = new MyNotFoundException(message);
break;
default:
clientException = new WhatEverException(message);
}
throw clientException;
}
}
}
}
PermissionException.class
- exception in what I want to transform AppException, if it came with 500 status code.
public class PermissionException extends AppException{
public PermissionException(String message) {
super("403 - Forbidden. You dont have enough rights to delete this Application");
}
Integer status;
/** application specific error code */
int code;
/** link documenting the exception */
String link;
/** detailed error description for developers */
String developerMessage;
public PermissionException(int status, int code, String message, String developerMessage, String link) {
super(message);
this.status = status;
this.code = code;
this.developerMessage = developerMessage;
this.link = link;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDeveloperMessage() {
return developerMessage;
}
public void setDeveloperMessage(String developerMessage) {
this.developerMessage = developerMessage;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public PermissionException() {}
}
ApplicationPresenter.class
- piece of UI logic, where I want something to do with PermissionException thrown by the ErrorHandlingFilter.
@SpringPresenter
public class ApplicationPresenter implements ApplicationView.Observer {
@Resource
private ApplicationService applicationService;
@Resource
private UiEnvironment uiEnvironment;
@Override
public void deleteSelectedApplication(BeanItemGrid<Application> applicationGrid) {
try {
applicationService.deleteById(applicationGrid.getSelectedItem().getId());
} catch (PermissionException e) {
e.printStackTrace();
e.getMessage();
} catch (AppException e2) {
}
}
}
How can I resolve my problem? I am still receiving standard 500 InternalErrorException.
UPDATED ALMOST THE WHOLE QUESTION ONE MORE TIME!
Suggested correctly above, ideal practice is to let the framework catch the Exception for you now that you have implemented an
ExceptionMapper
. However, one important point overviewing the phenomena which you are executing: if you need to handle any uncaught exceptions, you need to have anException
class implementingExceptionMapper
which maps toThrowable
Assuming your class
WhatEverException
caters to that. If not, then its a good practice to implementWhen you have an ExceptionMapper, you don't catch the exception yourself, but have the framework catch it, when the resource method is invoked on an HTTP request.
I have a different approach here. You can try this when starting your jetty server in the main java method
So you can get an output like this
You can Reference from here
The proper way to perform error handling is by registering
ExceptionMapper
instances that know what response should be returned in case of specific (or generic) exception.Please take a look at my other answer for more details: https://stackoverflow.com/a/23858695/2588800
This is a Jersey example, but you can extract the needed informations from here. I would only throw an exception and map this exception to any wanted response in the end.
Lets assume you have following ressource method, thowing the exception:
Create your exception class:
Now add your exception mapper:
In the end you have to register your exception mapper, so it can be used in your application. Here is some pseudo-code: