1. 程式人生 > >使用 ajax 解決瀏覽器快取功能

使用 ajax 解決瀏覽器快取功能

使用ajax時候瀏覽器快取功能造成的問題。

網站中使用到ajax的功能非常簡單。這裡先簡單描述一下,有兩個下拉框A,B。下拉框B的內容是根據使用者對A的選擇而變化的。這裡的主要流程就一目瞭然了:

使用者更改了下拉框A的選項;
瀏覽器向伺服器傳送一個非同步的命令;
伺服器接收到之後查詢資料庫;
伺服器把查詢結構封裝成xml傳送到客戶端;
客戶端解析xml資料,修改下拉框B的選項條目。
根據上面的大綱,先做第一步,在下拉框A上註冊onchange事件。

<select id="area" onchange="AreaChange()">響應的JS函式是AreaChange(),很明顯這個函式的任務就是傳送一個非同步命令到伺服器端。

function AreaChange()
{
 var area = document.getElementById("area").value;
 var roleID = document.getElementsByName("rolename")[0].value;
 var url = "PrivilegeUpdate?area=" + area + "&roleid=" + roleID;
 createXMLHttpRequest();
 xmlHttp.onreadystatechange = AreaChangeReady;
 xmlHttp.open("GET", url, true);
 xmlHttp.send(null);
}函式中首先得到下拉框選中的值,然後拼接成url,傳送到伺服器。由於是非同步操作,因此還要註冊一個接收訊息的響應函式AreaChangeReady。

接下來就是伺服器端的操作了,伺服器端的程式碼我是用java寫的。

public class PrivilegeUpdate extends HttpServlet
{
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
 {
  response.setContentType("text/html charset=UTF-8");
  String area = request.getParameter("area");
  areaDB Area = new areaDB();
  lineTable [] lt = Area.getLine(area);
  if(lt == null)
   return;
  StringBuffer results = new StringBuffer("<root><line>");
  for(int i=0;i<lt.length;i++)
  {
   results.append("<id>");
   results.append(lt[i].getLineID());
   results.append("</id>");
   results.append("<name>");
   results.append(lt[i].getLineName());
   results.append("</name>");
  }
  results.append("</line>");
  results.append("</root>");
  response.setContentType("text/xml");
  response.getWriter().write(results.toString());
 }
}這個類繼承了HttpServlet類,並且過載了doGet方法,由於以xml的方式傳送到客戶端,設定ContentType為text/xml。然後根據返回的結果拼接xml字串。

接下來就是客戶端的AreaChangeReady開始執行了。

function AreaChangeReady()
{
 if(xmlHttp.readyState == 4)
 {
  if(xmlHttp.status == 200)
  {
   updateLine();
   updateUnAddPrivilege();
  }
 }
}第二個函式呼叫可以不去理會,重要的是updateLine函式,主要功能是更新下拉框B。

function updateLine()
{
 clearLine();
 var linexml = null;
 linexml = xmlHttp.responseXML.getElementsByTagName("line");
 var line = document.getElementById("line");
 var option = null;
 if(linexml == null || linexml.length == 0)
 {
  option = document.createElement("option");
  option.setAttribute("value", "");
  option.appendChild(document.createTextNode("無線路"));
  line.appendChild(option);
  return;
 }
 var lineID = linexml[0].getElementsByTagName("id");
 var lineName = linexml[0].getElementsByTagName("name");
 for(var i=0;i<lineID.length;i++)
 {
  option = document.createElement("option");
  option.setAttribute("value", lineID[i].firstChild.nodeValue);
  option.appendChild(document.createTextNode(lineName[i].firstChild.nodeValue));
  line.appendChild(option);
 }
}解析了伺服器發來的xml資料,然後根據讀取的資料更新下拉框B。

上面的程式碼從理論上來說沒有問題,一些簡單的測試也符合預期。但是在進一步的測試中就出現了莫名其妙的問題。我更新資料庫中的資料,然後再進行選擇下拉框A的操作,下拉框B並不會如我預期那樣顯示正確的資料。這裡很顯然,肯定就是瀏覽器快取的原因了。

第一次選擇下拉框A的時候瀏覽器會如預期一樣向伺服器傳送請求,然後伺服器返回,瀏覽器更新下拉框B的選項。但同時,瀏覽器也做了個小動作,儲存了這個資料以備下一次使用。所以第二次,瀏覽器將不再向伺服器傳送資料,而是直接從快取中拿出資料,更新下拉框B的選項。如果這時候更新了資料庫,悲劇也就發生了。

明白了這個道理,解決方案當然也就出來了:不要讓瀏覽器快取這個xml資料。HTTP協議本身就可以設定瀏覽器是否進行快取。

在servlet的doGet方法進行輸出之前加入這麼一行程式碼:

response.setHeader("Cache-Control", "no-cache");但是問題還是存在,雖然servlet中看上去只只有一個輸出

response.getWriter().write(results.toString());但是方法還有一個出口那就是return。一開始我也以為return之後並不會向瀏覽器傳送資料,但是我錯了,雖然沒有傳送資料,瀏覽器也會把沒有資料這個結果快取起來。這個錯誤相當隱蔽,我被這個錯誤折磨了2天之後才突然明白過來。修改後的程式碼如下

public class PrivilegeUpdate extends HttpServlet
{
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
 {
  response.setContentType("text/html charset=UTF-8");
  String area = request.getParameter("area");
  String roleid = request.getParameter("roleid");
  areaDB Area = new areaDB();
  lineTable [] lt = Area.getLine(area);
  if(lt == null)
  {
   response.setContentType("text/xml");
   response.setHeader("Cache-Control", "no-cache");
   response.getWriter().write("<root></root>");
   return;
  }
  StringBuffer results = new StringBuffer("<root><line>");
  for(int i=0;i<lt.length;i++)
  {
   results.append("<id>");
   results.append(lt[i].getLineID());
   results.append("</id>");
   results.append("<name>");
   results.append(lt[i].getLineName());
   results.append("</name>");
  }
  results.append("</line>");
  results.append("</root>");
  response.setContentType("text/xml");
  response.setHeader("Cache-Control", "no-cache");
  response.getWriter().write(results.toString());
 }
}經歷過這次事件,以後使用ajax時候肯定會注意HTTP的Cache-Control屬性。

解決方法:可以在url里加入時間或者隨機數。