JAX-RS 2打印JSON请求 [英] JAX-RS 2 print JSON request

查看:126
本文介绍了JAX-RS 2打印JSON请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够从请求中打印JAX-RS 2 JSON有效负载,无论我的应用服务器上是否有实际实现。

I'd like to be able to print JAX-RS 2 JSON payload from request, regardless of actual implementation on my application server.

我试过建议SO上的解决方案,但都包括实际实现的二进制文件(如Jersey和类似的),我只允许在我的应用程序中使用javaee-api v 7.0。

I've tried suggested solutions on SO but all include binaries from actual implementation (like Jersey and similar), and I'm allowed only to use javaee-api v 7.0 in my application.

I我尝试在我的客户端上实现ClientRequestFilter和ClientResponseFilter,但它们不包含序列化实体。

I've tried implementing ClientRequestFilter and ClientResponseFilter on my Client but they don't contain serialized entities.

以下是客户端的示例:

WebTarget target = ClientBuilder.newClient().register(MyLoggingFilter.class).target("http://localhost:8080/loggingtest/resources/accounts");
Account acc = target.request().accept(MediaType.APPLICATION_JSON).get(account.Account.class);

这里是MyLoggingFilter的实现:

And here's the implementation of MyLoggingFilter:

@Provider
public class MyLoggingFilter implements ClientRequestFilter, ClientResponseFilter {

    private static final Logger LOGGER = Logger.getLogger(MyLoggingFilter.class.getName());

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {

        LOGGER.log(Level.SEVERE, "Request method: {0}", requestContext.getMethod());

    }

    @Override
    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
        LOGGER.log(Level.SEVERE, "Response status: {0}", responseContext.getStatus());
    }        
}


推荐答案

所以尝试实现此功能时需要考虑几件事情

So there are a couple things to consider when trying to implement this


  1. 对于请求实体,您需要处理序列化通过框架,意味着你想做类似的事情

@Override
public void filter(ClientRequestContext requestContext) {
    Object entity = requestContext.getEntity();
    String serialized = serializeEntity(entity);
    log(serialized);

在这里你自己序列化,也许使用Jackson ObjectMapper 或者什么的。你可以这样做,但它可以处理的类型有限。如果让对象按照框架已经处理的方式进行序列化,那么框架将能够支持比JSON更多的类型。

Here you are serializing it yourself, maybe using Jackson ObjectMapper or something. You could do it this way, but it's kind of limited in the types it can handle. If you let the object be serialized the way it is already handled by the framework, the framework will be able to support many more types than just JSON.

让框架处理序列化,仍然能够获取序列化数据,我们需要使用 WriterInterceptor 。我们可以做的是将实体输出流设置为 ByteArrayOutputStream ,然后让框架将请求对象序列化为我们的 ByteArrayOutputStream ,然后记录这些字节。这就是Jersey LoggingFilter 处理这个问题的方法。

To let the framework handle the serialization, and still be able to get serialized data, we need to use a WriterInterceptor. What we can do is set the entity output stream to a ByteArrayOutputStream, and then let the framework serialize the request object to our ByteArrayOutputStream, then afterwords log those bytes. This is how the Jersey LoggingFilter handles this.

对于响应,在我们的过滤器中,我们需要从响应流中提取数据,但我们需要确保流仍然有数据,因为它尚未针对客户端进行反序列化。要执行此操作 mark() reset()流,假设支持标记。如果没有,请将其包装在 BufferedOutputStream 中。再一次,泽西 LoggingFilter 处理这个问题。

For the response, in our filter, we need to extract the data from the response stream, but also we need to make sure the stream still has data, as it has not been deserialized yet for the client. To do this mark() and reset() the stream, assuming marking is supported. If not, wrap it in a BufferedOutputStream. Again, this is how the Jersey LoggingFilter handles this.

下面是一个完整的简单实现。其中大部分是直接从Jersey LoggingFilter 中获取的,尽管它仅针对您的用例而被删除。 Jersey LoggingFilter 除了实体之外还记录了很多其他信息。我遗漏的一件事是检查字符集。我刚刚使用了硬编码的UTF-8,因为Jersey使用的 MessageUtil 类是特定于Jersey的。如果你想让过滤器对其他字符集更加通用,你可能需要考虑修复它。

Below is a complete simple implementation. Most of it is taken straight from the Jersey LoggingFilter, though it is stripped down just for your use case. The Jersey LoggingFilter logs a lot of other information, aside from the just the entity. One thing I left out is the checking for the charset. I just used a hard coded UTF-8, as the MessageUtil class used by Jersey, is Jersey specific. If you want to make the filter more universal for other charsets, you may want to look into fixing that.

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.logging.Logger;
import javax.annotation.Priority;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;

@Priority(Integer.MIN_VALUE)
public class EntityLoggingFilter implements ClientRequestFilter, ClientResponseFilter, WriterInterceptor {

    private static final Logger logger = Logger.getLogger(EntityLoggingFilter.class.getName());
    private static final String ENTITY_STREAM_PROPERTY = "EntityLoggingFilter.entityStream";
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    private final int maxEntitySize = 1024 * 8;

    private void log(StringBuilder sb) {
        logger.info(sb.toString());
    }

    private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset) throws IOException {
        if (!stream.markSupported()) {
            stream = new BufferedInputStream(stream);
        }
        stream.mark(maxEntitySize + 1);
        final byte[] entity = new byte[maxEntitySize + 1];
        final int entitySize = stream.read(entity);
        b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
        if (entitySize > maxEntitySize) {
            b.append("...more...");
        }
        b.append('\n');
        stream.reset();
        return stream;
    }

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        if (requestContext.hasEntity()) {
            final OutputStream stream = new LoggingStream(requestContext.getEntityStream());
            requestContext.setEntityStream(stream);
            requestContext.setProperty(ENTITY_STREAM_PROPERTY, stream);
        }
    }

    @Override
    public void filter(ClientRequestContext requestContext,
            ClientResponseContext responseContext) throws IOException {
        final StringBuilder sb = new StringBuilder();
        if (responseContext.hasEntity()) {
            responseContext.setEntityStream(logInboundEntity(sb, responseContext.getEntityStream(),
                    DEFAULT_CHARSET));
            log(sb);
        }

    }

    @Override
    public void aroundWriteTo(WriterInterceptorContext context)
            throws IOException, WebApplicationException {
        final LoggingStream stream = (LoggingStream) context.getProperty(ENTITY_STREAM_PROPERTY);
        context.proceed();
        if (stream != null) {
            log(stream.getStringBuilder(DEFAULT_CHARSET));
        }
    }

    private class LoggingStream extends FilterOutputStream {

        private final StringBuilder sb = new StringBuilder();
        private final ByteArrayOutputStream baos = new ByteArrayOutputStream();

        LoggingStream(OutputStream out) {
            super(out);
        }

        StringBuilder getStringBuilder(Charset charset) {
            // write entity to the builder
            final byte[] entity = baos.toByteArray();

            sb.append(new String(entity, 0, entity.length, charset));
            if (entity.length > maxEntitySize) {
                sb.append("...more...");
            }
            sb.append('\n');

            return sb;
        }

        @Override
        public void write(final int i) throws IOException {
            if (baos.size() <= maxEntitySize) {
                baos.write(i);
            }
            out.write(i);
        }
    }
}

另请参见:

这篇关于JAX-RS 2打印JSON请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆