ResponseExceptionMapper в cxf с использованием клиентского (javax.ws.rs.client.Client) API

Этот пост не решает проблему: ResponseExceptionMapper в клиенте cxf. Вы заметите, что я на самом деле зарегистрировал и аннотировал своего провайдера, и я попробовал использовать WebApplicationException в соответствии с предложением вместо Exception/CustomException.

Постановка проблемы: Невозможно реализовать пользовательский обработчик исключений на стороне клиента с помощью API-интерфейса клиента (javax.ws.rs.client.Client) и класса @Provider, реализующего интерфейс ResponseExceptionMapper.

Вопросы:

  1. Разве Клиентский API не поддерживает пользовательских поставщиков на стороне клиента для обработки исключений?
  2. Любая литература, которую я искал для этой постановки задачи, использует реализацию JAXRSClientFactory; Мне еще предстоит найти какое-либо использование Client API для этого сценария. Должен ли я изменить свою реализацию?
  3. В чем разница между клиентским API и реализациями JAXRSClientFactory?

Я работаю над реализацией клиентского API cxf в Java и заметил, что для кодов http http выше 300 cxf оборачивает Response либо в WebApplicationException, либо ProcessingException (в зависимости от кода состояния ответа). Сервер в моем случае имеет настроенное тело ответа, указывающее фактическую причину для кода http http!200, как показано ниже (для кода ответа = 412):

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

К сожалению, само WebApplicationException не отображает это. Вместо этого единственное сообщение, захваченное в исключении напрямую, является общим "412 Precondition Failed". Я могу сделать что-то похожее на приведенный ниже блок исключения из фрагмента кода (включая фрагмент кода Client 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()

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);
    }
}

Вопросы:

Я пытаюсь выяснить, что здесь делается неправильно, и если Client API не поддерживает пользовательскую обработку исключений на стороне клиента по какой-либо причине? В чем разница между клиентским API и реализациями JAXRSClientFactory? Я также изучаю возможность использования ClientResponseFilter (еще не пробовал).

Любая помощь приветствуется. Благодарю.

0 ответов

Другие вопросы по тегам