1

这篇文章没有解决这个问题:ResponseExceptionMapper in cxf client。您会注意到我确实注册并注释了我的 Provider,并且我按照建议尝试使用 WebApplicationException 而不是 Exception/CustomException。

问题陈述:无法使用 Client (javax.ws.rs.client.Client) API 和实现 ResponseExceptionMapper 接口的 @Provider 类实现自定义客户端异常处理程序。

问题:

  1. 客户端 API 是否不支持自定义客户端提供程序进行异常处理?
  2. 我查找此问题陈述的任何文献都使用 JAXRSClientFactory 实现;在这种情况下,我还没有找到任何使用客户端 API。我必须切换我的实现吗?
  3. Client API 和 JAXRSClientFactory 实现之间有什么区别?

我正在使用 Java 开发 cxf 客户端 API 实现,并注意到对于 300 以上的 http 状态代码,cxf 将响应包装在 WebApplicationException 或 ProcessingException 中(取决于响应状态代码)。在我的例子中,服务器有一个自定义的响应正文,指示 http 状态代码 !200 的实际原因,如下所示(响应代码 = 412):

{
"requestError": {
  "serviceException": {
       "messageId": "SVC4120",
       "text": "Invalid Request: Invalid Coupon Code."
    }
  }
}

不幸的是,WebApplicationException 本身并没有呈现这个。相反,直接在异常中捕获的唯一消息是通用的“412 Precondition Failed”。我可以从代码片段(包括客户端 API 代码片段)中执行类似于以下异常块的操作:

protected RESPOBJ invoke(String endPointUrl) throws CustomException {
    Object reqPOJO = prepareRequest();
    try {
        if(client == null) {
            ClientBuilder builder = ClientBuilder.newBuilder();
            //register custom JAX-RS components
            builder.register(new CustomMapper());
        }
        WebTarget target = client.target(endPointUrl);
        //do this when queryParams exist
        if(!getUriParams().isEmpty()) {
            for(Map.Entry<String, String> queryParams : getUriParams().entrySet()) {
                target = target.queryParam(queryParams.getKey(), queryParams.getValue());
            }
        }
        Invocation.Builder builder = target.request();
        //create headers here
        MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
        if(isBasicAuthRequired()) {
            headers.add(AUTH_HEADER_PARAM, getBasicAuthentication());
        }
        headers.add(CONTENT_TYPE, getMediaType().toString());
        builder.headers(headers);
        builder.accept(getMediaType().toString());
        //GET or POST
        if(HttpMethodType.GET.equals(getHttpMethod())) {
            return builder.get(RESPOBJ.class);
        }
        return builder.post(Entity.entity(reqPOJO, getMediaType()), RESPOBJ.class);
    }
    catch (Exception ex) {
        if(ex instanceof ResponseProcessingException) {
            ResponseProcessingException e = (ResponseProcessingException) ex;
            logger.error("Unmarshalling failed: [" + e.getResponse().readEntity(String.class) + "]");
        }
        else if(ex instanceof WebApplicationException) {
            WebApplicationException e = (WebApplicationException) ex;
            logger.error("Error Response: ["+e.getResponse().readEntity(String.class) + "]");
        }
        throw new CustomException(ex);
    }
}

但是,我希望实现一些更清洁的东西,最好使用实现 ResponseExceptionMapper<> 接口的自定义异常处理程序。从文献中我注意到用于自定义客户端异常处理的 ResponseExceptionMapper 的唯一实现是使用 JAXRSClientFactory。然而,我当前的实现使用客户端 API(下面的代码片段)。从设计方面来看,我将对其进行修改以拥有一个单独的 CustomExceptionMapper 类,该类仅作为异常情况的提供者,但我不明白为什么这个自定义类被注册为提供者(适用于 200 个状态代码作为 MBR 和 MBW始终有效)但不适用于异常情况。

更新:在调试和观察 200 与 >300 状态代码(在我的情况下为 412)之间的变化时,我注意到对于 200 例 JAXRSUtils.readFromMessageBodyReader() 方法被调用,这是第一次检索自定义提供程序。对于下面代码片段中显示的状态代码,代码永远不会到达这里,这应该是找不到 CustomMapper 的原因。我必须如何注册我的 CustomExceptionMapper 有什么不同吗?还是客户端 API 根本不支持此功能?

// 对于失败情况,上面的方法返回 null (status > 300),而对于成功 200 情况,它执行最后一行的方法并获取提供程序。// AbstractClient 类调用 doReadEntity() 方法,该方法反过来调用并在 JAXRSUtils.readFromMessageBodyReader() 方法代码中找到 Provider

protected <T> T readBody(Response r, Message outMessage, Class<T> cls, 
                         Type type, Annotation[] anns) {

    if (cls == Response.class) {
        return cls.cast(r);
    }

    int status = r.getStatus();

    //this is invoked for failure case
    if ((status < 200 || status == 204) && r.getLength() <= 0 || status >= 300) {
        return null;
    }
    //this for 200 status code
    return ((ResponseImpl)r).doReadEntity(cls, type, anns);                                                
}

//My custom provider code
@Provider
@Consumes
@Produces(MediaType.APPLICATION_JSON)
public class CustomMapper implements MessageBodyReader<CustomResponse>, MessageBodyWriter<CustomRequest>, ResponseExceptionMapper<CustomException> {
    private Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return type.isAssignableFrom(CustomResponse.class);
    }

    @Override
    public CustomResponse readFrom(Class<CustomResponse> type, Type genericType, Annotation[] annotations,
        MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
        CustomResponse respObj = new CustomResponse();
        //json to pojo code
        return respObj;
    }

    @Override
    public long getSize(CustomRequest reqObj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return type.isAssignableFrom(CustomRequest.class);
    }

    @Override
    public void writeTo(CustomRequest reqObj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
        entityStream.write(gson.toJson(reqObj).getBytes());
    }

    @Override
    public CustomException fromResponse(Response exceptionResponse) {
        //Response obj to my CustomException code
        return (CustomException);
    }
}

问题:

我试图弄清楚这里做错了什么,如果客户端 API 出于任何原因不支持自定义客户端异常处理?Client API 和 JAXRSClientFactory 实现之间有什么区别?我也在研究可能使用 ClientResponseFilter (还没有尝试过)。

任何帮助表示赞赏。谢谢。

4

0 回答 0