java JAX-RS 2 打印 JSON 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36676017/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 01:44:18  来源:igfitidea点击:

JAX-RS 2 print JSON request

javajsonjakarta-eeloggingjax-rs

提问by D00de

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

我希望能够从请求中打印 JAX-RS 2 JSON 有效负载,而不管我的应用程序服务器上的实际实现如何。

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.

我已经在 SO 上尝试了建议的解决方案,但都包含来自实际实现的二进制文件(如 Jersey 和类似的),并且我只能在我的应用程序中使用 javaee-api v 7.0。

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

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

Here's an example of client:

下面是一个客户端的例子:

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

And here's the implementation of MyLoggingFilter:

这是 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());
    }        
}

回答by Paul Samsotha

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

因此,在尝试实现这一点时需要考虑几件事

  1. For the request entity, you will want the serialization to be handle by the framework, meaning you don'twant to do something like

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

    Here you are serializing it yourself, maybe using Hymanson ObjectMapperor something. You coulddo 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.

    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 ourByteArrayOutputStream, then afterwords log those bytes. This is how the Jersey LoggingFilterhandles this.

  2. For the response, in our filter, we need to extract the data from the response stream, but alsowe 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 LoggingFilterhandles this.

  1. 对于请求实体,你会想序列化是由框架处理,这意味着你希望这样做

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

    在这里你自己序列化它,也许使用Hyman逊ObjectMapper或其他东西。你可以这样做,但它可以处理的类型有限。如果您让对象按照框架已经处理的方式进行序列化,那么框架将能够支持更多类型,而不仅仅是 JSON。

    为了让框架处理序列化,并且仍然能够获取序列化数据,我们需要使用WriterInterceptor. 我们可以做的是将实体输出流设置为 a ByteArrayOutputStream,然后让框架将请求对象序列化为我们的ByteArrayOutputStream,然后记录这些字节。这就是泽西岛的LoggingFilter处理方式。

  2. 对于响应,在我们的过滤器中,我们需要从响应流中提取数据,但我们需要确保流仍然有数据,因为它尚未为客户端反序列化。要做到这一点mark()reset()流,假设支持标记。如果没有,请将其包装在BufferedOutputStream. 同样,这就是泽西岛的LoggingFilter处理方式。

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 LoggingFilterlogs 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 MessageUtilclass 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.

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

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

See Also:

也可以看看: