2011-09-06 51 views
14

Tôi muốn thêm đăng nhập vào Servlet của mình, vì vậy tôi đã tạo Bộ lọc sẽ hiển thị yêu cầu và đi tới Servlet. Nhưng đáng tiếc là tôi đã ngoại lệ hóa trong trường hợp:java.lang.IllegalStateException: getReader() đã được gọi cho yêu cầu này

java.lang.IllegalStateException: getReader() has already been called for this request 
    at org.apache.catalina.connector.Request.getInputStream(Request.java:948) 
    at org.apache.catalina.connector.RequestFacade.getInputStream(RequestFacade.java:338) 
    at com.noelios.restlet.ext.servlet.ServletCall.getRequestEntityStream(ServletCall.java:190) 

Vì vậy, để khắc phục vấn đề này, tôi đã tìm thấy giải pháp với Trình bao bọc, nhưng nó không hoạt động. Tôi có thể sử dụng/thay đổi mã nào khác? Bất kỳ ý tưởng?

[MyHttpServletRequestWrapper]

public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper 
{ 
    public MyHttpServletRequestWrapper(HttpServletRequest request) 
    { 
     super(request); 
    } 

    private String getBodyAsString() 
    { 
     StringBuffer buff = new StringBuffer(); 
     buff.append(" BODY_DATA START [ "); 
     char[] charArr = new char[getContentLength()]; 
     try 
     { 
      BufferedReader reader = new BufferedReader(getReader()); 
      reader.read(charArr, 0, charArr.length); 
      reader.close(); 
     } 
     catch (IOException e) 
     { 
      e.printStackTrace(); 
     } 
     buff.append(charArr); 
     buff.append(" ] BODY_DATA END "); 
     return buff.toString(); 
    } 

    public String toString() 
    { 
     return getBodyAsString(); 
    } 
} 

[MyFilter]

public class MyFilterimplements Filter 
{ 
    @Override 
    public void init(FilterConfig filterConfig) throws ServletException 
    { 
    } 

    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
    { 
     final HttpServletRequest httpServletRequest = (HttpServletRequest) request; 
     final HttpServletResponse httpServletResponse = (HttpServletResponse) response; 

     final HttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(httpServletRequest); 
     final String requestBody = requestWrapper.toString(); 

     chain.doFilter(request, response); 
    } 
} 

Trả lời

9

Hình như khuôn khổ Restlet đã kêu gọi getRequestEntityStream() trên đối tượng Request nó sẽ gọi getInputStream(), vì vậy gọi getReader() theo yêu cầu ném IllegalStateException . Các tài liệu Servlet API cho getReader() và getInputStream() nói:

public java.io.BufferedReader getReader() 
    ... 
    ... 
Throws: 
    java.lang.IllegalStateException - if getInputStream() method has been called on this request 

public ServletInputStream getInputStream() 
    ... 
    ... 
    Throws: 
    java.lang.IllegalStateException - if the getReader() method has already been called for this request 

Từ các tài liệu có vẻ như chúng ta không thể gọi cả hai getReader() và getInputStream() trên đối tượng Request. Tôi khuyên bạn nên sử dụng getInputStream() thay vì getReader() trong trình bao bọc của mình.

5

Vấn đề chính là bạn không thể đọc đầu vào cả dưới dạng luồng nhị phân và luồng ký tự, ngay cả khi luồng được gọi trong bộ lọc và bộ lọc còn lại trong servlet.

3

Theo như tôi có thể nói với servlet về cơ bản bị hỏng trong lĩnh vực này. Bạn có thể thử và làm việc xung quanh vấn đề này như được nêu ra here nhưng điều đó gây ra các vấn đề bí ẩn khác khi những thứ khác cố gắng và làm việc với nó.

Hiệu quả anh ta gợi ý nhân bản yêu cầu, đọc nội dung và sau đó trong lớp nhân bản ghi đè phương thức getReader và getInputStream để trả lại nội dung đã được truy lục.

Mã tôi đã kết thúc với là thế này:

import javax.servlet.ServletInputStream; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletRequestWrapper; 
import java.io.*; 

//this class stops reading the request payload twice causing an exception 
public class WrappedRequest extends HttpServletRequestWrapper 
{ 
    private String _body; 
    private HttpServletRequest _request; 

    public WrappedRequest(HttpServletRequest request) throws IOException 
    { 
     super(request); 
     _request = request; 

     _body = ""; 
     try (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())); 
    } 
} 

Dù sao này dường như làm việc tốt cho đến khi chúng ta nhận ra rằng tải lên một tập tin từ trình duyệt không hoạt động. Tôi chia cắt qua những thay đổi và phát hiện ra đây là thủ phạm.

Một số người trong nhận xét trong bài viết đó nói rằng bạn cần ghi đè các phương pháp để thực hiện với các tham số nhưng không giải thích cách thực hiện việc này.

Kết quả là tôi đã kiểm tra xem có sự khác biệt nào trong hai yêu cầu không. Tuy nhiên sau khi nhân bản yêu cầu, nó có bộ tham số giống hệt nhau (cả hai yêu cầu ban đầu + nhân bản không có) cũng giống như một bộ tiêu đề giống hệt nhau.

Tuy nhiên, theo một cách nào đó yêu cầu đã được thực hiện và vặn vẹo sự hiểu biết về yêu cầu tiếp tục xuống dòng - trong trường hợp của tôi gây ra lỗi bizaare trong thư viện (extdirectspring), nơi có nội dung nào đó đang đọc nội dung là Json. Lấy ra mã đọc cơ thể trong bộ lọc làm cho nó hoạt động trở lại.

mã gọi của tôi trông như thế này:

@Override 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException 
{ 
    HttpServletRequest properRequest = ((HttpServletRequest)request); 

    String pathInfo = properRequest.getPathInfo(); 
    String target = ""; 
    if(pathInfo == null) 
     pathInfo = ""; 

    if(pathInfo.equals("/router")) 
    { 
     //note this is because servlet requests hate you! 
     //if you read their contents more than once then they throw an exception so we need to do some madness 
     //to make this not the case 
     WrappedRequest wrappedRequest = new WrappedRequest(properRequest); 
     target = ParseExtDirectTargetFrom(wrappedRequest); 
     request = wrappedRequest; 
    } 

    boolean callingSpecialResetMethod = pathInfo.equals("/resetErrorState") || target.equals("resetErrorState"); 
    if(_errorHandler.IsRejectingRequests() && !callingSpecialResetMethod) 
     return; 

    try { 
     filterChain.doFilter(request, response); 
    } 
    catch (Exception exception) { 
     ((HttpServletResponse) response).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "ERROR"); 
     _errorHandler.NotifyOf(exception); 
    } 
} 

Tôi đã ommitted nội dung của ParseExtDirectTargetFrom nhưng nó gọi getReader().

Trong trường hợp của tôi bộ lọc đang hoạt động cho tất cả các yêu cầu khác nhưng hành vi lạ trong trường hợp này khiến tôi nhận ra điều gì đó không đúng và những gì tôi đang cố gắng thực hiện (thực hiện hành vi xử lý ngoại lệ hợp lý để kiểm tra) đáng giá có khả năng phá vỡ các yêu cầu ngẫu nhiên trong tương lai (vì tôi không thể tìm ra nguyên nhân khiến yêu cầu bị hỏng).

Ngoài ra nó đáng chú ý là các mã bị hỏng là không thể tránh khỏi - tôi cho rằng nó có thể là một cái gì đó từ mùa xuân nhưng ServletRequest đi tất cả các con đường lên - thats tất cả các bạn có được ngay cả khi bạn đã thực hiện một servlet từ đầu bởi subclassing HttpServlet

Đề xuất của tôi sẽ là điều này - không đọc nội dung yêu cầu trong bộ lọc. Bạn sẽ mở ra một thùng sâu sẽ gây ra những vấn đề lạ sau này.

0

Sử dụng ContentCachingRequestWrapper lớp. Gói HttpServletRequest trong phiên sẽ giải quyết vấn đề

Các vấn đề liên quan