如何使用Ajax从服务端获取数据

汇总所有Ajax获取数据的方法
服务器君一共花费了221.581 ms进行了5次数据库查询,努力地为您提供了这个页面。
试试阅读模式?希望听取您的建议

在本文中将给出一个例子来介绍使用 Ajax 技术从服务端获得数据的三种方法。这个例子很简单,就是两个选择框(html中的<select>标签),通过选中第一个select的某一项后,会从服务端得到一些数据,并加载到第2个select中。

从服务端获得XML格式的数据

从服务端获得数据的最容易想到的方法就是在服务端反加一定格式的数据,一般是XML格式,然后在服务端使用XMLDocument或其他技术来读取这些数据,并生成<select>标签中选项的格式文本(<option>标签)。下面的addOptions函数是这个例子的核心函数,它负责根据从服务端获得的数据生成<select>标签中的<option>标签。在这里所使用的方法是利用了<select>标签的innerHTML属性(仅限于firefox),如果是IE,要使用outerHTML属性(IE中<select>标签的innerHTML属性有一些小bug,读者可以试着在IE中使用innerHTML属性,看看会发生什么情况)。addOptions方法的实现代码如下:

// select表示<select>对象,xml表示XMLDocument对象
function addOptions(select, xml)
{    
    if(select)
    {
        var options = "";
        for(var i = 0; i < xml.childNodes[0].childNodes.length ; i++)
        {  
            if(xml.childNodes[0].childNodes[i].nodeName == "list")
            {
                var s = "";
                if(isIE())               
                    s = xml.childNodes[0].childNodes[i].text;         
                else
                    s = xml.childNodes[0].childNodes[i].textContent
                options += "<option value='" + s + "'>" ;
                options += s;
                options += "</option>"
            }
        }
            
        var id = select.id;
        if(isIE())
            select.outerHTML = "<SELECT id='" + id + "' onchange='onChange(this)'>" + options + "</SELECT>";
        else
            select.innerHTML = options;                
        
    }
}

onReadState函数将在XMLHttpRequest对象的异步访问服务端时调用。当readyState为4时表示成功从服务端返回XML数据。这个函数的实现代码如下:

// myRequest表示XMLHttpRequest对象,selectId表示<select>标签的id属性值
function onReadyState(myRequest, selectId) 
{ 
    if(myRequest.readyState == 4)   // 4表示成功获得相应信息
    {              
        try
        {
            var xml = myRequest.responseXML;   // 获得XMLDocument对象      
            var kind = document.getElementById(selectId); // 获得<select>对象
            addOptions(kind, xml);  // 向<select>标签中加入<option>标签
        }
        catch(e)
        {
            alert("onReadyState:" + e);
        }
    }
}

getData函数负责向服务端发送请求,并设置异步事件。实现代码如下:

function getData(url, selectId)
{
    var myRequest = getXMLHTTPRequest();  // 获得一个XMLHttpRequest对象
    
    if(myRequest)
    {
        myRequest.onreadystatechange =  function() // 接收获得数据状态的事件函数
        {                        
            onReadyState(myRequest, selectId);  
        }
         
        try 
        {
            myRequest.open( "post", url, true);
            
        }
        catch(e)
        {
            alert(e);
        } 
        try
        {
            myRequest.send("");
  
        }
        catch(e)
        {
            alert(e);
        }
  
    }
}

现在本例子的核心代码已经实现完成,下一步就是在html而加载时从服务端获得第1个<select>标签的数据,并将其加载到第1个<select>标签中。让我们先看一下这个静态的html代码。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type="text/javascript" src="myscript.js">
        </script>
    </head>
    <body>
        <select id="bigKind" onchange="onChange(this)" >
             
        </select>
        <select id="smallKind" >
           
        </select>
    </body>
</html>

从上面代码可以看出,这两个<select>标签分别是bigKind和smallKind,里面并没有<option>标签,这是因为<option>标签要在javascript里动态加载。下面我们先来加载bigKind中的数据。

window.onload = onLoad
function onLoad()
{                         
    try
    {
        getData("../GetXML", "bigKind");
               
    }
    catch(e)
    {
        alert("onLoad:" + e);
    }
}

其中GetXML是一个Servlet程序(读者可以将其换成其他的服务端程序,如asp.net、php的)。下面是这个GetXML程序的实现代码:

package servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import database.MyData;
public class GetXML extends HttpServlet
{
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        response.setContentType("application/xml;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try
        {
            String s = request.getParameter("kind");
            out.println("<data>");
            if (s == null)
            {
                for (String key : MyData.data.keySet())
                {
                    out.println("<list>" + key + "</list>");
                }
            } else
            {
                s = java.net.URLDecoder.decode(s, "UTF-8");
                System.out.println(s);
                java.util.List<String> smallKind = MyData.data.get(s);
                if (smallKind != null)
                {
                    for (String kind : smallKind)
                    {
                        out.println("<list>" + kind + "</list>");
                    }
                }
            }
            out.println("</data>");
        } finally
        {
            out.close();
        }
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }
    public String getServletInfo()
    {
        return "Short description";
    }
}

不管读者会不会java和servlet,从这个程序中的processRequest方法中都可以看出,首先会获得请求参数kind,如果这个参数不存在,则返回bigKind所需要的数据,以xml格式返回,类似于如下的格式:

<data>
  <list>data1</list>
  <list>data2</list>
</data>

如果kind参数存在,则在MyData.data中查询第2个<select>标签(smallKind)所需要的数据。data是一个Map类型。为了方便起见,本例子并未使用数据库,而是在MyData类中定义了一个静态的Map类型变量。MyData的实现代码如下:

package database;
import java.util.*;
public class MyData {
    public static Map<String, List<String>> data;    
    static {
        
        data = new HashMap<String, List<String>>();
        
        List<String> eProducts = new LinkedList<String>();
        eProducts.add("手机");
        eProducts.add("数码/IT");
        eProducts.add("家电");
        eProducts.add("电脑");
                
        data.put("消费电子", eProducts);
        
        List<String> goods = new LinkedList<String>();
        
        goods.add("化妆");
        goods.add("健康");
        goods.add("玩具");
        goods.add("办公/文体 ");
        goods.add("童装童鞋");
        goods.add("其他");
        
        data.put("日用百货", goods);
        
        List<String> books = new LinkedList<String>();
        
        books.add("小说");
        books.add("动漫"); 
        books.add("经济");
        books.add("法律");
        books.add("计算机");
        books.add("英语");
        books.add("通讯");
        books.add("其他");
        
        data.put("图书", books);                        
    }
}

其中data变量中的key值就是bigKind中的值,而每一个key对应的值(一个List<String>对象就是smallKind中值的列表)。下面我们来实现当第1个<select>标签bigKind变化时,更新smallKind标签。<select>的onchange事件函数的代码如下:

function onChange(obj)
{
    try
    {
        getData(encodeURI(encodeURI("../GetXML?kind=" +obj.options[obj.selectedIndex].value)), "smallKind");
     
    }
    catch(e)
    {
        alert(e);
    }
}

这个函数是<select>标签的onchange事件函数。obj表示<select>标签本身。这个函数中只有一条有实际意义的语句,也就是调用了getData方法,这个方法人在onLoad方法中调用getData时差不多,只是在传送url时使用了两个encodeURI方法。由于XMLHttpRequest方法以utf-8向服务端发送数据,因此,要使用两个encodeURI向服务端发送%xx形式的utf-8编码,然后在服务端进行解析。我们在GetXML中的processRequest方法中可以找到如下的一条语句:

s = java.net.URLDecoder.decode(s, "UTF-8");

就是进行解码操作。

注:如果在IE中,客户端可以不使用encodeURI对带中文的URL进行编码,服务端也不用解码。在服务端仍然可以正常显示中文。但在firefox中就必须要进行编码和解码。因此,要想跨浏览器,就需要使用本文所述的方法。

直接获得<option>...</option>内容的字符串

上面的获得数据的方法是从服务端获得了一个XML文档,并转换成XMLDocument对象,然后解析。这种方法虽然很好,但是操作XMLDocument对象还是有些麻烦,因此,我们可以在服务端直接反回<select>标签所需要的<option>标签字符串,然后将这些字符串传给<select>对象的innerHTML或outerHTML就可以了。服务端的代码和上面的实现代码类似,只需要将<data>去掉,然后将<list>改为<option>后,并使用如下的语句来设置ContentType:

response.setContentType("text/html;charset=UTF-8");

客户端可通过XMLHttpRequest对象的responseText属性获得这些含有<option>的文本,并将其赋给innerHTML或outerHTML属性。这种方法虽然很方便,但并不灵活。如果客户端不使用<select>标签,而是使用<table>或其他的标签显示数据,那么返回的这些数据就没什么用处了。而即方便,又灵活的应该是下面要介绍的方法。

从服务端返回javascript代码,在客户端使用eval函数执行

我们可以在服务端返回类似于如下的字符串:

var options = new Array();
options.push('data1');
options.push('data2');

然后使用eval函数执行上面的字符串,这样我们在javascript中就可以使用options数组了。我个人认为,使用数组要比使用XMLDocument更容易,代码量也更少。如果要返回更为复杂的数据,也可以使用javascript中的类或其他数据结构。根据上面的思想,新的processRequest方法的代码如下:

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("var options = new Array();");
        try 
        {
            String s = request.getParameter("kind");
            if (s == null)
            {                
                for (String key : MyData.data.keySet())
                {
                    out.println("options.push('" + key + "');");
                }
            } else
            {
                s = java.net.URLDecoder.decode(s, "UTF-8");
                System.out.println(s);
                java.util.List<String> smallKind = MyData.data.get(s);
                if (smallKind != null)
                {
                    for (String kind : smallKind)
                    {
                        out.println("options.push('" + kind + "');");
                    }
                }
            }
        } finally
        {
            out.close();
        }
    }
    客户端经过改进的addOptions函数如下:
// javascript表示从服务端返回的javascript代码字符串
function addOptions(select, javascript)
{    
    if(select)
    {   
        if(select.id == "smallKind")
        {
            if(isIE())
                select.options.length = 0; 
        }
        var myOptions = "";
        eval(javascript);  //执行从服务端返回的javascript代码
        for(var i = 0; i < options.length ; i++)  // 从options数组中取数据
        {             
            var s = "";
            if(isIE()) 
            {
                
                select.options[select.options.length] = new Option(options[i], options[i]);
            }
            else
            {
           
                myOptions += "<option value='" + options[i] + "'>" ;
                myOptions += options[i];
                myOptions += "</option>"
            }
        }
    }
       
    var id = select.id;
    if(!isIE())    
        select.innerHTML =  myOptions;           
}

在上面的addOptions方法中还有一个不同是在IE中使用了<select>对象的options数组来添加选择项,而不是使用outerHTML。这么做的好处是可以在onLoad方法中就获得<select>的选项值。而如果使用outerHTML在html未装载完时,<select>标签中选择项仍然为0。这样在onLoad方法中就无法访问<select>中的被加入项了,当然,在onchange事件中可以。

在firefox中使用innerHTML时,在html未装载完时,只要<select>标签被装载完(也就是调用了addOptions方法后),就可以访问<select>标签中的<option>了。个人感觉这一点要从IE做得好。顺便说一句,笔者使用的是IE6,不知道ie7会是什么效果。

简单来说,方法有以下几种:

  • 返回XML
  • 返回HTML
  • 返回Json
  • 返回一些javascript代码,在客户端执行

本文地址:http://www.nowamagic.net/librarys/veda/detail/705,欢迎访问原出处。

不打个分吗?

转载随意,但请带上本文地址:

http://www.nowamagic.net/librarys/veda/detail/705

如果你认为这篇文章值得更多人阅读,欢迎使用下面的分享功能。
小提示:您可以按快捷键 Ctrl + D,或点此 加入收藏

阅读一百本计算机著作吧,少年

很多人觉得自己技术进步很慢,学习效率低,我觉得一个重要原因是看的书少了。多少是多呢?起码得看3、4、5、6米吧。给个具体的数量,那就100本书吧。很多人知识结构不好而且不系统,因为在特定领域有一个足够量的知识量+足够良好的知识结构,系统化以后就足以应对大量未曾遇到过的问题。

奉劝自学者:构建特定领域的知识结构体系的路径中再也没有比学习该专业的专业课程更好的了。如果我的知识结构体系足以囊括面试官的大部分甚至吞并他的知识结构体系的话,读到他言语中的一个词我们就已经知道他要表达什么,我们可以让他坐“上位”毕竟他是面试官,但是在知识结构体系以及心理上我们就居高临下。

所以,阅读一百本计算机著作吧,少年!

《编程之美:微软技术面试心得》 《编程之美》小组 (作者)

《编程之美:微软技术面试心得》是一本让人着迷的书!阅读起来。有些题目的内容会引起强烈的共鸣,尤其是那些自己非常熟悉并且又深知解答的题目;也有一些题目让我异常惊诧,原来除了我所知道的解答思路之外,还有更好的解答以及更深层次的原因。还有一些题目是从来没想到过的。阅读过程是一次愉快的享受,也是脑细胞持续活跃的过程。

更多计算机宝库...