如何在Jersey客户端中读取有效载荷

时间:2018-11-30 13:51:02

标签: java jax-rs jersey-client

我在这里有一个小问题。触发请求时,我想用HMAC对整个消息进行签名,然后将结果签名添加到标题中。

所以我实现了

javax.ws.rs.ext.WriterInterceptorContext

public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException

方法我无法访问实体的字符串表示形式。它总是返回一个空的字符串。原因似乎是在WriterInterceptor之后执行的MessageBodyWriter。

基本上,我遇到以下两种情况:

public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
    try {
        final ClientOutputStream stream = (ClientOutputStream) requestContext.getProperty(HTTPCLIENT_ENTITY_STREAM);
        String payload = stream.getString(Charset.forName("UTF-8")); // returns alway empty String
        String signature = doSomeSuffWithPayload(payload);

        MultivaluedMap<String, Object> headers = context.getHeaders();
        headers.add(HmacHeaderValue.X_SIGNATURE.headerName(), signature);
        context.proceed();
    } catch (IllegalArgumentException | ParseException | InvalidKeyException | NoSuchAlgorithmException ex) {
        LOGGER.error(ex.getMessage());
    } catch (UnsupportedEncodingException ex) {
        LOGGER.error(ex.getMessage());
    } catch (IOException ex) {
        LOGGER.error(ex.getMessage());
    }
}

在这里,doSomeSuffWithPayload(payload)方法不起作用,因为有效负载始终为空。

我以为可以做到这一点,所以我将context.proceed()调用切换到了其他任何地方:

public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
    try {
        context.proceed();
        final ClientOutputStream stream = (ClientOutputStream) requestContext.getProperty(HTTPCLIENT_ENTITY_STREAM);
        String payload = stream.getString(Charset.forName("UTF-8")); // returns the right string representation
        String signature = doSomeSuffWithPayload(payload);

        MultivaluedMap<String, Object> headers = context.getHeaders();
        headers.add(HmacHeaderValue.X_SIGNATURE.headerName(), signature); // doesn't add the header
    } catch (IllegalArgumentException | ParseException | InvalidKeyException | NoSuchAlgorithmException ex) {
        LOGGER.error(ex.getMessage());
    } catch (UnsupportedEncodingException ex) {
        LOGGER.error(ex.getMessage());
    } catch (IOException ex) {
        LOGGER.error(ex.getMessage());
    }
}

在这种情况下,实体的字符串表示形式可以。但是将标头添加到请求中是行不通的。

所以atm我可以将(错误的)签名添加到标头中,并且将实体始终为空,或者将正确的签名与正确的实体添加在一起,但不添加标头。

我的问题是:是否有人知道使用WriterInterceptor获取实体的字符串表示形式的方法?

EDITH说:

我们正在使用球衣客户端的2.25.1版本。 2.27也不能解决问题。

1 个答案:

答案 0 :(得分:0)

深入研究API之后,我发现该实体确实在MessageBodyWriter中的WriterInterceptor之后写入。此外,还可以在MessageBodyWriter的过程中添加标头。这就是为什么以上两种方法都不起作用的原因。

我的解决方案atm是正确的MessageBodyWriter,让它序列化实体,就像在WriterInterceptor之后执行的MessageBodyWriter中那样。在这种情况下,不再需要WriterInterceptor,实现ClientRequestFilter即可解决问题。

import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Providers;

@Context
private Providers providers;

private String getPayloadFromRequest(ClientRequestContext requestContext) throws IOException {
    Object object = requestContext.getEntity();
    if (object != null) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            // buffer into which myBean will be serialized
            Class<Object> type = (Class<Object>) requestContext
                    .getEntityClass();
            GenericType<Object> genericType = new GenericType<Object>(type) {
            };

            // get most appropriate MBW
            final MessageBodyWriter<Object> messageBodyWriter = providers
                    .getMessageBodyWriter(type, type, new Annotation[]{},
                            MediaType.APPLICATION_JSON_TYPE);

            try {
                // use the MBW to serialize myBean into baos
                messageBodyWriter.writeTo(object, object.getClass(),
                        genericType.getType(), new Annotation[]{},
                        MediaType.APPLICATION_JSON_TYPE,
                        new MultivaluedHashMap<String, Object>(), baos);
            } catch (IOException e) {
                throw new RuntimeException(
                        "Error while serializing MyBean.", e);
            }

            return baos.toString();
        } finally {
            baos.close();
        }

    } else {
        return "";
    }
}

代码不是我的,但不幸的是我丢失了源代码。