如何更改Java Filter中的HTTP响应内容长度标题 [英] How to change the HTTP response content length header in Java Filter

查看:682
本文介绍了如何更改Java Filter中的HTTP响应内容长度标题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经写了一个Java HTTP Response过滤器,我正在修改HTTP响应正文。由于我正在改变HTTP响应正文,所以我必须根据新的内容来更新http内容长度的头文件。我正在这样做。

  response.setContentLength(next.getBytes()。length); 

听到下一个是字符串



但是,这种方法无法设置HTTP响应的新内容长度。有人可以告诉我什么是正确的方法来完成它在Java过滤器

$ $ $ code包com.test;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
导入javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;



public class DumpFilter implements Filter {

private static class ByteArrayServletStream extends ServletOutputStream {

ByteArrayOutputStream baos;

ByteArrayServletStream(ByteArrayOutputStream baos){
this.baos = baos;

$ b $ public void write(int param)throws IOException {
baos.write(param);


$ b $ private static class ByteArrayPrintWriter {

private ByteArrayOutputStream baos = new ByteArrayOutputStream();

private PrintWriter pw = new PrintWriter(baos);

private ServletOutputStream sos = new ByteArrayServletStream(baos);

public PrintWriter getWriter(){
return pw;
}

public ServletOutputStream getStream(){
return sos;
}

byte [] toByteArray(){
return baos.toByteArray();



private class BufferedServletInputStream extends ServletInputStream {

ByteArrayInputStream bais;

public BufferedServletInputStream(ByteArrayInputStream bais){
this.bais = bais;


public int available(){
return bais.available();


public int read(){
return bais.read();


public int read(byte [] buf,int off,int len){
return bais.read(buf,off,len);



$ b private class BufferedRequestWrapper extends HttpServletRequestWrapper {

ByteArrayInputStream bais;

ByteArrayOutputStream baos;

BufferedServletInputStream bsis;

byte [] buffer;

public BufferedRequestWrapper(HttpServletRequest req)throws IOException {
super(req);
InputStream is = req.getInputStream();
baos = new ByteArrayOutputStream();
字节buf [] =新字节[1024];
int letti; ((letti = is.read(buf))> 0){
baos.write(buf,0,letti);
}
buffer = baos.toByteArray();

$ b $ public ServletInputStream getInputStream(){
try {
bais = new ByteArrayInputStream(buffer);
bsis = new BufferedServletInputStream(bais);
catch(Exception ex){
ex.printStackTrace();
}

return bsis;
}

public byte [] getBuffer(){
return buffer;
}

}

私人布尔dumpRequest;
私有布尔dumpResponse;
$ b $ public void init(FilterConfig filterConfig)throws ServletException {
dumpRequest = Boolean.valueOf(filterConfig.getInitParameter(dumpRequest));
dumpResponse = Boolean.valueOf(filterConfig.getInitParameter(dumpResponse));

$ b $ public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,
FilterChain filterChain)throws IOException,ServletException {

final HttpServletRequest httpRequest =(HttpServletRequest)的servletRequest;
BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(httpRequest);

if(dumpRequest){
System.out.println(REQUEST - >+ new String(bufferedRequest.getBuffer()));
}

final HttpServletResponse response =(HttpServletResponse)servletResponse;

final ByteArrayPrintWriter pw = new ByteArrayPrintWriter();
HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response){
public PrintWriter getWriter(){
return pw.getWriter();

$ b $ public ServletOutputStream getOutputStream(){
return pw.getStream();
}

};

filterChain.doFilter(bufferedRequest,wrappedResp);

byte [] bytes = pw.toByteArray();

String s = new String(bytes);

String next =test message;

response.getOutputStream()。write(next.getBytes());
///response.setHeader(\"Content-Length,String.valueOf(next.length()));
response.setContentLength(next.getBytes()。length);
// if(dumpResponse)System.out.println(RESPONSE - >+ s);

$ b $ public void destroy(){}
$ b}

$ b $上面给出的b

是Filter类,但是你可能不需要读整个类。以下是我在修改http正文和设置内容长度字段的doFilter代码。



public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain)抛出IOException,ServletException {

final HttpServletRequest httpRequest =(HttpServletRequest)servletRequest;
BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(httpRequest);

if(dumpRequest){
System.out.println(REQUEST - >+ new String(bufferedRequest.getBuffer()));
}

final HttpServletResponse response =(HttpServletResponse)servletResponse;

final ByteArrayPrintWriter pw = new ByteArrayPrintWriter();
HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response){
public PrintWriter getWriter(){
return pw.getWriter();

$ b $ public ServletOutputStream getOutputStream(){
return pw.getStream();
}

};

filterChain.doFilter(bufferedRequest,wrappedResp);

byte [] bytes = pw.toByteArray();

String s = new String(bytes);

String next =test message;

response.getOutputStream()。write(next.getBytes());
///response.setHeader(\"Content-Length,String.valueOf(next.length()));
response.setContentLength(next.getBytes()。length);
// if(dumpResponse)System.out.println(RESPONSE - >+ s);


解决方案

这个可以从项目中独立使用,是 Carrot2 项目中的ContentLengthFilter.java rel =nofollow noreferrer> github。

请注意,使用一个字节流的响应包装来解决这个问题,所以这也保证了 Transfer-Encoding:Chunked

code>不会被过滤器链中的其他过滤器/代码设置,并且在设置时覆盖您的 Content-Length 标头。你可以通过测试这个更大的文件来验证,因为它们通常会在响应中被分块。

我也要在这里复制文件的内容,以确保它不会成为断开的链接。如果你打算在生产环境中使用它,你应该确保你使用合适的缓冲区大小来创建你的 ByteArrayOutputStream ,或者考虑将其切换为另一种类型的流,可能是FileOutputStream。

  / * 
* Carrot2项目。
*
* Copyright(C)2002-2010,Dawid Weiss,StanisławOsiński。
*保留所有权利。
*
*请参阅资料库结帐根文件夹中的完整许可证文件carrot2.LICENSE
*或
* 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;
导入javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/ **
*缓冲下面的过滤器的输出并设置精确的< code> Content-Length< / code>
*标题。这个头是Flash所需要的,以显示进度
*信息。
* /
public class ContentLengthFilter implements Filter
{
private final final static class BufferingOutputStream extends ServletOutputStream
{
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();

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

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



私有最终静态类BufferingHttpServletResponse扩展
HttpServletResponseWrapper
{
私有枚举StreamType
{
OUTPUT_STREAM,
WRITER
}

private final HttpServletResponse httpResponse;

私有StreamType获取;
私人PrintWriter作家;
private ServletOutputStream outputStream;
私有布尔缓冲;

public BufferingHttpServletResponse(HttpServletResponse响应)
{
super(response);
httpResponse =回应;

$ b @Override
public ServletOutputStream getOutputStream()抛出IOException
{
if(acquire == StreamType.WRITER)
throw new IllegalStateException(字符流已经获得。);

if(outputStream!= null)
return outputStream;

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

acquire = StreamType.OUTPUT_STREAM;
返回outputStream;

$ b @Override
public PrintWriter getWriter()抛出IOException
{
if(acquire == StreamType.OUTPUT_STREAM)
throw new IllegalStateException(已经获得二进制流。

if(writer!= null)
return writer;

if(hasContentLength())
{
writer = super.getWriter();


{
writer = new PrintWriter(new OutputStreamWriter(
getOutputStream(),getCharacterEncoding()),false);
}

acquire = StreamType.WRITER;
返回作者;
}

/ **
*返回< code> true< / code>如果用户设置< code> Content-Length< / code>
*明确。
* /
private boolean hasContentLength()
{
return super.containsHeader(Content-Length);
}

/ **
*推出缓冲的数据。
* /
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);


$ b public void doFilter(ServletRequest req,ServletResponse resp,FilterChain链)
抛出IOException,ServletException
{
final HttpServletResponse response =(HttpServletResponse)resp;
final BufferingHttpServletResponse wrapped =
BufferingHttpServletResponse(response);

chain.doFilter(req,wrapped);

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



public void destroy()
{
// Empty
}

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


I have written a Java HTTP Response filter in which I am modifying the HTTP response body. Since I am changing the HTTP responce body, I have to update the http content-length header filed in response in accordance with new content. I am doing it in the following way.

response.setContentLength( next.getBytes().length );

hear next is a string

However, this method is unable to set the new content length of the HTTP response. Could somebody advice me whats the correct way to get it done in Java filter

package com.test;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;



public class DumpFilter implements Filter {

      private static class ByteArrayServletStream extends ServletOutputStream {

        ByteArrayOutputStream baos;

        ByteArrayServletStream(ByteArrayOutputStream baos) {
          this.baos = baos;
        }

        public void write(int param) throws IOException {
          baos.write(param);
        }
      }

      private static class ByteArrayPrintWriter {

        private ByteArrayOutputStream baos = new ByteArrayOutputStream();

        private PrintWriter pw = new PrintWriter(baos);

        private ServletOutputStream sos = new ByteArrayServletStream(baos);

        public PrintWriter getWriter() {
          return pw;
        }

        public ServletOutputStream getStream() {
          return sos;
        }

        byte[] toByteArray() {
          return baos.toByteArray();
        }
      }

      private class BufferedServletInputStream extends ServletInputStream {

        ByteArrayInputStream bais;

        public BufferedServletInputStream(ByteArrayInputStream bais) {
          this.bais = bais;
        }

        public int available() {
          return bais.available();
        }

        public int read() {
          return bais.read();
        }

        public int read(byte[] buf, int off, int len) {
          return bais.read(buf, off, len);
        }

      }

      private class BufferedRequestWrapper extends HttpServletRequestWrapper {

        ByteArrayInputStream bais;

        ByteArrayOutputStream baos;

        BufferedServletInputStream bsis;

        byte[] buffer;

        public BufferedRequestWrapper(HttpServletRequest req) throws IOException {
          super(req);
          InputStream is = req.getInputStream();
          baos = new ByteArrayOutputStream();
          byte buf[] = new byte[1024];
          int letti;
          while ((letti = is.read(buf)) > 0) {
            baos.write(buf, 0, letti);
          }
          buffer = baos.toByteArray();
        }

        public ServletInputStream getInputStream() {
          try {
            bais = new ByteArrayInputStream(buffer);
            bsis = new BufferedServletInputStream(bais);
          } catch (Exception ex) {
            ex.printStackTrace();
          }

          return bsis;
        }

        public byte[] getBuffer() {
          return buffer;
        }

      }

      private boolean dumpRequest;
      private boolean dumpResponse;

      public void init(FilterConfig filterConfig) throws ServletException {
        dumpRequest = Boolean.valueOf(filterConfig.getInitParameter("dumpRequest"));
        dumpResponse = Boolean.valueOf(filterConfig.getInitParameter("dumpResponse"));
      }

      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
          FilterChain filterChain) throws IOException, ServletException {

        final HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
        BufferedRequestWrapper bufferedRequest= new BufferedRequestWrapper(httpRequest);

        if (dumpRequest) {
            System.out.println("REQUEST -> " + new String(bufferedRequest.getBuffer()));
        }

        final HttpServletResponse response = (HttpServletResponse) servletResponse;

        final ByteArrayPrintWriter pw = new ByteArrayPrintWriter();
        HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {
          public PrintWriter getWriter() {
            return pw.getWriter();
          }

          public ServletOutputStream getOutputStream() {
            return pw.getStream();
          }

        };

        filterChain.doFilter(bufferedRequest, wrappedResp);

        byte[] bytes = pw.toByteArray();

        String s = new String(bytes);

        String next = "test message";

        response.getOutputStream().write(next.getBytes());
        ///response.setHeader("Content-Length", String.valueOf(next.length()));
        response.setContentLength( next.getBytes().length );
       // if (dumpResponse) System.out.println("RESPONSE -> " + s);
      }

      public void destroy() {}

    }

given above is the Filter class, but you may not need to read the whole class. Following is the doFilter code where I am modifying the http body and setting the content length filed.

  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
              FilterChain filterChain) throws IOException, ServletException {

            final HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
            BufferedRequestWrapper bufferedRequest= new BufferedRequestWrapper(httpRequest);

            if (dumpRequest) {
                System.out.println("REQUEST -> " + new String(bufferedRequest.getBuffer()));
            }

            final HttpServletResponse response = (HttpServletResponse) servletResponse;

            final ByteArrayPrintWriter pw = new ByteArrayPrintWriter();
            HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {
              public PrintWriter getWriter() {
                return pw.getWriter();
              }

              public ServletOutputStream getOutputStream() {
                return pw.getStream();
              }

            };

            filterChain.doFilter(bufferedRequest, wrappedResp);

            byte[] bytes = pw.toByteArray();

            String s = new String(bytes);

            String next = "test message";

            response.getOutputStream().write(next.getBytes());
            ///response.setHeader("Content-Length", String.valueOf(next.length()));
            response.setContentLength( next.getBytes().length );
           // if (dumpResponse) System.out.println("RESPONSE -> " + s);
          }

解决方案

A great sample filter for doing this, that can be used standalone from the project, is this ContentLengthFilter.java from the Carrot2 project on github.

Note that uses a response wrapper with a byte stream to solve the issue, so this also ensures that Transfer-Encoding: Chunked doesn't get set by some other Filter/code in the filter chain, and override your Content-Length header when it's set. You can verify that by testing this with larger files, as they'd normally be chunked in the response.

I'm going to copy the contents of the file here as well, to ensure it doesn't become a broken link. If you're going to use this in a production environment, you should make sure you either create your ByteArrayOutputStream with an appropriate buffer size, or consider switching it out for another type of stream, possible a FileOutputStream.

/*
 * 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
    }
}

这篇关于如何更改Java Filter中的HTTP响应内容长度标题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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