2016-04-17 18 views
10

Tôi muốn có thể in tải JAX-RS 2 JSON theo yêu cầu, bất kể triển khai thực tế trên máy chủ ứng dụng của tôi.JAX-RS 2 in Yêu cầu JSON

Tôi đã thử các giải pháp được đề xuất trên SO nhưng tất cả đều bao gồm các tệp nhị phân từ việc triển khai thực tế (như Jersey và tương tự) và tôi chỉ được phép sử dụng javaee-api v 7.0 trong ứng dụng của mình.

Tôi đã thử triển khai ClientRequestFilter và ClientResponseFilter trên Máy khách của mình nhưng chúng không chứa các thực thể được tuần tự hóa.

Dưới đây là một ví dụ về khách hàng:

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

Và đây là việc thực hiện các 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()); 
    }   
} 

Trả lời

15

Vì vậy, có một vài điều cần xem xét khi cố gắng thực hiện điều này

  1. Đối với thực thể yêu cầu, bạn sẽ muốn serialization được xử lý bởi framework, meanin g bạn không muốn làm một cái gì đó giống như

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

    Ở đây bạn đang serializing nó cho mình, có thể sử dụng Jackson ObjectMapper hoặc một cái gì đó. Bạn có thể thực hiện theo cách này, nhưng nó bị hạn chế về các loại mà nó có thể xử lý. Nếu bạn để cho đối tượng được tuần tự hóa theo cách nó đã được khung công tác xử lý, khung công tác sẽ có thể hỗ trợ nhiều loại hơn là chỉ JSON.

    Để cho khuôn khổ xử lý tuần tự hóa và vẫn có thể nhận dữ liệu được tuần tự hóa, chúng tôi cần sử dụng WriterInterceptor. Những gì chúng tôi có thể làm là đặt luồng đầu ra của đối tượng thành một số ByteArrayOutputStream và sau đó để cho khung làm cho đối tượng yêu cầu nối tiếp với ByteArrayOutputStream của chúng tôi, sau đó, sau khi ghi lại các byte đó. Đây là cách Jersey LoggingFilter xử lý việc này. Đối với câu trả lời, trong bộ lọc của chúng tôi, chúng tôi cần trích xuất dữ liệu từ luồng phản hồi, nhưng cũng chúng tôi cần đảm bảo luồng vẫn có dữ liệu vì dữ liệu chưa được deserialized cho khách hàng. Để làm điều này mark()reset() luồng, giả sử đánh dấu được hỗ trợ. Nếu không, hãy bọc nó trong một số BufferedOutputStream. Một lần nữa, đây là cách Jersey LoggingFilter xử lý việc này.

Dưới đây là triển khai đơn giản hoàn chỉnh. Hầu hết nó được lấy thẳng từ Jersey LoggingFilter, mặc dù nó bị tước xuống chỉ dành cho trường hợp sử dụng của bạn. Jersey LoggingFilter ghi lại rất nhiều thông tin khác, ngoài thực thể. Một điều tôi bỏ ra là kiểm tra bộ ký tự. Tôi chỉ sử dụng một mã UTF-8 cứng, như là lớp MessageUtil được sử dụng bởi Jersey, là Jersey cụ thể. Nếu bạn muốn làm cho bộ lọc phổ quát hơn đối với các bộ ký tự khác, bạn có thể muốn xem xét việc sửa bộ lọc đó.

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

Xem thêm:

+0

Các công trình lớn. Tôi chỉ cần chú thích bộ lọc là @Provider và đăng ký nó trên Máy khách của tôi và nó ghi nhật ký JSON từ Thực thể đã đăng. BTW, tôi đang sử dụng JAX-B để sắp xếp các thực thể. Cảm ơn thời gian tốt đẹp của bạn, có upvote của tôi và chấp nhận. – D00de

+0

Tuyệt vời để gỡ lỗi! –