We are building a Java SDK to simplify the access to one of our services that provide a REST API. This SDK is to be used by 3rd-party developers. I am struggling to find the best pattern to implement the error handling in the SDK that better fits the Java language.
Let's say we have the rest endpoint: GET /photos/{photoId}.
This may return the following HTTP status codes:
401 : The user is not authenticated
403 : The user does not have permission to access this photo
404 : There's no photo with that id
The service looks something like this:
interface RestService {
public Photo getPhoto(String photoID);
}
In the code above I am not addressing the error handling yet. I obviously want to provide a way for the client of the sdk to know which error happened, to potentially recover from it. Error handling in Java is done using Exceptions, so let's go with that. However, what is the best way to do this using exceptions?
1. Have a single exception with information about the error.
public Photo getPhoto(String photoID) throws RestServiceException;
public class RestServiceException extends Exception {
int statusCode;
...
}
The client of the sdk could then do something like this:
try {
Photo photo = getPhoto("photo1");
}
catch(RestServiceException e) {
swtich(e.getStatusCode()) {
case 401 : handleUnauthenticated(); break;
case 403 : handleUnauthorized(); break;
case 404 : handleNotFound(); break;
}
}
However I don't really like this solution mainly for 2 reasons:
By looking at the method's signature the developer has no idea what kind of error situations he may need to handle.
The developer needs to deal directly with the HTTP status codes and know what they mean in the context of this method (obviously if they are correctly used, a lot of the times the meaning is known, however that may not always be the case).
2. Have a class hierarchy of errors
The method signature remains:
public Photo getPhoto(String photoID) throws RestServiceException;
But now we create exceptions for each error type:
public class UnauthenticatedException extends RestServiceException;
public class UnauthorizedException extends RestServiceException;
public class NotFoundException extends RestServiceException;
Now the client of the SDK could then do something like this:
try {
Photo photo = getPhoto("photo1");
}
catch(UnauthenticatedException e) {
handleUnauthorized();
}
catch(UnauthorizedException e) {
handleUnauthenticated();
}
catch(NotFoundException e) {
handleNotFound();
}
With this approach the developer does not need to know about the HTTP status codes that generated the errors, he only has to handle Java Exceptions. Another advantage is that the developer may only catch the exceptions he wants to handle (unlike the previous situation where it would have to catch the single Exception (RestServiceException) and only then decide if he wants to deal with it or not).
However, there's still one problem. By looking at the method's signature the developer still has no idea about the kind of errors he may need to handle because we only have the super class in the method's signature.
3. Have a class hierarchy of errors + list them in the method's signature
Ok, so what comes to mind now is to change the method's signature to:
public Photo getPhoto(String photoID) throws UnauthenticatedException, UnauthorizedException, NotFoundException;
However, it is possible that in the future new error situations could be added to this rest endpoint. That would mean adding a new Exception to the method's signature and that would be a breaking change to the java api. We would like to have a more robust solution that would not result in breaking changes to the api in the situation described.
4. Have a class hierarchy of errors (using Unchecked exceptions) + list them in the method's signature
So, what about Unchecked exceptions? If we change the RestServiceException to extend the RuntimeException:
public class RestServiceException extends RuntimeException
And we keep the method's signature:
public Photo getPhoto(String photoID) throws UnauthenticatedException, UnauthorizedException, NotFoundException;
This way I can add new exceptions to the method's signature without breaking existing code.
However, with this solution the developer is not forced to catch any exception and won't notice that there are error situations he needs to handle until he carefully reads the documentation (yeah, right!) or noticed the Exceptions that are in the method's signature.
What's the best practice for error handling in these kind of situations?
Are there other (better) alternatives to the ones I mentioned?
Exception handling alternatives: Callbacks
I don't know if it's a better alternative, but you could use callbacks. You can make some methods optional by providing a default implementation. Take a look to this:
/**
* Example 1.
* Some callbacks will be always executed even if they fail or
* not, all the request will finish.
* */
RestRequest request = RestRequest.get("http://myserver.com/photos/31",
Photo.class, new RestCallback(){
//I know that this error could be triggered, so I override the method.
#Override
public void onUnauthorized() {
//Handle this error, maybe pop up a login windows (?)
}
//I always must override this method.
#Override
public void onFinish () {
//Do some UI updates...
}
}).send();
This is how the callback class looks like:
public abstract class RestCallback {
public void onUnauthorized() {
//Override this method is optional.
}
public abstract void onFinish(); //Override this method is obligatory.
public void onError() {
//Override this method is optional.
}
public void onBadParamsError() {
//Override this method is optional.
}
}
Doing something like this you could define an request life-cycle, and manage every state of the request. You can make some methods optional to implement or not. You can get some general errors and give the chance at the user to implements the handling, like in the onError.
How can I define clearly what exceptions handle?
If you ask me, the best approach is draw the life-cycle of the request, something like this:
This is only a poor example, but the important it's keep in mind that all the methods implementation, could be or not, optionals. If onAuthenticationError is obligatory, not neccesarily the onBadUsername will be too, and viceversa. This is the point that makes this callbacks so flexible.
And how I implement the Http client?
Well I don't know much about http clients, I always use the apache HttpClient, but there's not a lot of differences between the http clients, the most have a little more or a little fewer features, but in the end, they are all just the same. Just pick up the http method, put the url, the params, and send. For this example I will use the apache HttpClient
public class RestRequest {
Gson gson = new Gson();
public <T> T post(String url, Class<T> clazz,
List<NameValuePair> parameters, RestCallback callback) {
// Create a new HttpClient and Post Header
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
try {
// Add your data
httppost.setEntity(new UrlEncodedFormEntity(parameters));
// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);
StringBuilder json = inputStreamToString(response.getEntity()
.getContent());
T gsonObject = gson.fromJson(json.toString(), clazz);
callback.onSuccess(); // Everything has gone OK
return gsonObject;
} catch (HttpResponseException e) {
// Here are the http error codes!
callback.onError();
switch (e.getStatusCode()) {
case 401:
callback.onAuthorizationError();
break;
case 403:
callback.onPermissionRefuse();
break;
case 404:
callback.onNonExistingPhoto();
break;
}
e.printStackTrace();
} catch (ConnectTimeoutException e) {
callback.onTimeOutError();
e.printStackTrace();
} catch (MalformedJsonException e) {
callback.onMalformedJson();
}
return null;
}
// Fast Implementation
private StringBuilder inputStreamToString(InputStream is)
throws IOException {
String line = "";
StringBuilder total = new StringBuilder();
// Wrap a BufferedReader around the InputStream
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
// Read response until the end
while ((line = rd.readLine()) != null) {
total.append(line);
}
// Return full string
return total;
}
}
This is an example implementation of the RestRequest. This is only one simple example, theres a lot of topics to discuss when you are making your own rest client. For example, "what kind of json library use to parse?", "are you working for android or for java?" (this is important because I don't know if android supports some features of java 7 like multi-catch exceptions, and there's some technologies that isn't availabe for java or android and viceversa).
But the best that I can say you is code the sdk api in terms of the user, note that the lines to make the rest request are few.
Hope this helps! Bye :]
It seems you are doing things by "hand".
I would recommend you0 give a try to Apache CXF.
It's a neat implementation the JAX-RS API that enables you to almost forget about REST. It plays well with (also recommended) Spring.
You simply write classes that implement your interfaces (API). What you need to do is to annotate the methods and parameters of your interfaces with JAX-RS annotations.
Then, CXF does the magic.
You throw normal Exceptions in your java code, and then use exception mapper on server/nd or client to translate between them and HTTP Status code.
This way, on server/Java client side, you only deal with regular 100% Java exception, and CXF handles the HTTP for you: You have both the benefits of a clear REST API and a Java Client ready to be used by your users.
The client can either be generated from your WDSL, or built at runtime from introspection of the interface annotations.
See :
http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Exceptionhandling
http://cxf.apache.org/docs/how-do-i-develop-a-client.html
In our application, we have defined and mapped a set of error codes and their counterpart Exceptions :
4XX Expected / Functional excecption (like bad arguments, empty sets, etc)
5XX Unexpected / Unrecovable RunTimeException for internal errors that "should not happen"
It follows both REST and Java standards.
I've seen libraries that combine your suggestions 2 and 3, e.g.
public Photo getPhoto(String photoID) throws RestServiceException, UnauthenticatedException, UnauthorizedException, NotFoundException;
This way, when you add a new checked exception that extends RestServiceException, you're not changing the method's contract and any code using it still compiles.
Compared to a callback or unchecked exception solution, an advantage is that this ensures your new error will be handled by the client code, even if it's only as a general error. In a callback, nothing would happen, and with an unchecked exception, your client application might crash.
The solution may vary depending on your needs.
If it is supposed that there could appear unpredictable new exception types in the future, your second solution with checked exception hierarchy and method that throw their superclass RestServiceException is the best one. All known subclasses should be listed in the javadoc like Subclasses: {#link UnauthenticatedException}, ..., to let developers know what kind of of exceptions there could hide. It should be noticed that if some method could throw only few exceptions from this list, they should be described in the javadoc of this method using #throws.
This solution is also applicable in the case when all appearances of RestServiceException means that any of it's subclasses could hide behind it. But in this case, if RestServiceException subclasses hasn't their specific fields and methods, your first solution is preferrable, but with some modifications:
public class RestServiceException extends Exception {
private final Type type;
public Type getType();
...
public static enum Type {
UNAUTHENTICATED,
UNAUTHORISED,
NOT_FOUND;
}
}
Also there is a good practice to create alternative method that will throw unchecked exception that wraps RestServiceException exeption itself for usage within ‘all-or-nothing’ business logic.
public Photo getPhotoUnchecked(String photoID) {
try {
return getPhoto(photoID);
catch (RestServiceException ex) {
throw new RestServiceUncheckedException(ex);
}
}
It all comes down to how informative your API error responses are. The more informative the error handling of the API is, the more informative the exception handling can be. I would believe the exceptions would only be as informative as the errors returned from the API.
Example:
{ "status":404,"code":2001,"message":"Photo could not be found."}
Following your first suggestion, if the Exception contained both the status and the API error code, the developer has a better understanding of what he needs to do and more option when it comes to exception handling. If the exception also contained the error message that was returned, as well, the developer shouldn't even need to reference the documentation.
Related
In my Java app, I have the following service method that calls another method and accumulate responses. Then returns these responses as a list. If there is not any exception, it works properly. However, it is possible to encounter exception for one of the call in the loop. In that case, it cannot return the previous responses retrieved until exception (if there are 10 process in the loop and there is an exception for the 6th process, then it cannot return the previous 5 responses added to the response list).
public List<CommandResponse> process(final UUID uuid) {
final Site site = siteRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException(SITE_ENTITY_NAME));
// code omitted for brevity
for (Type providerType : providerTypeList) {
// operations
responses.add(demoService.demoMethod());
}
return responses;
}
Under these conditions, I am wondering if I should use a try-catch mechanism or should I return response in the loop and finally return null. What would you suggest for this situations?
public CommandResponse operation(final UUID uuid) {
final Site site = siteRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException(SITE_ENTITY_NAME));
// code omitted for brevity
for (Type providerType : providerTypeList) {
// operations
return demoService.demoMethod();
}
return null;
}
Well, following the best practices the method demoMethod() should not throw exception, instead capture the exception and send it as response.
This implies either CommandResponse can hold exception response. Following this the code looks as follows:
class CommandResponse<T>{
public T errorResponse();
public T successResponse();
public boolean isSucces();
}
And then later while rendering response you can handle failures/exceptions as per use case.
OR
another way to handle this is having an interface Response with two implementations one for Success & another for failure. Thus making method process to return List<Response>.
It all depends on the requirements, the contract between your process() method and its callers.
I can imagine two different styles of contract:
All Or Nothing: the caller needs the complete responses list, and can't sensibly proceed if some partial response is missing. I'd recommend to throw an exception in case of an error. Typically, this is the straightforward approach, and applies to many real-world situations (and the reason why the concept of exceptions was introduced).
Partial Results: the caller wants to get as much of the complete results list as currently possible (plus the information which parts are missing?). Return a data structure consisting of partial results plus error descriptions. This places an additional burden on the caller (extracting reults from a structure instead of directly getting them, having to explicitly deal with error messages etc.), so I'd only go that way if there is a convincing use case.
Both contracts can be valid. Only you know which one matches your situation. So, choose the right one, and document the decision.
There is one thing I have trouble understanding with quarkus.
I use JPA with Oracle. So I have the error
IllegalStateException: You have attempted to perform a blocking operation on an IO thread. This is not allowed, as blocking the IO thread
I looked in the Quarkus doc how to go about making JPA calls without having this difficulty. But all the examples and the doc use PostgreSQL or MariaDB with responsive clients. But I have not found any for classic JDBC clients.
I found a solution that works partially.
Uni.cretaFrom().Items(() -> MyBlokingIOCall());
Indeed I no longer have the exception.
But the MyBlokingIOCall method can raise exceptions.
I think I can use Uni's onFailure but we cannot pass methods raising an exception to
Uni.cretaFrom().Items
Indeed this method has as argument a Supplier, therefore the exceptions must be captured.
it's impossible to use Uni's onFailure with this solution.
If I use the current code in imperative mode
try {
Response.ok (myService ());
} catch (throwable e) {
Response.status (555, e.getMessage ());
}
I tried in vain to do this in reactive form
but I did not find a solution. I did not find any help.
I imagined something like.
Uni.createFrom().Items(() -> myService())
.onItem().apply(data -> Response.ok(data))
.onFailure().apply(err -> Response.status(555, err.getMessage()))
But this causes a compilation error. indeed the method
myService cannot be used in a Supplier because it throws an exception. it must therefore be captured. But then we no longer go into Uni's onFailure and we cannot trace the error we caught.
I think I think too much about wearing my imperative code instead of thinking differently.
But I can't find a doc or example that looks like this by any means. I imagine that there is a way of doing things (if not Quarkus would not exist). I think that when you think about it with the right approach, you have to find the doc, but when you don't know where to go, it's more complicated. I haven't found it because I don't know what I'm looking for.
I suppose I have to encapsulate the JPA call which produces a Uni or a Multi which is consumed by a processor which transforms the Entity into DTO which is then transmitted to the resource Rest layer which transforms the DTO into Response. the JPA call must be able to produce errors the processor too and the Resource layer too
It is, therefore, necessary at each step to capture these errors to propagate them through Uni or Multi
But how to do it?
This is kind of an know lambda limitation when it comes to conciseness and first expectations by simply providing the lambda implementation (or a method reference) as a method call parameter.
Meanwhile, it can be simply worked-around by implementing an extension around the expected Functional Interface, in your case it would be the Supplier<T>, that will catch any ** checked** exception and throw an unchecked one:
/**
* A Supplier sub-interface that catches any thrown exception
* and translates to an unchecked one.
*
* <p>This is a functional interface whose functional method is
* {#link #getChecked()}.
*/
#FunctionalInterface
public interface CheckedSupplier<T> extends Supplier<T> {
#Override
default T get() {
try {
return getChecked();
} catch (final Exception e) {
// Your common exception handling logic here..
throw new RuntimeException(e);
}
}
/**
* Gets a result.
*
* #return a result
* #throws Exception
*/
T getChecked() throws Exception;
}
Note that the functional interface signature is no longer #get() but #getChecked(). But that would make no difference for the compiler, which will try to check the functional signature based on the expected one and which is:
a method returning a T-typed object
Then you can simply use the new CheckedSupplier interface using just an explicit cast wherever a Supplier<T> (or alike) is expected:
Uni.createFrom().item((CheckedSupplier<DataType>) MutinyTest::findOne)
.onItem()
.apply(i-> Response.ok(i))
.onFailure()
.apply(e-> Response.status(555, e.getMessage()));
I changed my myService method so that it returns a Supplier
and handles exceptions
public Supplier<List<String>> myService (String arg){
return () -> {
try {
//my code
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
and I called it that
Uni.createFrom().item(myService("sample")))
.onItem().apply(data -> Response.ok(data))
.onFailure().recoverWithItem(err -> Response.status(600, err.getMessage()))
.onItem().apply(ResponseBuilder::build)
.emitOn(Infrastructure.getDefaultExecutor())
Suppose I've got an endpoint in Dropwizard, say
#GET
public Response foo() { throw new NullPointerException(); }
When I hit this endpoint it logs the exception and everything, which is great! I love it. What I love less is that it returns a big status object to the user with status: ERROR (which is fine) as well as a gigantic stack trace, which I'm less excited about.
Obviously it's best to catch and deal with exceptions on my own, but from time to time they're going to slip through. Writing a try catch block around the entire resource every time is fine, but (a) it's cumbersome, and (b) I always prefer automated solutions to "you have to remember" solutions.
So what I would like is something that does the following:
Logs the stack trace (I use slf4j but I assume it would work for whatever)
Returns a general purpose error response, which does not expose potentially privileged information about my server!
I feel like there must be a built-in way to do this -- it already handles exceptions in a relatively nice way -- but searching the docs hasn't turned up anything. Is there a good solution for this?
As alluded to by reek in the comments, the answer is an ExceptionMapper. You'll need a class like this:
#Provider
public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {
#Override
public Response toResponse(RuntimeException runtime) {
// ...
}
}
You can do whatever logging or etc. you like in the toResponse method, and the return value is what is actually sent up to the requester. This way you have complete control, and should set up sane defaults -- remember this is for errors that slip through, not for errors you actually expect to see! This is also a good time to set up different behaviors depending on what kind of exceptions you're getting.
To actually make this do anything, simply insert the following line (or similar) in the run method of your main dropwizard application:
environment.jersey().register(new RuntimeExceptionMapper());
where environment is the Environment parameter to the Application's run method. Now when you have an uncaught RuntimeException somewhere, this will trigger, rather than whatever dropwizard was doing before.
NB: this is still not an excuse not to catch and deal with your exceptions carefully!
Add the following to your yaml file. Note that it will remove all the default exception mappers that dropwizard adds.
server:
registerDefaultExceptionMappers: false
Write a custom exception mapper as below:
public class CustomExceptionMapper implements ExceptionMapper<RuntimeException> {
#Override
public Response toResponse(RuntimeException runtime) {
// ...
}
}
Then register the exception mapper in jersey:
environment.jersey().register(new CustomExceptionMapper());
Already mentioned this under the comments, but then thought I would give it a try with a use case.
Would suggest you to start differentiating the Exception that you would be throwing. Use custom exception for the failures you know and throw those with pretty logging. At the same RuntimeException should actually be fixed. Anyhow if you don't want to display stack trace to the end user you can probably catch a generic exception, log the details and customize the Response and entity accordingly.
You can define a
public class ErrorResponse {
private int code;
private String message;
public ErrorResponse() {
}
public ErrorResponse(int code, String message) {
this.code = code;
this.message = message;
}
... setters and getters
}
and then within you resource code you can modify the method as -
#GET
public Response foo() {
try {
...
return Response.status(HttpStatus.SC_OK).entity(response).build();
} catch (CustomBadRequestException ce) {
log.error(ce.printStackTrace());
return Response.status(HttpStatus.SC_BAD_REQUEST).entity(new ErrorResponse(HttpStatus.SC_BAD_REQUEST, ce.getMessage())).build();
} catch (Exception e) {
log.error(e.printStackTrace(e));
return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage())).build();
}
}
This article details Checked and Unchecked Exceptions implementation for Jersey with customized ExceptionMapper:
https://www.codepedia.org/ama/error-handling-in-rest-api-with-jersey/
Official Dropwizard documentation also covers a simpler approach, just catching using WebApplicationException:
#GET
#Path("/{collection}")
public Saying reduceCols(#PathParam("collection") String collection) {
if (!collectionMap.containsKey(collection)) {
final String msg = String.format("Collection %s does not exist", collection);
throw new WebApplicationException(msg, Status.NOT_FOUND)
}
// ...
}
https://www.dropwizard.io/en/stable/manual/core.html#responses
It worked for me by simply registering the custom exception mapper created in the run method of the main class.
environment.jersey().register(new CustomExceptionMapper());
where CustomExceptionMapper can implement ExceptionMapper class like this
public class CustomExceptionMapperimplements ExceptionMapper<Exception>
I need to write a RestClient used by several application that return a specific object.
In case of bad request (400) I'd like to advice the caller application of the message error and status code.
I wonder if is it a good behavior to throw a managed Exception with code and message property in order to be catched properly from the caller code.
Something like this:
RestClient.java
ClientResponse response;
try {
response = client.resource(requestURI).queryParams(queryParams)
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.post(ClientResponse.class, richiesta);
boolean output = response.getStatus() == Response.Status.NO_CONTENT.getStatusCode();
if (!output && response.getStatus() == Response.Status.BAD_REQUEST)
throw new RestClientRuntimeException(response.getStatus, response.getEntity(String.class));
} catch (ClientHandlerException e) {
throw new RestClientRuntimeException(e);
} catch (UniformInterfaceException e) {
throw new RestClientRuntimeException(e);
}
Caller application
try
{
boolean output = restClient.method()
} catch (RestClientRuntimeException e) {
// read exception status and message entity
}
Is it a good practice ?
Your question has several aspects. Lets consider them separately:
Should I translate http error codes into exceptions?
Yes you should translate error codes into exceptions. Exceptions are nice (if used correctly of course). They separate the happy path from the exceptional cases. If you don't use exceptions but return codes, your callers need to check for that return code. And if you call several methods you get some nasty cascading ifs:
if (service1.method1() == NO_ERROR) {
if (service2.method2() = NO_ERROR) {
if (service3.method2() = NO_ERROR) {
...
} else {
...
}
} else {
...
}
} else {
...
}
Furthermore if you have several layers (and you almost certainly have), you need to do this at every level again. Exceptions are much better here. They let you code (and read!) the nice and clean happy path first and then
you can worry about the exceptions in the catch block. That's easier to write and easier to read.
Should I use checked exceptions?
This can get quite religious. Some people think checked exceptions are a good idea, some think, they are evil. I don't want to debate that here in detail. Just that: Do you want to force your callers to think about that particular exception? Is there always or at least in the vast majority of cases a way for the caller to handle that exception?
If you come to the conclusion that the caller cannot do anything but log the exception and fail itself, you should refrain from using checked exceptions. This makes the caller code much cleaner. There is no use in pointless try..catch blocks that are just there to make the compiler happy.
Speaking of REST: In case of a 500, there is most likely no chance to recover. If you get a 405 you most likely have a bug and there is no way to recover either. If you get a 409 Conflict with some special info on how to resolve the conflict there might be a good way to handle that (depending on your requirements). In such a case you may consider a checked exception.
Should I store the response code in the exception?
When you use generic RestClientRuntimeExceptions and have your callers query the response code, then your caller is obviously coupled to this being a REST call. You can do that if you are writing a generic REST client that can query arbitrary REST APIs. No problem in this case.
But you are already using a generic library (I guess Jersey). So what's the point in wrapping a generic API around a generic API? There may be some reasons (e.g. evaluating some internally used header params) but you should think about it whether this is justified.
Maybe you want to write an application-specific REST client, i.e. one that deserializes your application-specific representation classes (DTOs). In such a case you should not use generic exceptions but rather application-specific ones. If you use application-specific exceptions your callers are not coupled to this being a REST call. Maybe in the future a cool new technology shows up and you need to change your client. If you use the generic exceptions and have your callers evaluate the response codes, the callers need to change, too. But if you use application-specific exceptions, this interface will be more stable. The callers don't even need to know that such a thing as REST exists. This makes the caller code simpler and more maintainable as you separate concerns.
Should I put a rest client into a jar?
I know you haven't asked that question. But maybe you should do so. Providing a rest-client jar with a Jersey dependency to arbitrary clients (that's what it seems to me that you do) looks nice at first but can get you into real trouble with dependencies. If you are not convinced, I can explain that in detail but lets discuss this in a separate question.
Since your success response is JSON, I would model the error responses as JSON too.
For example consider the following JSON serialized from a POJO like ErrorMessage.java having corresponding fields.
{
"statusCode": 400,
"errorMessage": "Bad Request",
"errorDetails": "<details here>"
}
Since it is HTTP, it would be better to communicate error codes based on HTTP status codes. errorMessage and errorDetails can be blank in case of a successful status code.
Why don't you check the HTTP status code family?
Other errors besides 400 can happen. If you test the status code family, you catch them all:
switch (response.getStatusInfo().getFamily()) {
case CLIENT_ERROR:
// It's a client error
// You can throw an exception
break;
case SERVER_ERROR:
// It's a server error
// You can throw an exception
break;
default:
break;
}
IMHO it's a good practice at least for 400 status code to have custom exception because it implies malformed data was sent.
Instead of throwing unchecked exception and catching them, checked exceptions are more suitable.
I have a class which takes enum values like Male,Female #POST . when I sent a wrong value like 'male' instead of 'Male' it shows me 400 Bad Request with this message in rest client : Can not construct instance of constants.Constants$GenderEnum from String value 'male': value not one of declared Enum instance names
at [Source: org.apache.catalina.connector.CoyoteInputStream#718a453d; line: 7, column: 23] (through reference chain: valueobjects.ConsumerValueObject["gender"])
My Rest End Point Looks like below :
#Consumes("application/json")
#Produces("application/json")
#POST
public Response addConsumer(ConsumerValueObject consumerVO)
Here ConsumerValueObject holds the enum.
How to suppress that error message in Rest client? I tried with ExceptionMapper but it did not help!I need to suppress the message due to security issues!
This is the Jackson response from either JsonParseExceptionMapper or JsonMappingExceptionMapper. These classes come with the dependency
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${2.x.version}</version>
</dependency>
Whether you have this explicit dependency or you have the resteasy-jackson2-provider (which uses the above under the hood), most likely the mappers are registered implicitly through classpath scanning. For instance you have an empty Application class.
#ApplicationPath("/")
public class ResteasyApplication extends Application {}
This will cause disovery/registration through classpath scanning. If you don't have either of those dependencies, and if you are in Wildfly, I am not exactly sure how they are registered, but that is what's happening.
You could write/register your own ExceptionMappers for the JsonParseException and JsonMappingException
#Provider
public class JsonMappingExceptionMapper
implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException e) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
}
but from what I have tested, it's a tossup as to which one will be registered, yours or Jackson's. The mappers are put into a Set (so unordered), then pushed into a Map, so only one get's pushed in. The order in which they are pushed in like I said is a tossup.
I guess this is really only a partial answer, as I have not been able to find a solution that is guaranteed to use your mapper, aside from registering all your classes explicitly (ultimately disabling the classpath scanning), but that is a hassle.
But now the fight has been narrowed down. I will try again some more if I get a chance later
UPDATE
So this is not a solution, just a semi-proof-of-concept to show how we can get it to use our ExceptionMapper.
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.my.pkg.JsonMappingExceptionMapper;
#Path("/init")
public class InitResource {
#GET
public Response init() {
ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance();
factory.getExceptionMappers().put(JsonMappingException.class,
new JsonMappingExceptionMapper());
return Response.ok("Done!").build();
}
}
Once we hit the init endpoint for first time, our JsonMappingExcpetionMapper will register, and override the existing one, whether it is Jackson's or ours.
Of course we would not want to do this for real, it's just showing how to override the mapper. The thing I can't figure out is where to put this code. I've tried a ServletContextListener, in the Application constructor, in a Feature with a low priority. I can't figure it out. None of the above occur before RESTeasy does its final registration.
Do you really want to supress the error message or do you want to fix the actual probelm?
You can actually catch all thrown exception with a custom exception mapper like
#Provider
public class CustomExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable t) {
return Response.ok().build();
}
}
though, this will handle all caught exceptions and return a 200 OK which tricks clients to think that the request actually succeeded - which was not the case! Instead of Throwable you should be able to catch the concrete exception (even if it is a RuntimeException) as well - maybe you have not declared it as provider or did not specify the correct exception class?
Though, as already mentioned returning a different status code for an exception is generally bad practice and should be avoided. Fixing the actual problem is probably more suitable in that case.
JAX-RS provides MessageBodyReader and MessageBodyWriter interfaces which you can declare to un/marshall an inputstream to an object or an object to return to an output-stream. The official documentation on MessageBodyReader has more detailed information on that regard.
One implementation therefore could be the following steps:
Read the input-stream to f.e. string
Replace all "male" or "female" tokens with their upper-case version
Parse the string to a json-representation (using org.json.JSONObject f.e)
Use ObjectMapper to convert the JSON representation to a Java object
return the mapped object
This works if the input failure is just a simple upper/lower case issue. If there are typos or semantically alternative available, which are not yet in your enum, you need to put in a bit more effort.
If you, however, fail to create a proper object representation, you should return a user-failure (something in the 400 range) to the client to inform the client that something went wrong.