I am developing a REST-facade for an EJB service, which means it calls the EJB, translates the result to representations a REST-caller will understand and then returns it (as json or xml). All of that works splendid. But the EJB service throws a variety of exceptions, e. g. when no result is found or a few different other cases. Since I don't want those propagating to the REST-caller, I implemented an ExceptionMapper:
public class EjbExceptionMapper implements ExceptionMapper<EJBException> {
private static final Logger logger = LoggerFactory.getLogger(EjbExceptionMapper.class);
#Override
public Response toResponse(final EJBException exception) {
ResponseBuilder result = Response.status(Status.BAD_REQUEST);
logger.debug("Bad request:", exception);
if (exception.getCause() != null) {
final Throwable cause = exception.getCause();
if (cause instanceof NoDeliveryFoundException) {
logger.debug("Found NoDeliveryFoundException:", cause);
result = Response.status(Status.NO_CONTENT).entity(cause.getMessage());
}
}
return result.build();
}
}
All the exceptions from my EJB-service arrive as javax.ejb.EJBException, which this Mapper manages to catch just fine, with different custom Exceptions of the application as causes. The plan is to return different Responses depending on the type of cause of the EJBException. The logger-calls used for debugging are both executed in case I get a NoDeliveryFoundException as the cause, so I know it's executed (the Mapper registered correctly and is used for mapping), but the client never sees a response.
Every call leading to an EJBException in the underlying service (and thus the use of this ExceptionMapper) leads to no Response at all, as if the toResponse()-method were returning null and not a custom built Response.
I even went so far as to log the Response right before returning it, it exists and contains what I expect, so I am positive that it is returned by the toResponse-method. But still, my client receives no Response.
So now I'm stumped and since no search managed to even find someone describing a similar problem, I turn to you, dear SO. ;)
Your toResponse-method contains some conflicting logic.
Response.status(Status.NO_CONTENT).entity(cause.getMessage());
Here you are assigning the the HTTP status code 204 No Content, but you are also adding a response. You can't set the status to 204 No Content and return a response body at the same time.
Also, if Exception#getCause() is null or if it's not an instance of NoDeliveryFoundException, the response body is empty.
Could this be the cause of your problems?
Related
at work we building an web application with java spring backend and vue frontend.
At the moment we uses 2 or 3 http response code to pass errors between frontend and backend.
If you call and endpoint with wrong parameters, you'll get an BAD_REQUEST. If some exception was thrown in the backend (which isn't related with the parameters) the backend returns an INTERNAL_SERVER_ERROR and if you pass some ids which aren't in the database the backend returns an NOT_FOUND.
This method has multiple problems:
we have no structure in error case which we can use to pass information to the user (frontend)
we want to indicate problems to the user, which can't be classified by HTTP response codes. For example if an external service isn't available, we want to pass the service name to the frontend. But I don't know if "SERVICE_UNAVAILABLE" fits here...
I found this already: https://www.baeldung.com/spring-response-status-exception
We could use the message field to pass detailed information about an error (specific error json in message field).
Is this a good idea?
Opinions?
T
You can certainly pass information back in this field but using ResponseStatusExceptions. Depending on how much information the frontend needs (e.g. if it's just surfacing a user friendly message to the user) this may be enough for your needs.
Another approach, if you want to use a custom object in the response (esp. per exception/response code), is using #ControllerAdvice and extending ResponseEntityExceptionHandler.
e.g. say you have a custom exception ExternalServiceUnavailableException which had some underlying ServiceInformation you could retrieve from it. Then you could do something like
public class ServiceInformation {
private final String name;
private final String status;
private final String statusMessage;
//snip
}
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({ ExternalServiceUnavailableException.class })
public ResponseEntity<Object> handleExternalServiceUnavailable(ExternalServiceUnavailableException ex, WebRequest request) {
final ServiceInformation si = ex.getServiceInformation();
return ResponseEntity
.status(503) // or whatever code you want
.body(si); // or map to some other object/format
// or use the constructor to supply headers etc.
}
}
When you throw a ExternalServiceUnavailableException this would result in a response body like
{
"name": "my-external-service",
"status": "timeout",
"statusMessage": "Service timed out after 30 seconds"
}
A more complete example of this can be found in the below article where the same custom error object is used for each of the exceptions of consequence as well as a default handler.
https://www.baeldung.com/exception-handling-for-rest-with-spring
This makes it easier for the frontend to interpret (as does your proposed approach) since there is only a single format to expect and parse, but you are free to return different response shapes per exception.
Edit: it's worth remembering that there are also response codes 502 (bad gateway) and 504 (gateway timeout) which can be used to indicate an external service is either unavailable or timing out. If these are appropriate you could just use appropriate ResponseStatusExceptions with a message set to include the service name (or other info). As above, it depends on what you need/want the fronted to receive.
I am working on an application which uses Dropwizard, which has this implementation of ExceptionMapper: https://github.com/dropwizard/dropwizard/blob/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/errors/LoggingExceptionMapper.java
Problem with this implementation is that even though this catches both 4** and 5** errors, it only logs 5** errors.
I need to implement ExceptionMapper such that LoggingExceptionMapper is not used at all and my CustomExceptionMapper logs both CLIENT_ERRORs and SERVER_ERRORs.
I am wondering how would my application know that it needs to use CustomExceptionMapper instead of the Dropwizard class?
Also would it suffice to add CLIENT_ERROR to if condition, to log out all errors?
#Override
public Response toResponse(E exception) {
// If we're dealing with a web exception, we can service certain types of request (like
// redirection or server errors) better and also propagate properties of the inner response.
if (exception instanceof WebApplicationException) {
final Response response = ((WebApplicationException) exception).getResponse();
Response.Status.Family family = response.getStatusInfo().getFamily();
if (family.equals(Response.Status.Family.REDIRECTION)) {
return response;
}
if (family.equals(Response.Status.Family.SERVER_ERROR) || family.equals(Response.Status.Family.CLIENT_ERROR) {
logException(exception);
}
return Response.fromResponse(response)
.type(MediaType.APPLICATION_JSON_TYPE)
.entity(new ErrorMessage(response.getStatus(), exception.getLocalizedMessage()))
.build();
}
Or would there be a better way to do this?
JAX-RS spec about ExceptionMapper:
When choosing an exception mapping provider to map an exception, an
implementation MUST use the provider whose generic type is the nearest
superclass of the exception.
How would my application know that it needs to use CustomExceptionMapper instead of the Dropwizard class?
You can throw a custom exception from your application and create an ExceptionMapper for that specific exception.
Would it suffice to add CLIENT_ERROR to if condition, to log out all errors?
Yes, 4xx and 5xx family has all the error responses.
I am using jetty with spring boot, and implemented few rest calls, all the get methods are working fine in both the environment dev and qa , but post is always returning '405 Method Not Allowed', I checked everything like headers, body and other parameters everything seems correct.
I am using spring rest implementation, any ideas why its failing.
method Definition:
#RequestMapping(value ="/resub" ,method=RequestMethod.POST )
public ResponseEntity<?> resub(#RequestBody Subscription Subscription,HttpServletRequest request ){
try {
// call to other service methods
//}
} catch (Exception e) {
if(e instanceof HttpClientErrorException)
return commonUtil.getExceptionResponse(e.getMessage());
else
return commonUtil.getExceptionResponse("Unknown Error Occured"); }
}
This answer should really be a comment, but unfortunately I don't have 50 rep yet.
The first problem I notice is that you're closing the resub() function before it even gets to the catch statement.
That doesn't explain why you would get a 405 http status response though.
"The principal cause of this issue is either using the wrong method (not applicable to your case) or multiple handlers have been defined for the same verb/method, and one of the handlers is blocking the expected handler from processing the request. ... IIS processes handlers from first to last based on the order handler entries in the applicationHost.config and web.config files, where the first matching combination of path, verb, resource, etc., will be used to handle the request [1]."
Hope this helps, if not let me know and I can continue trying to help :)
[1] - https://www.asp.net/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications
I found another post showing how we can create our own checked exceptions that also return a HTTP status code different from 500. However, I need it to be a RuntimeException.
Then, I found WebApplicationException which is an unchecked exception, returns a HTTP status code but does not allow me to set message as in a regular exception.
Is there any unchecked exception out there in Java EE 6 that allows me to set an error message like in a regular exception and also returns a HTTP status code that I can set?
Edit: Including an explanation of why I want this as requested by John.
I created a filter to catch HTML and XSS attacks from my requests parameters. Instead of checking for that everytime in Filter.doFilter which would be too slow, I extended HttpServletRequestWrapper and used it like this.
HttpFilterRequest implements Filter
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(new SafeHttpRequest((HttpServletRequest) request), response);
} catch (SecurityViolationException e) {
log.warn(format("A security violation was detected. Please enable debug for further details: %s]", e.getMessage()));
HttpServletResponse resp = (HttpServletResponse) response;
resp.sendError(e.getStatusCode());
}response);
}
SafeHttpRequest extends HttpServletRequestWrapper (supressing parts to shorten code)
#Override
public String getParameter(String parameter) {
return xssAndHtmlValidation(super.getParameter(parameter));
}
#Override
public String getHeader(String name) {
return xssAndHtmlValidation(super.getHeader(name));
}
xssAndHtmlValidation() throws SecurityViolationException which is a RuntimeException but the catch block at doFilter doesn't work because my exception is thrown as a ServletException containing SecurityViolationException.
Ok, so the issue is that you want SafeHttpRequest.xssAndHtmlValidation() to throw an exception that can pass out of HttpServletRequest.getParameter() and HttpServletRequest.getHeader(), neither of which declares any checked exceptions. You want this exception ultimately to be caught by HttpFilterRequest.doFilter(). You want it to have a customizable message and you want it to carry an HTTP response code.
You clearly do need an unchecked exception to approach the problem this way. It seems most appropriate to create a new one from scratch, by extending java.lang.RuntimeException. You can give that class whatever fields, constructors, and methods you want, by which to transport any information at all from xssAndHtmlValidation() to the filter. Doing so for an exception class is no different from doing so for any other class, though your constructors should be sure to invoke an appropriate superclass constructor.
The filter must then have a catch block for the new custom exception type. Since it is your own custom exception, it is unlikely to be caught by anything between the request and the filter, and since you will catch that specific exception, you can easily invoke whatever nice methods you provided for yourself, such as maybe a getResponseCode(). Presumably the catch block would then invoke one of the sendError() methods on the response object, and then return normally rather than throwing an exception up the stack.
Do note, by the way, that if the problem is detected too late, after the underlying resource has already committed to a different response, then attempting to sendError() will cause an IllegalStateException instead of changing the response code. The client won't see that (because, again, the response has already been committed), but the response might be truncated.
Note also that it is unclear whether you actually need your custom exception to carry an HTTP response code. Would it ever vary from one instance to another? If not, then the appropriate response code is inherent in the fact that the exception was thrown at all, and the filter can set a response code appropriately based solely on the fact that it has caught that particular exception type.
Update:
Of course, if your JSP engine is going to wrap your exception and throw it as a ServletException then you can catch that exception and decide what to do based on exception.getCause().getClass() (but do watch out for the cause being null). That could work if the engine has not already committed the response in such cases.
If your JSP engine is after all going to intercept all exceptions and convert them into HTTP code 500 responses, then any approach based on throwing an exception is simply a dead end. Your best option then is to handle it on the front end, in your filter, before passing the request down the chain. That's a natural fit for a filter.
You expressed concern that doing the test in the filter would be too slow, but that could be slower than your proposed alternative only if the request contains parameters or headers that are never examined by downstream components. On the other hand, your approach based on a request wrapper could in fact be the slower one if request headers or parameters are accessed more than once each downstream, as you will perform the validation on each access, even though you only need to perform it once per parameter / header.
I have a simple POJO that I annotated with REST annotations as follows:
#GET
#Path("/domains/{domainid}")
#Override
public Domain getDomain(#PathParam("domainid") UUID domainID) throws Exception {
logger.info("Retrieving domain "+ domainID);
Domain d = null;
try {
d = MyClient.getDomains().get(domainID.toString());
logger.debug("Returning "+d.getName());
} catch (Exception e) {
logger.error("Could not retrieve domain", e);
}
return d;
}
Note that the log statement including d.getName() can actually throw an NPE which is then caught and logged. That's not pretty but it's also not the point here.
Ultimately whether d has a value or not, I return it.
In the case of a null value, my client receives an HTTP 204 status code. This is what wget displays: HTTP request sent, awaiting response... 204 No Content
Oddly enough, my browsers don't budge an inch. They still display the previous page (I suppose it makes sense to stay put when no content is received). I would have expected a blank page.
Three questions:
is HTTP 204 the right response to be returned?
how can I control that via annotations? Via other configuration?
what is the standard REST best practice regarding null objects?
Thanks
EDIT
There is a great question on the very same topic here: Is it correct to return 404 when a REST resource is not found?
If the request is trying to GET/locate/find a resource, and it can't be found, traditionally, we should send a 404 Not Found. For further discussion see here.
That being said, I generally like to have my resource methods return Response, as it's easier to fine tune the response the way I want (a little - not much - more detail here). But seeing as how your method is overriding an interface contract (and returning a model object), JAX-RS gives us a nice hierarchy of exceptions that will get mapped to a particular response/status. The list can be seen here.
So in your particular case, if the resource can't be found, you can throw a WebApplicationException(Response.Status.NOT_FOUND) or a NotFoundException, and the exception will be mapped to a 404 Not Found. Something like
d = MyClient.getDomains().get(domainID.toString());
if (d == null) {
throw new NotFoundException(); // <-- JAX-RS 2.0
// or throw new WebApplicationException(Response.Status.NOT_FOUND);
// ^^ JAX-RS 1.x
}
The method will exit when the exception is thrown, and the client will receive a response with a 404 Not Found status.
Related Q&As
How to catch 404 (NotFoundException) without being dependant on a JAX-RS implementation?
Is it correct to return 404 when a REST resource is not found?
EDIT
In the first line I stated "If the request is trying to GET/locate/find a resource..", but really, this applies to almost all cases we are using URI templates, whether it is for a GET, POST, PUT, DELETE, whatever. Consider this example
#PUT
#Path("/customers/{id}")
public Response updateCustomer(#PathParam("id") long id, Customer customer) {
...
}
Here is a method that allows the client to update a customer via a PUT. The client should know the complete URI to the resource before trying to update it. If the {id} parameter (used for lookup) is not found say in a database, then the resource doesn't exist, and a 404 Not Found should also be returned to the client.