内容


利用 Jython 与 Ajax 技术构建一个简单的 Web 应用程序

Comments

一个典型的 Ajax 应用场景——注册用户名检验

随着互联网应用的普及与 Web 2.0,SNS 等模式的逐渐成熟,越来越多的网站开始吸纳用户,允许用户注册并登录后才可以使用网站应用。互动性强的,更为动态的网站会利用 Ajax 技术构建以增强用户浏览体验(UE,User Experience),相信很多用户都曾经有过这样的注册体会:在注册页面输入用户名后,不用点击检查或者提交按钮,页面不刷新就会自动提示用户名是否可用。这就是一个典型的 Ajax 应用,用户名在“后台”通过 Ajax 技术实现直接提交到服务器进行检查,而不必经过用户显视执行某些动作才能提交检查。下面以微博网站推特(twitter.com)为例展示。

步骤一, 打开注册页面。见图 1。

图 1. twitter.com 注册页面
图 1. twitter.com 注册页面
图 1. twitter.com 注册页面

步骤二,输入一个用户名,例如 neoremind。页面不刷新,而是在文本框右边提示“正在检查(checking availability)”。见图 2。

图 2. 输入已存在用户名,页面提示正在检查
图 2. 输入已存在用户名,页面提示正在检查
图 2. 输入已存在用户名,页面提示正在检查

步骤三,经过一段时间的检查,文本框右边提示“用户名已经存在(username has already been taken)”。因此 neoremind 这个用户名是重复的,不能被使用。见图 3。

图 3. 输入已存在用户名后,页面提示用户名不合法
图 3. 输入已存在用户名后,页面提示用户名不合法
图 3. 输入已存在用户名后,页面提示用户名不合法

步骤四,把用户名改为 neoremind123。页面不刷新,而是在文本框右边再次提示“正在检查(checking availability)”,之后提示“合法(ok)”。因此 neoremind123 是可以被使用的用户名。见图 4。

图 4. 输入新的用户名后,页面提示该用户名可以使用
图 4. 输入新的用户名后,页面提示该用户名可以使用
图 4. 输入新的用户名后,页面提示该用户名可以使用

上述场景的实现思路

分析上述场景,这是一个典型的 B/S(Browser/Server)架构应用程序,分别要实现客户端,即浏览器端与服务器端编程。该方法的关键在于对浏览器端的 JavaScript 与服务器异步通信的组合。启用这种组合非常简单:利用 JavaScript 内置的 XMLHttpRequest 对象向服务器发出异步请求(例如检查操作),它直接从浏览器与后端服务进行通信。服务器端采用 Servlet 技术,Servlet 源于请求 / 响应模式,它可以接收来自客户端浏览器的 HTTP 请求,并产生一个响应,然后将这个响应发送到客户端。当请求返回时,就可以使用 JavaScript 和 CSS 来相应的更新界面(UI),而不是刷新整个页面。这样就可以做到更好的用户浏览体验,用户甚至不知道浏览器正在与服务器通信,Web 站点看起来是即时响应的。

上述分析可以参考图 5。

图 5. 实现架构图
图 5. 实现架构图
图 5. 实现架构图

下面的章节将分别介绍如何编写服务器端实现,如何编写应用了 Ajax 技术的前端页面以及二者的整合以构建一个完整的应用程序,同时提供了一个增强用户浏览体验的补充章节,可供参考。

利用 Jython 写服务器端 Servlet 处理程序

服务器端 Servlet 原理

在网上浏览任何资源,服务方需要提供一个 Web 服务器,浏览网页的过程就是浏览器通过 HTTP 协议与 Web 服务器进行交互的过程。如今随着应用的发展,客户需要动态地与服务器进行交互,为了实现这一目标,就需要开发一个遵循 HTTP 协议的服务器端应用组件来处理 HTTP 请求。Servlet 是一个基于 Java 技术的运行在服务器端的 Web 组件,利用 Sevlet 可以很轻松的扩展 Web 服务的功能,使它满足特定的应用需要。Servlet 由 Servlet 容器管理,也就是 Servlet 的运行环境(Servlet Runtime Environment),它负责接收请求和提供响应等服务。

例如 Tomcat 就是一个常用的 Servlet 容器,其接收客户端请求并做出响应的步骤如下:

1、客户访问 Web 服务器,发送 HTTP 请求。

2、Web 服务器接收到请求后,传递给 Servlet 容器。

3、Servlet 容器加载 Servlet,请求信息被封装为 HttpServletRequest 对象。

4、Servlet 逻辑代码处理请求,POST 请求由 doPost 方法处理,GET 请求由 doGet 方法处理。

5、Servlet 实例把处理结果封装为 HttpServletResponse 对象,然后发送回浏览器。

下面的代码是专为上述应用场景用 Java 写的服务器端 Servlet 处理程序,这里已经注册过的用户名分别为“neoremind”,“jack”,“simeon”,“victor”,实际的应用中这些数据是需要从数据库中读取出来的,这里为了简化说明,暂且简单处理。

清单 1. 服务器端的 Java 实现的用于验证用户名合法性的 Servlet
 import java.io.IOException; 
 import javax.servlet.ServletException; 
 import javax.servlet.http.HttpServlet; 
 import javax.servlet.http.HttpServletRequest; 
 import javax.servlet.http.HttpServletResponse; 

 public class RegisterServlet extends HttpServlet{ 

    // HTTP POST 请求处理方法
    public void doPost(HttpServletRequest request, HttpServletResponse response) 
 throws ServletException, IOException 
    { 
        // 已经注册的用户名列表
        String[] username_list = {"neoremind", "jack", "simeon", "victor"}; 
        // 得到用户提交请求欲申请注册的用户名
        String req_username = request.getParameter("username"); 
        if (req_username == null) 
        { 
    req_username = ""; 
        } 
        // 设置 HTTP 响应类型信息
        response.setContentType("text/html;charset=utf-8"); 
        // 遍历所有已经注册的用户名,如果发现有重名的,则提示“用户名已经存在,请另外选择一个”
        for (int i = 0; i < username_list.length; i++) 
        { 
            if (req_username.equals(username_list[i])) 
            { 
                response.getWriter().print("Username has existed, 
                please choose another one."); 
            } 
        } 
        // 检查过所有的用户名后,发现没有重名,则提示“用户名合法。检查成功!”
        response.getWriter().print("Valid username. OK!"); 
    } 

    // HTTP GET 请求处理方法,同 POST 请求处理方法
    public void doGet(HttpServletRequest request, HttpServletResponse response) 
 throws ServletException, IOException 
    { 
        doPost(request,response); 
    } 
 }

Jython 也能胜任 Servlet 工作

Jython 是两种广泛流行语言 Java 和 Python 的组合,它是 Python 的纯 Java 实现。Java 现如今已被广泛地接受,Java 拥有庞大的可利用的类库,并有很详尽的文档。Python 则具有很好的灵活性,开发快速,容易使用。Jython 能够实现在 Java 或 Python 中实现的任何类、算法。Jython 无缝地结合了 Java 与 Python,使用户能以 Python 语言的语法编写在 Java 虚拟机(JVM,Java Virtual Machine)上运行的软件。也即体现了 Jython 对于 Java 的兼容性。所以既然在 3.1 小节能用 Java 编写 Servlet,那么 Jython 也完全可以胜任此项工作,与 Java 程序相比,Jython 可以极大的的减少编程代码量,而且使用语法简洁,不失为服务器端解决方案的一种新的思路。

下面的代码是 3.1 小节中 Java Servlet 处理程序的 Jython 语言实现。见清单 2。

清单 2. 服务器端的 Jython 实现的用于验证用户名合法性的 Servlet
 from javax.servlet.http import HttpServlet 
 class RegisterServlet(HttpServlet): 
    # HTTP POST 请求处理函数
    def doPost(self, req, res): 
        # 得到用户提交请求欲申请注册的用户名
        searchterm = req.getParameter("username") 
        if not searchterm: 
            searchterm = ""
        # 已经注册的用户名列表
        usernameList = ['neoremind', 'jack', 'simeon', 'victor'] 
        # 设置 HTTP 响应类型信息
        res.setContentType("text/html;charset=utf-8") 
        out = res.getWriter() 
        # 遍历所有已经注册的用户名,如果发现有重名的,则提示“用户名已经存在,请另外选择一个”
        if searchterm in usernameList: 
            print>>out,"Username has existed, please choose another one."
        # 检查过所有的用户名后,发现没有重名,则提示“用户名合法。检查成功!”
        else: 
            print>>out,"Valid username. OK!"

    # HTTP GET 请求处理函数,同 POST 请求处理函数
    def doGet(self, req, res): 
        pass

在前端应用 Ajax 技术

Ajax(Asynchronous JavaScript and XML)是一种独立于 Web 服务器的浏览器技术。它的全称是“异步 JavaScript 及 XML”。实际上,它由几种技术和标准以新的强大方式组合而成的。这些技术和标准包括:JavaScript,XML,HTML 以及 CSS。

通过 AJAX,JavaScript 可使用 XMLHttpRequest 对象来直接与服务器进行通信。通过这个对象,可在不重载或者刷新页面的情况与 Web 服务器交换数据。AJAX 在浏览器与 Web 服务器之间使用异步数据传输 HTTP 请求,这样就可使网页从服务器请求少量的信息,而不是整个页面,从而达到使因特网应用程序更小、更快,更友好的目标。

在传统的 Web 应用中,如果希望和服务器进行交互,就必须利用 HTML 表单向服务器发送 GET 或 POST 请求。而利用 Ajax 技术,用户可以甚至不知道浏览器正在与服务器通信,就可以得到 Web 站点的即时响应,这全靠一个 JavaScript 内置对象—— XMLHttpRequest。

下面是前端页面 register.jsp 的代码实现。用户名输入框是 input 类型,在其上侦听 onchange 动作,也就是说当用户的光标焦点离开输入框后会触发 onchange 指定的 JavaScript 函数 isValidUsername()。isValidUsername 函数是我们要详细讲解的重点。

首先需要根据不同的浏览器创建 XMLHttpRequest 对象,具体的处理函数是 GetXmlHttpObject。IE 浏览器使用 ActiveXObject,而其他的浏览器使用名为 XMLHttpRequest 的 JavaScript 内建对象。如需针对不同的浏览器来创建此对象,我们要使用一条“try-catch”语句。

接下来要向服务器发送请求,XMLHttpRequest 对象有几个重要的属性,见表 1。

表 1. JavaScript 中 XMLHttpRequest 对象的属性
属性描述
onreadystatechange 每次状态改变所触发事件的事件处理程序
readyState 对象状态值:
0 = 未初始化(uninitialized)
1 = 正在加载(loading)
2 = 加载完毕(loaded)
3 = 交互(interactive)
4 = 完成(complete)
responseText 从服务器进程返回的数据的字符串形式
responseXML 从服务器进程返回的 DOM 兼容的文档数据对象
status 从服务器返回的数字代码,比如 404(未找到)或 200(就绪)
statusText 伴随状态码的字符串信息

onreadystatechange 属性存有处理服务器响应的回调函数。stateChanged() 函数就是相应的回调处理函数。这里是将一个节点 id 为 txtHint 的 DOM 对象的 innerHTML 属性重新附上字符串,也就是检验用户名是否合法的提示信息,这个信息是服务器端返回过来的,通过 responseText 取得。

readyState 属性存有服务器响应的状态信息。每当 readyState 改变时,onreadystatechange() 函数就会被执行。

清单 3 实现 Ajax 技术的前端 register.jsp 页面代码
 <html> 
 <head> 
    <script> 
        var xmlHttp 
        function isValidUsername(str) 
        { 
            if (str.length==0) 
            { 
                document.getElementById("txtHint").innerHTML=""; 
                return; 
            } 
            // 获取 XMLHttpRequest 对象
            xmlHttp=GetXmlHttpObject() 
            if (xmlHttp==null) 
            { 
                alert ("您的浏览器不支持 AJAX !"); 
                return; 
            } 
            // 发送请求到指定的 URL 处理
            var url= 
                "http://localhost:8080/jythonAjax/RegisterServlet.py"; 
            // 利用 URL 追加字符串的方式添加一个参数 username 
            url=url+"?username="+str; 
            // 设置服务器响应的状态信息改变后,处理它的回调函数
            xmlHttp.onreadystatechange=stateChanged; 
            // open() 方法规定发送 POST 请求到指定的 url,并规定应当对请求进行异步处理
            xmlHttp.open("POST",url,true); 
            // send() 方法可将请求送往服务器
            xmlHttp.send(null); 
        } 

        // 处理服务器响应的状态信息改变的回调函数
        function stateChanged() 
        { 
            // 当服务器请求已完成(可以访问服务器响应并使用它)
            if (xmlHttp.readyState==4) 
            { 
                // 从服务器的 response 获得数据,即返回的提示消息
                document.getElementById("txtHint").innerHTML=xmlHttp.responseText; 
            } 
        } 

        // 根据不同的浏览器,返回相应 XMLHttpRequest 对象
        function GetXmlHttpObject() 
        { 
            var xmlHttp=null; 
            try 
            { 
                // Firefox, Opera 8.0+, Safari 等使用 XMLHttpRequest 对象
                xmlHttp=new XMLHttpRequest(); 
            } 
            catch (e) 
            { 
                // 针对 Internet Explorer 6.0 及以上版本使用
                try 
                { 
                    xmlHttp=new ActiveXObject("Msxml2.XMLHTTP"); 
                } 
                catch (e) 
                { 
                    // 针对 Internet Explorer 5.5 及以下版本使用
                    xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); 
                } 
            } 
            return xmlHttp; 
        } 
    </script> 
 </head> 
 <body> 
    <form> 
    <!-- 输入用户名的文本框 --> 
        Username:<input type="text" id="username" 
        onchange="isValidUsername(this.value)" /> 
    </form> 
    <!-- 提示信息的 span --> 
    <p>Suggestions: 
        <span id="txtHint"></span> 
    </p> 
 </body> 
 </html>

整合服务器端与前端构建完整的注册检验 Web 应用程序

下面介绍在 Windows 平台上从环境搭建,到配置应用程序完整的流程,供读者参考。Demo 代码可以在页面底部进行下载。

安装 Java 和 Tomcat 服务器

Java Development Kit 的安装版本需要高于 5.0,Tomcat 服务器的安装版本需要高于 5.5。具体的步骤在此省略,有需要的读者请自行在网络搜索寻找教程。

安装 Jython

安装 Jython2.2.1 版本。下载地址为 http://sourceforge.net/projects/jython/files/jython/2.2.1/jython_installer-2.2.1.jar/download

下载完毕后,运行

java -jar jython_installer-2.2.1.jar

会打开带有界面的安装程序,依次点击下一步直至安装完毕,默认安装路径为 C:\jython2.2.1。

更多 Jython 安装指南请参考 http://wiki.python.org/jython/InstallationInstructions

配置 Web 应用程序

在 %TOMCAT_HOME%\webapps 新建以下文件结构。其中 jython.jar 需要从 C:\jython2.2.1\jython.jar 拷贝过来。

清单 4 Web 应用程序目录结构
 %TOMCAT_HOME%\webapps 
 `------jythonAjax 
 `--------register.jsp 
                             `--------RegisterServlet.py 
                                `--------WEB-INF 
                                            `--------web.xml 
                                            `--------lib 
                                                       `--------jython.jar

register.jsp 的内容为清单 3。

RegisterServlet.py 的内容为清单 2。

web.xml 内容为清单 5。

清单 5 Web 应用程序部署描述文件 web.xml
 <?xml version="1.0" encoding="UTF-8"?> 
 <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web- 
 app_2_4.xsd"> 
    <display-name>JythonAjaxHelloworld</display-name> 
    <welcome-file-list> 
        <welcome-file>index.html</welcome-file> 
            <welcome-file>index.htm</welcome-file> 
            <welcome-file>index.jsp</welcome-file> 
            <welcome-file>default.html</welcome-file> 
            <welcome-file>default.htm</welcome-file> 
            <welcome-file>default.jsp</welcome-file> 
    </welcome-file-list> 
    <servlet> 
        <servlet-name>PyServlet</servlet-name> 
        <servlet-class> 
            org.python.util.PyServlet 
        </servlet-class> 
        <init-param> 
            <param-name>python.home</param-name> 
            <param-value>C:\jython2.2.1</param-value> 
        </init-param> 
        <init-param> 
            <param-name>python.path</param-name> 
            <param-value>C:\jython2.2.1\Lib</param-value> 
        </init-param> 
        <load-on-startup>1</load-on-startup> 
    </servlet> 
    <servlet-mapping> 
        <servlet-name>PyServlet</servlet-name> 
        <url-pattern>*.py</url-pattern> 
    </servlet-mapping> 
 </web-app>

开始体验

执行 %TOMCAT%\bin\startup.bat 启动 Tomcat 服务器。当看到“Server startup in ??? ms”后证明服务器启动成功。见图 6。

图 6 Tomcat 服务器启动成功的命令行提示
图 6 Tomcat 服务器启动成功的命令行提示
图 6 Tomcat 服务器启动成功的命令行提示

打开浏览器,输入地址 http://localhost:8080/jythonAjax/register.jsp 后,回车打开页面。见图 7

图 7 注册首页
图 7 注册首页
图 7 注册首页

输入用户名 neoremind。页面不刷新,而是在文本框下方提示“该用户名已经存在(Username has existed, please choose another one.)”。见图 8。

图 8 输入已存在用户名后,页面提示错误
图 8 输入已存在用户名后,页面提示错误
图 8 输入已存在用户名后,页面提示错误

重新输入用户名 neoremind123。页面不刷新,而是在文本框下方提示“该用户名合法(Valid username. OK!)”。见图 9。

图 9 输入不存在用户名后,页面提示用户名合法
图 9 输入不存在用户名后,页面提示用户名合法
图 9 输入不存在用户名后,页面提示用户名合法

增强的用户体验—— loading 提示性的动态图标

实际应用中,由于各种网络环境的关系,客户端和服务器端之间相互可能会存在比较大的延时,用户不清楚浏览器在检验用户名是否合法,为了实现更加友好的用户体验,如果提供一个动态滚动的图片会使用户知道现在正在检查用户名是否合法。要实现这样的功能并不复杂,只需要修改前端页面即可。

步骤一,修改 register.jsp 的 stateChanged() 函数。见清单 6。

清单 6 stateChanged() 函数修改版
 // 处理服务器响应的状态信息改变的回调函数
 function stateChanged() 
 { 
    // 当服务器请求已完成(可以访问服务器响应并使用它)
    if (xmlHttp.readyState==4) 
    { 
        // 显示返回的提示信息
        document.getElementById("txtHint").innerHTML=xmlHttp.responseText; 
        // 阻塞正在加载缓冲 gif 图片的显示,隐藏它
        document.getElementById('loading').style.display="none"; 
    } 
    // 当服务器请求未完成
    else 
    { 
        // 提示信息为空
        document.getElementById("txtHint").innerHTML=""
        // 显示正在加载缓冲的 gif 图片
        document.getElementById('loading').style.display="block"; 
    } 
 }

步骤二,修改 register.jsp,多加入一个 DIV 用以显示“正在加载(loading)”图标。见清单 7。

清单 7 register.jsp 的 <body> 修改版
 <body> 
 <form> 
    Username: 
    <!-- 输入用户名的文本框 --> 
    <input type="text" id="username" onchange="isValidUsername(this.value)" /> 
 </form> 
 <!-- 提示信息的 span --> 
 <p>Suggestions: 
    <span id="txtHint"></span> 
 </p> 
 <!-- 正在加载缓冲 gif 图片,默认为不显示 --> 
 <div id="loading" style="display: none;"> 
    <img id="pic" src="loading.gif"/> 
 </div> 
 </body>

步骤三,把 loading 图标放在 %TOMCAT_HOME%\webapps\ jythonAjax 目录下。图标请在页面底部下载。

打开浏览器,输入地址 http://localhost:8080/jythonAjax/register.jsp 后,回车打开页面。输入用户名后,可以看到有一个“正在加载”的图标提示用户当前正在检验用户名是否合法。见图 10

图 10 输入用户名后,页面提示图标显示正在和服务器交互检查用户名
图 10 输入用户名后,页面提示图标显示正在和服务器交互检查用户名
图 10 输入用户名后,页面提示图标显示正在和服务器交互检查用户名

结束语

基于 Java 的 Servlet 同样可以用 Jython 来编码,为服务器端的实现提供了另一种解决方案。Ajax 也并不神秘,只是让用户在不用刷新页面的情况下,可以动态地更新网页部分内容,然后运用到各种需要用到这种性能的场合,将为用户提供更加实时、更加类似于桌面应用程序的体验。但是本文涉及的服务器端与前端应用是非常简单的,只是对于复杂 Web 应用程序的冰山一角,感兴趣的读者可以进一步寻找相关主题深入学习。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=607467
ArticleTitle=利用 Jython 与 Ajax 技术构建一个简单的 Web 应用程序
publish-date=01122011