2012-07-25 15 views
8

Estoy usando Jersey para crear recursos RESTful API y ResponseBuilder para generar la respuesta.Jersey/JAX-RS: Devuelve Content-Length en el encabezado de respuesta en lugar de codificación de transferencia fragmentada

código de ejemplo para el recurso REST:

public class infoResource{ 
    @GET 
    @Path("service/{id}") 
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) 
    public Response getCompany(@PathParam("id")String id) { 
     //company is just a POJO. 
     Company company = getCompany(id); 
     return Response.status(200).entity(company).build(); 
    } 
} 

en la respuesta, que está volviendo fragmentada codificación de transferencia en las cabeceras de respuesta. ¿Cuál es la forma adecuada en el "mundo de Jersey" para que devuelva el encabezado Content-Length en lugar del encabezado Transfer-Encoding: chunked en los encabezados de respuesta?

Respuesta

4

Seleccionar Content-Length o Transfer-Encoding es solo la opción de Contenedores. Es realmente una cuestión de tamaño del búfer.

Una posible solución es proporcionar un SevletFilter que almacena en búfer todos esos bytes marshalled y establece Content-Length valor del encabezado.

Ver this page.

@WebFilter 
public class BufferFilter implements Filter { 

    @Override 
    public void init(FilterConfig filterConfig) { 
    } 

    @Override 
    public void doFilter(ServletRequest request, 
         ServletResponse response, 
         FilterChain chain) 
     throws IOException, ServletException { 

     final ByteArrayOutputStream buffer = 
      new ByteArrayOutputStream(); 

     // prepare a new ServletResponseWrapper 
     // which returns the buffer as its getOutputStream(); 

     chain.doFilter(...) 

     // now you know how exactly big is your response. 

     final byte[] responseBytes = buffer.toByteArray(); 
     response.setContentLength(responseBytes.length); 
     response.getOutputStream().write(responseBytes); 
     response.flush(); 
    } 

    @Override 
    public void destroy() { 
    } 
} 
+2

Sería preferible tamponar a un archivo (para evitar quedarse sin de memoria -> si necesita devolver respuestas grandes). Pero veo tu punto aquí. – tuga

+0

[aquí hay un filtro completo de ejemplo que funciona] (https://stackoverflow.com/a/46122629/26510) para esta solución, que sigue usando un 'ByteArrayOutputStream', pero es fácil de acceder y probar ... fyi .... –

2

Por ejemplo, si el flujo de entrada se lee de un sistema de archivos local, justo complemento:

response.header("Content-Length", file.length()); 

comprobar el código completo para una explicación más clara:

@Path("/files") 
public class FileDownloadService { 

    private static final String TXT_FILE = "C:\\your file";  
    @GET 
    @Path("/txt") 
    @Produces(MediaType.APPLICATION_OCTET_STREAM) 
    public Response getTextFile() throws IOException { 
     File file = new File(TXT_FILE); 
     FileInputStream inStream = new FileInputStream(file); 
     ResponseBuilder response = Response.ok((Object) inStream); 
     response.header("Content-Disposition", "attachment; filename=\"filename\""); 
     response.header("Content-Length", file.length()); 
     return response.build(); 
    } 
} 

El cliente lado es un código Apache HttpClient.

2

En su clase que extiende ResourceConfig puede establecer el tamaño del búfer. Las respuestas por encima de este tamaño se dividirán en fragmentos, a continuación tendrá Content-Length.

public class ApplicationConfig extends ResourceConfig { 

    public ApplicationConfig() { 
    //your initialization 
    property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 2000000); 
    } 
} 
0

una respuesta a una pregunta muy similar en StackOverflow puede be found here

he copiado aquí para asegurarse de que no se convierte en un comentario:

Un gran filtro de muestra para hacer esto, que se puede usar de forma independiente desde el proyecto, es this ContentLengthFilter.java del proyecto Carrot2 en github.

Nota que utiliza un envoltorio de respuesta con un flujo de bytes para resolver el problema, por lo que también se asegura de que Transfer-Encoding: Chunked no quede ajustado por algún otro filtro/código en la cadena de filtros, y anular su cabecera Content-Length cuando se ajusta. Puede verificarlo probando esto con archivos más grandes, ya que normalmente se fragmentarían en la respuesta.

voy a copiar el contenido del archivo aquí también, para asegurarse de que no se convierta en un enlace roto:

/* 
* Carrot2 project. 
* 
* Copyright (C) 2002-2010, Dawid Weiss, Stanisław Osiński. 
* All rights reserved. 
* 
* Refer to the full license file "carrot2.LICENSE" 
* in the root folder of the repository checkout or at: 
* http://www.carrot2.org/carrot2.LICENSE 
*/ 

package org.carrot2.webapp; 

import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.OutputStreamWriter; 
import java.io.PrintWriter; 

import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletOutputStream; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpServletResponseWrapper; 

/** 
* Buffer the output from filters below and set accurate <code>Content-Length</code> 
* header. This header is required by flash, among others, to display progress 
* information. 
*/ 
public class ContentLengthFilter implements Filter 
{ 
    private final static class BufferingOutputStream extends ServletOutputStream 
    { 
     private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

     @Override 
     public void write(int b) throws IOException 
     { 
      baos.write(b); 
     } 

     @Override 
     public void write(byte [] b) throws IOException 
     { 
      baos.write(b); 
     } 

     @Override 
     public void write(byte [] b, int off, int len) throws IOException 
     { 
      baos.write(b, off, len); 
     } 
    } 

    private final static class BufferingHttpServletResponse extends 
     HttpServletResponseWrapper 
    { 
     private enum StreamType 
     { 
      OUTPUT_STREAM, 
      WRITER 
     } 

     private final HttpServletResponse httpResponse; 

     private StreamType acquired; 
     private PrintWriter writer; 
     private ServletOutputStream outputStream; 
     private boolean buffering; 

     public BufferingHttpServletResponse(HttpServletResponse response) 
     { 
      super(response); 
      httpResponse = response; 
     } 

     @Override 
     public ServletOutputStream getOutputStream() throws IOException 
     { 
      if (acquired == StreamType.WRITER) 
       throw new IllegalStateException("Character stream already acquired."); 

      if (outputStream != null) 
       return outputStream; 

      if (hasContentLength()) 
      { 
       outputStream = super.getOutputStream(); 
      } 
      else 
      { 
       outputStream = new BufferingOutputStream(); 
       buffering = true; 
      } 

      acquired = StreamType.OUTPUT_STREAM; 
      return outputStream; 
     } 

     @Override 
     public PrintWriter getWriter() throws IOException 
     { 
      if (acquired == StreamType.OUTPUT_STREAM) 
       throw new IllegalStateException("Binary stream already acquired."); 

      if (writer != null) 
       return writer; 

      if (hasContentLength()) 
      { 
       writer = super.getWriter(); 
      } 
      else 
      { 
       writer = new PrintWriter(new OutputStreamWriter(
        getOutputStream(), getCharacterEncoding()), false); 
      } 

      acquired = StreamType.WRITER; 
      return writer; 
     } 

     /** 
     * Returns <code>true</code> if the user set <code>Content-Length</code> 
     * explicitly. 
     */ 
     private boolean hasContentLength() 
     { 
      return super.containsHeader("Content-Length"); 
     } 

     /** 
     * Push out the buffered data. 
     */ 
     public void pushBuffer() throws IOException 
     { 
      if (!buffering) 
       throw new IllegalStateException("Not buffering."); 

      BufferingOutputStream bufferedStream = 
       (BufferingOutputStream) outputStream; 

      byte [] buffer = bufferedStream.baos.toByteArray(); 
      httpResponse.setContentLength(buffer.length); 
      httpResponse.getOutputStream().write(buffer); 
     } 
    } 

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
     throws IOException, ServletException 
    { 
     final HttpServletResponse response = (HttpServletResponse) resp; 
     final BufferingHttpServletResponse wrapped = 
      new BufferingHttpServletResponse(response); 

     chain.doFilter(req, wrapped); 

     if (wrapped.buffering) 
     { 
      wrapped.pushBuffer(); 
     } 
    } 

    public void destroy() 
    { 
     // Empty 
    } 

    public void init(FilterConfig config) throws ServletException 
    { 
     // Empty 
    } 
} 
Cuestiones relacionadas