如何访问Quarkus ClientWebApplicationException的内部数据?
解决Quarkus Rest Client异常中获取请求上下文的方案
要在异常映射器中拿到请求方法、URI、请求头以及客户端标识,核心思路是通过请求/响应拦截器捕获并传递请求上下文,替换默认抛出的ClientWebApplicationException为自定义异常,从而在异常映射器中直接获取所需信息。
步骤1:定义请求上下文封装类
先封装需要的请求信息,方便后续传递:
public class RequestContext { private String method; private URI uri; private Map<String, List<String>> headers; private String clientKey; // 区分不同RestClient的标识,对应@RegisterRestClient的configKey // 省略getter/setter public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public URI getUri() { return uri; } public void setUri(URI uri) { this.uri = uri; } public Map<String, List<String>> getHeaders() { return headers; } public void setHeaders(Map<String, List<String>> headers) { this.headers = headers; } public String getClientKey() { return clientKey; } public void setClientKey(String clientKey) { this.clientKey = clientKey; } }
步骤2:实现请求拦截器捕获上下文
通过ClientRequestFilter在请求发送前捕获上下文,并存入请求属性中:
public class RequestContextFilter implements ClientRequestFilter { public static final String CONTEXT_KEY = "rest-client-request-context"; @Override public void filter(ClientRequestContext requestContext) throws IOException { RequestContext context = new RequestContext(); context.setMethod(requestContext.getMethod()); context.setUri(requestContext.getUri()); context.setHeaders(new HashMap<>(requestContext.getHeaders())); // 获取当前RestClient的configKey,用于区分不同客户端 String clientKey = (String) requestContext.getProperty("org.eclipse.microprofile.rest.client.clientKey"); context.setClientKey(clientKey); // 将上下文存入请求属性,后续响应拦截器可直接获取 requestContext.setProperty(CONTEXT_KEY, context); } }
步骤3:实现响应拦截器抛出自定义异常
拦截4xx/5xx响应,取出请求上下文,抛出自定义异常(继承ClientWebApplicationException,保留原有响应信息):
public class RestClientException extends ClientWebApplicationException { private final RequestContext requestContext; public RestClientException(Response response, RequestContext requestContext) { super(response); this.requestContext = requestContext; } public RequestContext getRequestContext() { return requestContext; } }
对应的响应拦截器:
public class ErrorResponseFilter implements ClientResponseFilter { @Override public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { if (responseContext.getStatus() >= 400) { // 取出之前存入的请求上下文 RequestContext context = (RequestContext) requestContext.getProperty(RequestContextFilter.CONTEXT_KEY); // 构建响应对象并抛出自定义异常 Response response = Response.fromResponse(responseContext.getResponse()).build(); throw new RestClientException(response, context); } } }
步骤4:注册拦截器到RestClient接口
在你的RestClient接口上绑定这两个拦截器:
@RegisterRestClient(configKey = "user-service-client") @ClientRequestFilter(RequestContextFilter.class) @ClientResponseFilter(ErrorResponseFilter.class) public interface UserServiceClient { // 你的接口方法,例如: @GET @Path("/users/{id}") User getUserById(@PathParam("id") Long id); }
步骤5:实现异常映射器处理自定义异常
在异常映射器中直接获取请求上下文和响应信息,按需构造错误响应:
@Provider public class RestClientExceptionMapper implements ExceptionMapper<RestClientException> { @Override public Response toResponse(RestClientException exception) { RequestContext requestContext = exception.getRequestContext(); Response originalResponse = exception.getResponse(); // 提取所需信息:客户端标识、请求方法、URI、请求头等 String clientId = requestContext.getClientKey(); String requestMethod = requestContext.getMethod(); URI requestUri = requestContext.getUri(); Map<String, List<String>> requestHeaders = requestContext.getHeaders(); // 构造自定义错误返回体(示例用简单实体类) ErrorDetails errorDetails = new ErrorDetails(); errorDetails.setClientId(clientId); errorDetails.setRequestMethod(requestMethod); errorDetails.setRequestUri(requestUri.toString()); errorDetails.setRequestHeaders(requestHeaders); errorDetails.setStatusCode(originalResponse.getStatus()); errorDetails.setStatusReason(originalResponse.getStatusInfo().getReasonPhrase()); return Response.status(originalResponse.getStatus()) .entity(errorDetails) .type(MediaType.APPLICATION_JSON) .build(); } // 错误详情实体类 public static class ErrorDetails { private String clientId; private String requestMethod; private String requestUri; private Map<String, List<String>> requestHeaders; private int statusCode; private String statusReason; // 省略getter/setter } }
关键注意事项
- 客户端标识:通过
org.eclipse.microprofile.rest.client.clientKey属性获取的是@RegisterRestClient中配置的configKey,可直接用来区分不同的RestClient实例。 - 避免内存泄漏:使用请求属性存储上下文而非
ThreadLocal,无需手动清理,请求结束后上下文会自动销毁。 - 兼容性:自定义异常继承
ClientWebApplicationException,保证Quarkus RestClient的原有错误处理逻辑不受影响。
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

