为什么这个HTTP servlet行为不一致?

时间:2011-12-12 03:40:29

标签: java ajax http servlets

Intranet站点有一个搜索表单,该表单使用AJAX调用不同域上的servlet以获取搜索建议。

这适用于Internet Explorer,Intranet域是“受信任的站点”,并且为受信任的站点启用了跨域请求,但在Firefox中不起作用。

我试图通过在Intranet服务器上创建一个servlet来解决这个问题,所以在同一个域上有一个对我的servlet的JS调用,然后我的servlet调用另一个域上的建议servlet。跨域调用是服务器端的,因此无论浏览器设置如何,都应该可以正常工作。

AJAX调用和我的servlet对另一个servlet的调用都使用带有URL参数的HTTP POST请求和空请求内容。

我坚持使用POST请求的原因是JS代码全部在搜索服务器上的文件中,我无法修改,并且该代码使用POST请求。

我尝试使用GET请求调用客户现有的建议servlet,并产生404错误。

问题是结果不一致。

我使用System.out.println次调用来显示服务器日志中结果的完整URL和大小。

输出首先似乎会根据调用的浏览器和/或网站而改变,但现在看来甚至会在同一浏览器的会话之间发生变化。

E.g。在搜索框中输入“g”,我使用Firefox在开发环境的前几次尝试中得到了这个输出:

Search suggestion URL: http://searchdev.companyname.com.au/suggest?q=g&max=10&site=All&client=ie&access=p&format=rich
Search suggestion result length: 64

在测试环境(不同的Intranet服务器但相同的搜索服务器)上使用Firefox进行的初始尝试为同一搜索URL生成了0的结果长度。
Internet Explorer的初始尝试在两种环境中都产生了0的结果长度。

然后我尝试搜索不同的字母,发现当“g”没有时,“t”在IE中产生了结果。

关闭浏览器并暂停一段时间后,我再次尝试并获得了不同的结果。

E.g。使用Firefox并在开发环境中尝试“g”现在在以前生成一个时不会产生任何结果。

这种不一致使我觉得我的servlet代码有问题,如下所示。可能导致问题的原因是什么?

我认为搜索建议是由Google Search Appliance提供的,搜索服务器上的JS文件似乎都来自Google。

实际的AJAX调用是在一个文件中的这一行:

XH_XmlHttpPOST(xmlhttp, url, '', handler);

XH_XmlHttpPOST函数在另一个文件中如下:

function XH_XmlHttpPOST(xmlHttp, url, data, handler) {
  xmlHttp.open("POST", url, true);
  xmlHttp.onreadystatechange = handler;
  xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  xmlHttp.setRequestHeader("Content-Length",
      /** @type {string} */ (data.length));
  XH_XmlHttpSend(xmlHttp, data);
}

这是我的servlet代码:

package com.companyname.theme;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class suggest extends HttpServlet {
    Properties props=null;

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String result = "";
        String args = req.getQueryString();
        String baseURL = props.getProperty("searchFormBaseURL");
        String urlStr = baseURL + "/suggest?" + args;
        System.out.println("Search suggestion URL: " + urlStr);

        try {
            int avail, rCount;
            int totalCount = 0;
            byte[] ba = null;
            byte[] bCopy;
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            OutputStream os = conn.getOutputStream();
            os.write("".getBytes());
            os.close();
            InputStream is = conn.getInputStream();
            while ((avail = is.available()) > 0) {
                if (ba == null) ba = new byte[avail];
                else if (totalCount + avail > ba.length) {
                    // Resize ba if there's more data available.
                    bCopy = new byte[totalCount + avail];
                    System.arraycopy(ba, 0, bCopy, 0, totalCount);
                    ba = bCopy;
                    bCopy = null;
                }
                rCount = is.read(ba, totalCount, avail);
                if (rCount < 0) break;
                totalCount += rCount;
            }
            is.close();
            conn.disconnect();
            result = (ba == null ? "" : new String(ba));
            System.out.println("Search suggestion result length: " + Integer.toString(result.length()));
        } catch(MalformedURLException e) {
            e.printStackTrace();
        } catch(IOException e) {
            e.printStackTrace();
        }
        PrintWriter pw = resp.getWriter();
        pw.print(result);
    }

    @Override
    public void init() throws ServletException {
        super.init();
        InputStream stream = this.getClass().getResourceAsStream("/WEB-INF/lib/endeavour.properties");
        props = new Properties();
        try {
            props.load(stream);
            stream.close();
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
    }

2 个答案:

答案 0 :(得分:1)

解决方案:不要依赖InputStream.available()

该方法的JavaDoc表示它总是返回0 HttpURLConnection.getInputStream()实际上返回一个HttpInputStream,其中available()似乎有效,但显然有时会有更多数据返回0。

我将读取循环更改为根本不使用available(),现在它始终返回预期结果 工作的servlet在下面。

package com.integral.ie.theme;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class suggest extends HttpServlet implements
javax.servlet.Servlet {
    Properties props=null;

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        //super.doPost(req, resp);
        final int maxRead=200;

        String result="";
        String args=req.getQueryString();
        String baseURL=props.getProperty("searchFormBaseURL");
        String urlStr=baseURL+"/suggest?"+args;
        //System.out.println("Search suggestion URL: "+urlStr);
        try {
            int rCount=0;
            int totalCount=0;
            int baLen=maxRead;
            byte[] ba=null;
            byte[] bCopy;
            URL url=new URL(urlStr);
            HttpURLConnection conn=(HttpURLConnection)url.openConnection();
            conn.setRequestMethod("POST");
            // Setting these properties may be unnecessary - just did it
            // because the GSA javascript does it.
            conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
            conn.setRequestProperty("Content-Length","0");

            InputStream is=conn.getInputStream();
            ba=new byte[baLen];
            while (rCount>=0) {
                try {
                    rCount=is.read(ba,totalCount,baLen-totalCount);
                    if (rCount>0) {
                        totalCount+=rCount;
                        if (totalCount>=baLen) {
                            baLen+=maxRead;
                            bCopy=new byte[baLen];
                            System.arraycopy(ba,0,bCopy,0,totalCount);
                            ba=bCopy;
                            bCopy=null;
                        }
                    }
                } catch(IOException e) {
                    // IOException while reading - allow the method to return
                    // anything we've read so far.
                }
            }

            is.close();
            conn.disconnect();
            result=(totalCount==0?"":new String(ba,0,totalCount));

            //System.out.println("Search suggestion result length: "
            //+Integer.toString(result.length()));

        } catch(MalformedURLException e) {
            e.printStackTrace();
        } catch(IOException e) {
            e.printStackTrace();
        }
        PrintWriter pw=resp.getWriter();
        pw.print(result);
    }

    @Override
    public void init() throws ServletException {
        super.init();
        InputStream stream=this.getClass().getResourceAsStream("/WEB-INF/lib/endeavour.properties");
        props=new Properties();
        try {
            props.load(stream);
            stream.close();
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
}

答案 1 :(得分:0)

从单元测试开始。 Servlet很容易进行单元测试,而HttpUnit对我们有用。

在浏览器中调试Servlet代码并使用println调用将花费更多时间从长远来看,SO上的某些人很难消化所有这些信息来帮助您。

另外,请考虑使用JavaScript框架(如JQuery)进行AJAX调用。在我看来,没有理由直接触摸xmlHttp对象,因为框架会为你隐藏它。