developer tip

HTTP 서블릿 요청을 한 번 읽은 후 POST 본문에서 매개 변수가 손실됩니다.

optionbox 2020. 10. 19. 07:59
반응형

HTTP 서블릿 요청을 한 번 읽은 후 POST 본문에서 매개 변수가 손실됩니다.


Java Servlet 필터에서 두 개의 http 요청 매개 변수에 액세스하려고합니다. 여기에는 새로운 것이 없지만 매개 변수가 이미 사용되었다는 사실에 놀랐습니다! 이로 인해 필터 체인에서 더 이상 사용할 수 없습니다.

이는 매개 변수가 POST 요청 본문 (예 : 양식 제출)에 들어올 때만 발생하는 것 같습니다.

매개 변수를 읽고 소비하지 않는 방법이 있습니까?

지금까지이 참조 만 찾았습니다. Servlet Filter using request.getParameter loses Form data .

감사!


제쳐두고,이 문제를 해결하는 다른 방법은 필터 체인을 사용하지 않고 대신 파싱 된 요청 본문에서 작동 할 수있는 측면을 사용하여 자체 인터셉터 구성 요소를 빌드하는 것입니다. 또한 요청 InputStream을 자신의 모델 객체로 한 번만 변환하므로 더 효율적일 수 있습니다 .

그러나 특히 요청이 필터 체인을 통해 이동할 때 요청 본문을 두 번 이상 읽는 것이 여전히 합리적이라고 생각합니다. 일반적으로 서비스 구성 요소와 분리 된 HTTP 계층에서 유지하려는 특정 작업에 필터 체인을 사용합니다.

Will Hartung이 제안한 대로을 확장 HttpServletRequestWrapper하고 요청을 소비 InputStream하며 기본적으로 바이트를 캐싱하여 이를 달성했습니다 .

public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
  private ByteArrayOutputStream cachedBytes;

  public MultiReadHttpServletRequest(HttpServletRequest request) {
    super(request);
  }

  @Override
  public ServletInputStream getInputStream() throws IOException {
    if (cachedBytes == null)
      cacheInputStream();

      return new CachedServletInputStream();
  }

  @Override
  public BufferedReader getReader() throws IOException{
    return new BufferedReader(new InputStreamReader(getInputStream()));
  }

  private void cacheInputStream() throws IOException {
    /* Cache the inputstream in order to read it multiple times. For
     * convenience, I use apache.commons IOUtils
     */
    cachedBytes = new ByteArrayOutputStream();
    IOUtils.copy(super.getInputStream(), cachedBytes);
  }

  /* An inputstream which reads the cached request body */
  public class CachedServletInputStream extends ServletInputStream {
    private ByteArrayInputStream input;

    public CachedServletInputStream() {
      /* create a new input stream from the cached request body */
      input = new ByteArrayInputStream(cachedBytes.toByteArray());
    }

    @Override
    public int read() throws IOException {
      return input.read();
    }
  }
}

이제 요청 본문은 필터 체인을 통과하기 전에 원래 요청을 래핑하여 두 번 이상 읽을 수 있습니다.

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

    /* wrap the request in order to read the inputstream multiple times */
    MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);

    /* here I read the inputstream and do my thing with it; when I pass the
     * wrapped request through the filter chain, the rest of the filters, and
     * request handlers may read the cached inputstream
     */
    doMyThing(multiReadRequest.getInputStream());
    //OR
    anotherUsage(multiReadRequest.getReader());
    chain.doFilter(multiReadRequest, response);
  }
}

이 솔루션을 사용하면 getParameterXXX기본 호출이 getInputStream()물론 캐시 된 요청을 읽을 수 있기 때문에 메서드 를 통해 요청 본문을 여러 번 읽을 수도 있습니다 InputStream.

편집하다

최신 버전의 ServletInputStream인터페이스. isReady, setReadListener등과 같은 몇 가지 메소드의 구현을 제공해야 합니다. 아래 설명에 제공된대로이 질문참조하십시오 .


내가 늦었다는 것을 알고 있지만이 질문은 여전히 ​​나에게 관련이 있었고이 SO 게시물은 Google에서 가장 인기있는 인기 게시물 중 하나였습니다. 다른 사람이 몇 시간을 절약 할 수 있기를 바라며 내 솔루션을 게시하겠습니다.

제 경우에는 모든 요청과 응답을 시체와 함께 기록해야했습니다. Spring Framework를 사용하면 대답은 실제로 매우 간단 합니다. ContentCachingRequestWrapperContentCachingResponseWrapper 만 사용하면 됩니다.

import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }

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

        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);

        try {
            chain.doFilter(requestWrapper, responseWrapper);
        } finally {

            String requestBody = new String(requestWrapper.getContentAsByteArray());
            String responseBody = new String(responseWrapper.getContentAsByteArray());
            // Do not forget this line after reading response content or actual response will be empty!
            responseWrapper.copyBodyToResponse();

            // Write request and response body, headers, timestamps etc. to log files

        }

    }

}

유일한 방법은 필터에서 전체 입력 스트림을 직접 소비하고 원하는 것을 가져온 다음 읽은 콘텐츠에 대한 새 InputStream을 만들고 해당 InputStream을 ServletRequestWrapper (또는 HttpServletRequestWrapper)에 넣는 것입니다.

단점은 직접 페이로드를 구문 분석해야한다는 것입니다. 표준에서는 해당 기능을 사용할 수 없습니다.

부록-

내가 말했듯이 HttpServletRequestWrapper를 봐야합니다.

필터에서 FilterChain.doFilter (request, response)를 호출하여 계속 진행합니다.

사소한 필터의 경우 요청 및 응답은 필터에 전달 된 것과 동일합니다. 그럴 필요는 없습니다. 이를 자신의 요청 및 / 또는 응답으로 바꿀 수 있습니다.

HttpServletRequestWrapper는이를 용이하게하도록 특별히 설계되었습니다. 원래 요청을 전달한 다음 모든 호출을 가로 챌 수 있습니다. 이것의 고유 한 하위 클래스를 만들고 getInputStream 메서드를 고유 한 것으로 대체합니다. 원래 요청의 입력 스트림을 변경할 수 없으므로 대신이 래퍼가 있고 자체 입력 스트림을 반환합니다.

가장 간단한 경우는 원래 요청 입력 스트림을 바이트 버퍼로 소비하고 원하는대로 수행 한 다음 해당 버퍼에서 새 ByteArrayInputStream을 만드는 것입니다. 이것은 FilterChain.doFilter 메소드로 전달되는 랩퍼에서 리턴되는 것입니다.

ServletInputStream의 하위 클래스를 만들고 ByteArrayInputStream에 대한 또 다른 래퍼를 만들어야하지만 그다지 큰 문제는 아닙니다.


위의 답변은 매우 유용했지만 내 경험에는 여전히 문제가있었습니다. tomcat 7 서블릿 3.0에서는 getParamter 및 getParamterValues도 덮어 써야했습니다. 여기의 솔루션에는 get-query 매개 변수와 post-body가 모두 포함됩니다. 원시 문자열을 쉽게 얻을 수 있습니다.

다른 솔루션과 마찬가지로 Apache commons-io 및 Googles Guava를 사용합니다.

이 솔루션에서 getParameter * 메서드는 IOException을 throw하지 않지만 IOException을 throw 할 수있는 super.getInputStream () (본문을 가져 오기 위해)을 사용합니다. 나는 그것을 포착하고 runtimeException을 던진다. 그렇게 좋지 않습니다.

import com.google.common.collect.Iterables;
import com.google.common.collect.ObjectArrays;

import org.apache.commons.io.IOUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ContentType;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * Purpose of this class is to make getParameter() return post data AND also be able to get entire
 * body-string. In native implementation any of those two works, but not both together.
 */
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
    public static final String UTF8 = "UTF-8";
    public static final Charset UTF8_CHARSET = Charset.forName(UTF8);
    private ByteArrayOutputStream cachedBytes;
    private Map<String, String[]> parameterMap;

    public MultiReadHttpServletRequest(HttpServletRequest request) {
        super(request);
    }

    public static void toMap(Iterable<NameValuePair> inputParams, Map<String, String[]> toMap) {
        for (NameValuePair e : inputParams) {
            String key = e.getName();
            String value = e.getValue();
            if (toMap.containsKey(key)) {
                String[] newValue = ObjectArrays.concat(toMap.get(key), value);
                toMap.remove(key);
                toMap.put(key, newValue);
            } else {
                toMap.put(key, new String[]{value});
            }
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (cachedBytes == null) cacheInputStream();
        return new CachedServletInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    private void cacheInputStream() throws IOException {
    /* Cache the inputStream in order to read it multiple times. For
     * convenience, I use apache.commons IOUtils
     */
        cachedBytes = new ByteArrayOutputStream();
        IOUtils.copy(super.getInputStream(), cachedBytes);
    }

    @Override
    public String getParameter(String key) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(key);
        return values != null && values.length > 0 ? values[0] : null;
    }

    @Override
    public String[] getParameterValues(String key) {
        Map<String, String[]> parameterMap = getParameterMap();
        return parameterMap.get(key);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        if (parameterMap == null) {
            Map<String, String[]> result = new LinkedHashMap<String, String[]>();
            decode(getQueryString(), result);
            decode(getPostBodyAsString(), result);
            parameterMap = Collections.unmodifiableMap(result);
        }
        return parameterMap;
    }

    private void decode(String queryString, Map<String, String[]> result) {
        if (queryString != null) toMap(decodeParams(queryString), result);
    }

    private Iterable<NameValuePair> decodeParams(String body) {
        Iterable<NameValuePair> params = URLEncodedUtils.parse(body, UTF8_CHARSET);
        try {
            String cts = getContentType();
            if (cts != null) {
                ContentType ct = ContentType.parse(cts);
                if (ct.getMimeType().equals(ContentType.APPLICATION_FORM_URLENCODED.getMimeType())) {
                    List<NameValuePair> postParams = URLEncodedUtils.parse(IOUtils.toString(getReader()), UTF8_CHARSET);
                    params = Iterables.concat(params, postParams);
                }
            }
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        return params;
    }

    public String getPostBodyAsString() {
        try {
            if (cachedBytes == null) cacheInputStream();
            return cachedBytes.toString(UTF8);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /* An inputStream which reads the cached request body */
    public class CachedServletInputStream extends ServletInputStream {
        private ByteArrayInputStream input;

        public CachedServletInputStream() {
            /* create a new input stream from the cached request body */
            input = new ByteArrayInputStream(cachedBytes.toByteArray());
        }

        @Override
        public int read() throws IOException {
            return input.read();
        }
    }

    @Override
    public String toString() {
        String query = dk.bnr.util.StringUtil.nullToEmpty(getQueryString());
        StringBuilder sb = new StringBuilder();
        sb.append("URL='").append(getRequestURI()).append(query.isEmpty() ? "" : "?" + query).append("', body='");
        sb.append(getPostBodyAsString());
        sb.append("'");
        return sb.toString();
    }
}

나도 같은 문제가 있었고 아래 코드가 더 간단하고 저에게 효과적이라고 생각합니다.

public class MultiReadHttpServletRequest extends  HttpServletRequestWrapper {

 private String _body;

public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException {
   super(request);
   _body = "";
   BufferedReader bufferedReader = request.getReader();           
   String line;
   while ((line = bufferedReader.readLine()) != null){
       _body += line;
   }
}

@Override
public ServletInputStream getInputStream() throws IOException {
   final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(_body.getBytes());
   return new ServletInputStream() {
       public int read() throws IOException {
           return byteArrayInputStream.read();
       }
   };
}

@Override
public BufferedReader getReader() throws IOException {
   return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}

필터 자바 클래스에서,

HttpServletRequest properRequest = ((HttpServletRequest) req);
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(properRequest);
req = wrappedRequest;
inputJson = IOUtils.toString(req.getReader());
System.out.println("body"+inputJson);

궁금한 점이 있으면 알려주세요.


따라서 이것은 기본적으로 Lathy의 답변이지만 ServletInputStream에 대한 새로운 요구 사항에 대해 업데이트되었습니다.

즉 (ServletInputStream의 경우) 다음을 구현해야합니다.

public abstract boolean isFinished();

public abstract boolean isReady();

public abstract void setReadListener(ReadListener var1);

이것은 편집 된 Lathy의 개체입니다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class RequestWrapper extends HttpServletRequestWrapper {

    private String _body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        _body = "";
        BufferedReader bufferedReader = request.getReader();
        String line;
        while ((line = bufferedReader.readLine()) != null){
            _body += line;
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        CustomServletInputStream kid = new CustomServletInputStream(_body.getBytes());
        return kid;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}

그리고 어딘가에서 (??) 이것을 발견했습니다 ( "추가"메소드를 다루는 일류 클래스입니다.

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

public class CustomServletInputStream extends ServletInputStream {

    private byte[] myBytes;

    private int lastIndexRetrieved = -1;
    private ReadListener readListener = null;

    public CustomServletInputStream(String s) {
        try {
            this.myBytes = s.getBytes("UTF-8");
        } catch (UnsupportedEncodingException ex) {
            throw new IllegalStateException("JVM did not support UTF-8", ex);
        }
    }

    public CustomServletInputStream(byte[] inputBytes) {
        this.myBytes = inputBytes;
    }

    @Override
    public boolean isFinished() {
        return (lastIndexRetrieved == myBytes.length - 1);
    }

    @Override
    public boolean isReady() {
        // This implementation will never block
        // We also never need to call the readListener from this method, as this method will never return false
        return isFinished();
    }

    @Override
    public void setReadListener(ReadListener readListener) {
        this.readListener = readListener;
        if (!isFinished()) {
            try {
                readListener.onDataAvailable();
            } catch (IOException e) {
                readListener.onError(e);
            }
        } else {
            try {
                readListener.onAllDataRead();
            } catch (IOException e) {
                readListener.onError(e);
            }
        }
    }

    @Override
    public int read() throws IOException {
        int i;
        if (!isFinished()) {
            i = myBytes[lastIndexRetrieved + 1];
            lastIndexRetrieved++;
            if (isFinished() && (readListener != null)) {
                try {
                    readListener.onAllDataRead();
                } catch (IOException ex) {
                    readListener.onError(ex);
                    throw ex;
                }
            }
            return i;
        } else {
            return -1;
        }
    }
};

궁극적으로 요청을 기록하려고했습니다. 그리고 위의 프랑켄슈타인 조각들은 제가 아래를 만드는 데 도움이되었습니다.

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

//one or the other based on spring version
//import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;

import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.filter.OncePerRequestFilter;


/**
 * A filter which logs web requests that lead to an error in the system.
 */
@Component
public class LogRequestFilter extends OncePerRequestFilter implements Ordered {

    // I tried apache.commons and slf4g loggers.  (one or the other in these next 2 lines of declaration */
    //private final static org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory.getLog(LogRequestFilter.class);
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(LogRequestFilter.class);

    // put filter at the end of all other filters to make sure we are processing after all others
    private int order = Ordered.LOWEST_PRECEDENCE - 8;
    private ErrorAttributes errorAttributes;

    @Override
    public int getOrder() {
        return order;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String temp = ""; /* for a breakpoint, remove for production/real code */

        /* change to true for easy way to comment out this code, remove this if-check for production/real code */
        if (false) {
            filterChain.doFilter(request, response);
            return;
        }

        /* make a "copy" to avoid issues with body-can-only-read-once issues */
        RequestWrapper reqWrapper = new RequestWrapper(request);

        int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
        // pass through filter chain to do the actual request handling
        filterChain.doFilter(reqWrapper, response);
        status = response.getStatus();

        try {
            Map<String, Object> traceMap = getTrace(reqWrapper, status);
            // body can only be read after the actual request handling was done!
            this.getBodyFromTheRequestCopy(reqWrapper, traceMap);

            /* now do something with all the pieces of information gatherered */
            this.logTrace(reqWrapper, traceMap);
        } catch (Exception ex) {
            logger.error("LogRequestFilter FAILED: " + ex.getMessage(), ex);
        }
    }

    private void getBodyFromTheRequestCopy(RequestWrapper rw, Map<String, Object> trace) {
        try {
            if (rw != null) {
                byte[] buf = IOUtils.toByteArray(rw.getInputStream());
                //byte[] buf = rw.getInputStream();
                if (buf.length > 0) {
                    String payloadSlimmed;
                    try {
                        String payload = new String(buf, 0, buf.length, rw.getCharacterEncoding());
                        payloadSlimmed = payload.trim().replaceAll(" +", " ");
                    } catch (UnsupportedEncodingException ex) {
                        payloadSlimmed = "[unknown]";
                    }

                    trace.put("body", payloadSlimmed);
                }
            }
        } catch (IOException ioex) {
            trace.put("body", "EXCEPTION: " + ioex.getMessage());
        }
    }

    private void logTrace(HttpServletRequest request, Map<String, Object> trace) {
        Object method = trace.get("method");
        Object path = trace.get("path");
        Object statusCode = trace.get("statusCode");

        logger.info(String.format("%s %s produced an status code '%s'. Trace: '%s'", method, path, statusCode,
                trace));
    }

    protected Map<String, Object> getTrace(HttpServletRequest request, int status) {
        Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");

        Principal principal = request.getUserPrincipal();

        Map<String, Object> trace = new LinkedHashMap<String, Object>();
        trace.put("method", request.getMethod());
        trace.put("path", request.getRequestURI());
        if (null != principal) {
            trace.put("principal", principal.getName());
        }
        trace.put("query", request.getQueryString());
        trace.put("statusCode", status);

        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            trace.put("header:" + key, value);
        }

        if (exception != null && this.errorAttributes != null) {
            trace.put("error", this.errorAttributes
                    .getErrorAttributes((WebRequest) new ServletRequestAttributes(request), true));
        }

        return trace;
    }
}

이 코드를 소금과 함께 가져 가십시오.

가장 중요한 "테스트"는 POST가 페이로드와 함께 작동하는지 여부입니다. 이것이 "이중 읽기"문제를 노출하는 것입니다.

의사 예제 코드

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("myroute")
public class MyController {
    @RequestMapping(method = RequestMethod.POST, produces = "application/json")
    @ResponseBody
    public String getSomethingExample(@RequestBody MyCustomObject input) {

        String returnValue = "";

        return returnValue;
    }
}

테스트하려는 경우 "MyCustomObject"를 일반 ole "Object"로 바꿀 수 있습니다.

이 답변은 여러 SOF 게시물과 예제에서 frankensteined입니다. 그러나 모든 것을 모으는 데 시간이 걸리므로 미래의 독자에게 도움이되기를 바랍니다.

내 전에 Lathy의 답변을 찬성하십시오. 그것 없이는 여기까지 갈 수 없었습니다.

아래는 이것을 해결하는 동안 내가 얻은 예외 중 하나 / 일부입니다.

이 요청에 대해 getReader ()가 이미 호출되었습니다.

내가 "빌려온"장소 중 일부는 다음과 같습니다.

http://slackspace.de/articles/log-request-body-with-spring-boot/

https://github.com/c0nscience/spring-web-logging/blob/master/src/main/java/org/zalando/springframework/web/logging/LoggingFilter.java

https://howtodoinjava.com/servlets/httpservletrequestwrapper-example-read-request-body/

https://www.oodlestechnologies.com/blogs/How-to-create-duplicate-object-of-httpServletRequest-object

https://github.com/c0nscience/spring-web-logging/blob/master/src/main/java/org/zalando/springframework/web/logging/LoggingFilter.java


Spring에는 다음과 같은 기능이 내장되어 있습니다 AbstractRequestLoggingFilter.

@Bean
public Filter loggingFilter(){
    final AbstractRequestLoggingFilter filter = new AbstractRequestLoggingFilter() {
        @Override
        protected void beforeRequest(final HttpServletRequest request, final String message) {

        }

        @Override
        protected void afterRequest(final HttpServletRequest request, final String message) {

        }
    };

    filter.setIncludePayload(true);
    filter.setIncludeQueryString(false);
    filter.setMaxPayloadLength(1000000);

    return filter;
}

불행히도 여전히 요청에서 직접 페이로드를 읽을 수는 없지만 String 메시지 매개 변수에는 페이로드가 포함되어 있으므로 다음과 같이 가져올 수 있습니다.

String body = message.substring(message.indexOf("{"), message.lastIndexOf("]"));


getInputStream()내 경우에는 덮어 쓰기가 작동하지 않았습니다. 내 서버 구현은이 메서드를 호출하지 않고 매개 변수를 구문 분석하는 것 같습니다. 다른 방법을 찾지 못했지만 4 개의 getParameter * 메서드를 모두 다시 구현합니다. 다음은 getParameterMap(Apache Http Client 및 Google Guava 라이브러리가 사용됨) 의 코드입니다 .

@Override
public Map<String, String[]> getParameterMap() {
    Iterable<NameValuePair> params = URLEncodedUtils.parse(getQueryString(), NullUtils.UTF8);

    try {
        String cts = getContentType();
        if (cts != null) {
            ContentType ct = ContentType.parse(cts);
            if (ct.getMimeType().equals(ContentType.APPLICATION_FORM_URLENCODED.getMimeType())) {
                List<NameValuePair> postParams = URLEncodedUtils.parse(IOUtils.toString(getReader()), NullUtils.UTF8);
                params = Iterables.concat(params, postParams);
            }
        }
    } catch (IOException e) {
        throw new IllegalStateException(e);
    }
    Map<String, String[]> result = toMap(params);
    return result;
}

public static Map<String, String[]> toMap(Iterable<NameValuePair> body) {
    Map<String, String[]> result = new LinkedHashMap<>();
    for (NameValuePair e : body) {
        String key = e.getName();
        String value = e.getValue();
        if (result.containsKey(key)) {
            String[] newValue = ObjectArrays.concat(result.get(key), value);
            result.remove(key);
            result.put(key, newValue);
        } else {
            result.put(key, new String[] {value});
        }
    }
    return result;
}

Spring AbstractRequestLoggingFilter를 보거나 사용하십시오.


요청을 제어 할 수있는 경우 콘텐츠 유형을 binary / octet-stream으로 설정할 수 있습니다. 이를 통해 입력 스트림을 사용하지 않고 매개 변수를 쿼리 할 수 ​​있습니다.

However, this might be specific to some application servers. I only tested tomcat, jetty seems to behave the same way according to https://stackoverflow.com/a/11434646/957103.


First of all we should not read parameters within the filter. Usually the headers are read in the filter to do few authentication tasks. Having said that one can read the HttpRequest body completely in the Filter or Interceptor by using the CharStreams:

String body = com.google.common.io.CharStreams.toString(request.getReader());

This does not affect the subsequent reads at all.


you can use servlet filter chain, but instead use the original one, you can create your own request yourownrequests extends HttpServletRequestWrapper.

참고URL : https://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once

반응형