0

In my Quarkus microservice, I am using a microprofile rest client to fetch data from other external services:

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.smallrye.mutiny.Uni;

/**
 * A MicroProfile REST client of internal services APIs, such as xxx service, xxx service. To
 * configure the base URL, one needs to add a line to the application.properties file. 
 */
@Path("/api")
@ApplicationScoped
@RegisterRestClient
public interface InternalService {

  @GET
  @Path...
}

One annoying thing is that in case of failures, it hides the response content, and always returns

{"message":"Handled
Internally","stackFrames":["org.jboss.resteasy.microprofile.client.ExceptionMapping$HandlerException:
Handled Internally","\tat
org.jboss.resteasy.microprofile.client.ExceptionMapping.filter(ExceptionMapping.java:72)","\tat
org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.filterResponse(ClientInvocation.java:715)","\tat
org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:489)","\tat

and by reading the source code, I found this default behavior is intentinoal:

/**
 * This implementation is a bit of a hack and dependent on Resteasy internals.
 * We throw a ResponseProcessingExceptoin that hides the Response object
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class ExceptionMapping implements ClientResponseFilter {
    public static class HandlerException extends ResponseProcessingException {
        protected ClientResponse handled;
        protected List<ResponseExceptionMapper> candidates;

I think this client library is convenient and would like to continue to use it, but how can I tame it or override it in order to return the real error message?

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
GeauxEric
  • 2,814
  • 6
  • 26
  • 33
  • I don't know if it's possible to change this, or how, but I know it's been done to prevent exactly what you seem to be doing. Suppose client A asks service B for something. Service B, in turn, asks service C for something else. Here, B acts as a server to A and as a client to C. It would be a bad idea for B to take C's error response and forward it to A without a change. (Unless B is an HTTP proxy, but you don't use JAX-RS to implement those.) That might easily expose internal implementation details, including possibly sensitive data. Service B should return its own error response to client A. – Ladicek May 04 '21 at 07:13
  • @Ladicek mind putting that in an answer? – geoand May 04 '21 at 07:18
  • Well, a proper answer would include example code with defining an exception mapper and whatever, for which I don't have the time... But if you think what I wrote is enough, I'll do it :-) – Ladicek May 04 '21 at 07:49

2 Answers2

0

Figured it out myself after asking the Quarkus autheros: https://github.com/quarkusio/quarkus/issues/17153#issuecomment-841573713

GeauxEric
  • 2,814
  • 6
  • 26
  • 33
-1

I don't know if it's possible to change this, or how, but I know it's been done to prevent exactly what you seem to be doing.

Suppose client A asks service B for something. Service B, in turn, asks service C for something else. Here, B acts as a server to A and as a client to C. It would be a bad idea for B to take C's error response and forward it to A without a change [1]. Doing that might easily expose internal implementation details, including possibly sensitive data. Service B should handle the error response from C (by catching the exception) and return its own error response to client A.

[1] Unless B is an HTTP proxy, but you don't use JAX-RS and MicroProfile RestClient to implement those.

Ladicek
  • 5,970
  • 17
  • 20
  • In my case, service B acts as an edge service rather than a proxy. I did not mean to expose the error directly to client A; I would like to be able to see the error and properly handle it in B. Right now, since the microprofile client just hides everything and throws a default exception, I can do nothing about it. – GeauxEric May 04 '21 at 16:51
  • So if you wrap the RestClient invocation into `try { ... } catch (Exception e) { System.out.println(e); }`, you don't get anything interesting out of `e`? – Ladicek May 05 '21 at 07:32
  • no. The exception is message is hard coded and always the same. – GeauxEric May 06 '21 at 20:43
  • Alright, so I don't see the same behavior as you, but you can try a few things. One, you can catch `WebApplicationException` and then use `org.jboss.resteasy.client.exception.WebApplicationExceptionWrapper.unwrap` to get the original exception. Make sure to not rethrow this exception -- you'd be exposing server C's response directly to client A, which is usually wrong. Two, it is actually possible to revert to previous behavior by setting the `resteasy.original.webapplicationexception.behavior` config property to `true`. I'd say that 99.9% of time, you don't wanna do this, but it's there. – Ladicek May 07 '21 at 10:52