最近闲暇时,研究了一下DWR框架,个人感觉是一个很不错很好用的Ajax框架,封装的很好
DWR入门
1.1 简介
DWR是一个可以允许你去创建AJAX WEB站点的JAVA开源库。它可以让你在浏览器中的Javascript代码调用Web服务器上的Java代码,就像在Java
代码就在浏览器中一样。 DWR包含2个主要部分:
一个运行在服务器端的Java Servlet,它处理请求并且向浏览器发回响应。
运行在浏览器端的JavaScript,它发送请求而且还能动态更新网页。
DWR工作原理是通过动态把Java类生成为Javascript。它的代码就像Ajax魔法一样,你感觉调用就像发生在浏览器端,但是实际上代码调用发生
在服务器端,DWR负责数据的传递和转换。这种从Java到JavaScript的远程调用功能的方式使DWR用起来有种非常像RMI或者SOAP的常规RPC机制
,而且DWR的优点在于不需要任何的网页浏览器插件就能运行在网页上。 Java从根本上讲是同步机制,然而AJAX却是异步的。所以你调用远程
方法时,当数据已经从网络上返回的时候,你要提供有反调 (callback) 功能的DWR。
http://www.java3z.com/cwbwebhome/article/article2/img4/r_howitworks.png
1.2 第一个DWR程序:Hello World
1.2.1 将DWR放入你的工程
1) 从官方网站下载dwr.jar包。然后将它放在你webapp的WEB-INF/lib目录下。
2) 将下载的dwr-版本号-src.zip \java\org\directwebremoting内的engine.js和util.js放入WEB应用中,比如js文件夹下。
1.2.2 编辑配置文件
1). ------------------web.xml---------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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">
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<!-- 是servlet的加载顺序 数字越小代表加载的优先级越高 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
2).--------------------------dwr.xml----------------------------------------------
在web.xml的同一目录下,创建dwr.xml,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr/dwr20.dtd">
<dwr>
<allow>
<create creator="new" javascript="service">
<param name="class" value="helloWorld.Service"/>
</create>
</allow>
</dwr>
3)。 编写---------------------------service.java--------------------------------
就像没有dwr一样,写一个简单类并加一个方法 是
package helloWorld;
public class Service
{ public String sayHello(String yourName)
{ //可以是访问数据库的复杂代码
return "Hello World " + yourName;
}
}
4)。 -----------------------------------测试DWR-------------------------------------
将代码放入应用服务器(比如Tomcat),启动。
然后在地址栏输入http://localhost:8080/你的工程/dwr 然后点击service,会看到刚才写的sayHello()的方法,输入自己的名字然
后点击“Execute”,如果发现确实是正确的返回结果,说明测试通过了,可以进入下一步了。
5)。--------------------------------- 编写一个 index.jsp---------------------------------
<%@ page language="java" pageEncoding="UTF-8"%>
<html>
<head>
<title>My JSP 'first_dwr.jsp' starting page</title>
<script type='text/javascript' src="js/util.js"></script>
<script type='text/javascript' src="js/engine.js"></script>
<script type='text/javascript' src="js/service.js"> </script>
<script type="text/javascript">
function firstDwr()
{
service.sayHello("孤魂",callBackHello);
}
function callBackHello(data){
alert(data);
}
</script>
</head>
<body>
<input type="button" name="button" value="测试" onclick="firstDwr()">
</body>
</html>
2.2 常用<init-param>参数列表(web.xml中的)
2.2.1 安全参数
allowGetForSafariButMakeForgeryEasier
开始版本:2.0
默认值:false
描述:置成true使DWR工作在Safari 1.x , 会稍微降低安全性。
crossDomainSessionSecurity
开始版本:2.0
默认值:true
描述:设置成false使能够从其他域进行请求。注意,这样做会在安全性上有点冒险,参考一下这篇文章,在没有理解这个后果前不要设置成为false。
debug
开始版本:1.0
默认值:false
描述:设置成true使DWR能够debug和进入测试页面
scriptSessionTimeout
开始版本:2.0
默认值:1800000(30分钟)
描述:script session 的超时设置
maxCallCount
开始版本:2.0rc2 和 1.1.4
默认值:20
描述:一次批量(batch)允许最大的调用数量。(帮助保护Dos攻击)
2.2.2 Ajax服务器加载时保护参数
pollAndCometEnabled
开始版本:2.0
默认值:false
描述:设置成true能增加服务器的加载能力,尽管DWR有保护服务器过载的机制。
maxWaitingThreads
开始版本:2.0
默认值:100
描述:最大等待线程数量。
preStreamWaitTime
开始版本:2.0
默认值:29000(单位:毫秒)
描述:对一个打开流前的反应,等待的最大时间
postStreamWaitTime
开始版本:2.0
默认值:1000(单位:毫秒)
描述:对一个打开流后的反应,等待的最大时间
2.3 日志配置
DWR工作在JDK1.3中不支持java.util.logging,但我们并不强迫任何人都去使用commons-logging或者log4j,所以在使用HttpServlet.log()方法时DWR将正常工作,如果没有日志类的话。然而如果DWR可以使用,那么它将使用日志。
Commoms-Logging
由于大多数servlet容器都使用它,几乎每个人都将使用commons-logging。所以如果你的webapp不明确使用commons-logging,它将被默认设为可以使用。 在这些日志将被一些配置文件所约束,比如java.util.logging或者log4j,可以去查看他们各自的文档获得详情。
HttpServlet.log()
如果你正在使用HttpServlet.log(), 以下的代码用来控制DWR日志
<init-param>
<param-name>logLevel</param-name>
<param-value>DEBUG</param-value>
</init-param>
值可以是:FATAL,ERROR,WARN(默认),INFO,DEBUG
DWR2.0插件
org.directwebremoting.Container
org.directwebremoting.WebContextFactory.WebContextBuilder
org.directwebremoting.ServerContextFactory.ServerContextBuilder
org.directwebremoting.servlet.UrlProcessor
org.directwebremoting.extend.AccessControl
org.directwebremoting.extend.AjaxFilterManager
org.directwebremoting.extend.ConverterManager
org.directwebremoting.extend.CreatorManager
org.directwebremoting.extend.DebugPageGenerator
org.directwebremoting.extend.HtmlCallMarshaller
org.directwebremoting.extend.HtmlPollHandler
org.directwebremoting.extend.PageNormalizer
org.directwebremoting.extend.PlainCallMarshaller
org.directwebremoting.extend.PlainPollHandler
org.directwebremoting.extend.Remoter
org.directwebremoting.extend.ScriptSessionManager
org.directwebremoting.extend.ServerLoadMonitor
--------------------------------------------------------dwr.xml配置----------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://www.getahead.ltd.uk/dwr/dwr20.dtd">
<dwr>
<!-- 仅当需要扩展DWR时才需要 -->
<init>
<creator id="..." class="..." />
<converter id="..." class="..." />
</init><!-- 没有它DWR什么也做不了 -->
<allow>
<create creator="..." javascript="..." />
<convert converter="..." match="..." />
</allow><!-- 有必要告诉DWR方法签名 -->
<signatures>...</signatures>
</dwr>
3.2 <init>标签
这个初始化部分申明被用来创建远程beans而且这个类能被用来以某种过程转换。大多数例子你将不需要用它,如果你想去定义一个新的Creator或者Converter,就要在此被申明。 在init部分里有了定义只是告诉DWR这些扩展类的存在,给出了如何使用的信息。这时他们还没有被使用。这种方式很像Java中的import语句。多数类需要在使用前先import一下,但是只有import语句并不表明这个类已经被使用了。每一个creator和converter都用id属性,以便后面使用。
3.3 <allow>标签
allow部分定义了DWR能够创建和转换的类。
3.3.1 Creator
每一个在类中被调用的方法需要一个<create …>有若干类型的creator,使用“new”关键字或者Spring 框架等。
create元素是如下的结构:
<allow>
<create creator="..." javascript="..." scope="...">
<param name="..." value="..." />
<auth method="..." role="..." />
<exclude method="..." />
<include method="..." />
</create>
...
</allow>
1).new:Java用“new”关键字创造对象 是DWR默认的creator,如下所示 <create id="new" class="org.directwebremoting.create.NewCreator"/>
没有必要把它加入dwr.xml,它已经在DWR内部文件了。 这个creator将使用默认构造器创建类的实例,以下是用new创建器的好处
安全:DWR创造的对象生存的时间越短,多次调用中间的值不一致的错误机会越少。
内存消耗低: 如果你的站点用户量非常大,这个创造器可以减少VM的内存溢出。
2).none: 它不创建对象,看下面的原因。
(v1.1+) none创建器不创建任何对象,它会假设你不须要创建对象。有2个使用的原因:
你可能在使用的scope不是"page"(看上面),并在在前面已经把这个对象创建到这个scope中了,这时你就不需要再创建对象了。
还有一种情况是要调用的方法是静态的,这时也不需要创建对象。DWR会在调用创建器之前先检查一下这个方法是不是静态的。
2. javascript属性
在浏览器里给你创建的对象命名。避免使用JavaScript保留字。这个名字将在页面里作为js被导入,就像第2章节的那个jsp: dwr.xml
<create creator="new" javascript="service">
<param name="class" value="helloWorld.Service" />
</create>
html / jsp:
<html> <head>
…
<script type='text/javascript' src='dwr/interface/service.js'>
…
3. scope属性
和定义在servlet的scope一样大的范围,它允许你指定哪个bean是可以获得的。选项可以是:application, session, request和page。这些值应该已经被开发者们熟悉了。
4. param元素
被用来指定创造器的其他参数,每种构造器各有不同。例如,"new"创造器需要知道要创建的对象类型是什么。每一个创造器的参数在各自的文档中能找到。
5. include和exclude元素
允许一个创造器去限制进入类的方法。一个创造器必须指定include列表或exclude列表之一。如果是include列表则暗示默认的访问策略是"拒绝",include中的每个方法就是允许访问的方法;如果是exclude列表则暗示默认的访问策略是"允许",exclude中的每个方法就是拒绝访问的方法。 比如:
<create creator="new" javascript="Fred">
<param name="class" value="com.example.Fred" />
<include method="setWibble" />
</create>
3.3.2 Converter
我们需要确认所有的参数能被转换。许多JDK提供的类型使你能够使用,但是你如果要转换你自己的代码,就必须告诉DWR。一般是指JavaBean的参数需要一个<convert…>标签作为入口。
你不需要在dwr.xml中<allow>部分的<convert>中定义。它们默认支持。
所有主要的类型,boolean, int , double等等。
包装类,Boolean, Integer等等。
java.lang.String
java.util.Date 和 java.sql.Times,java.sql.Timestamp。
数组(存放以上类型的)
集合类型 (List, Set, Map, Iterator等等) (存放以上类型的)
DOM对象(来自于DOM, XOM, JDOM和DOM4J)
1. 日期转换器
如果你有一个String(例如:“2001-02-11”)在Javascript,你想把它转换成Java日期。那么你有2种选择,一是使用Date.parse()然后使用DataConverter传入服务器端,还有一种选择是把该String传入,然后用java的SimpleDateFormat(或者其他的)来转换。 同样,如果你有个Java的Date类型并且希望在HTML使用它。你可以先用SimpleDateFormat把它转换成字符串再使用。也可以直接传Date给Javascript,然后用Javascript格式化。第一种方式简单一些,尽管浪费了你的转换器,而且这样做也会是浏览器上的显示逻辑受到限制。其实后面的方法更好,也有一些工具可以帮你
2. 数组转换器
数组实体不太容易理解。默认情况下DWR能转换所有原生类型的数组,还有所有marshallable对象的数组。这些marshallable对象包括前面介绍的String和Date类型。match属性看上去很怪。
3. bean和对象转换器
两个没有默认打开的转换器是Bean 和 Object 转换器。Bean转换器可以把POJO转换成Javascript的接合数组(类似与Java中的Map),或者反向转换。这个转换器默认情况下是没打开的,因为DWR要获得你的允许才能动你的代码。 Object转换器很相似,不同的是它直接应用于对象的成员,而不是通过getter和setter方法。下面的例子都是可以用object来替换bean的来直接访问对象成员。 如果你有一个在 <create ...> 中声明的远程调用Bean。它有个一参数也是一个bean,并且这个bean有一个setter存在一些安全隐患,那么攻击者就可能利用这一点。 你可以为某一个单独的类打开转换器:
<convert converter="bean" match="your.full.package.BeanName"/>
如果要允许转换一个包或者子包下面的所有类,可以这样写: <convert converter="bean" match="your.full.package.*"/>
显而易见,这样写是允许转换所有的JavaBean: <convert converter="bean" match="*"/>
BeanConverter 和 JavaBeans 规范 用于被BeanConverter转换的Bean必须符合JavaBeans的规范,因为转换器用的是Introspection,而不是Reflection。
这就是说属性要符合一下条件:有getter和setter,setter有一个参数,并且这个参数的类型是getter的返回类型。setter应该返回void,getter应该没有任何参数。setter没有重载。以上这些属于常识。就在eclipse里自动为每个属性添加setter,getter那种类型,如果你用的不是JavaBean,那么你应该用ObjectConverter.
设置Javascript变量 DWR可以把Javascript对象(又名maps,或联合数组)转换成JavaBean或者Java对象。例子:
public class Remoted {
public void setPerson(Person p) { // ... } }
public class Person {
public void setName(String name) { ... }
public void setAge(int age) { ... } // ... }
如果这个Remoted已经被配置成Creator了,Persion类也定义了BeanConverter,那么你可以通过下面的方式调用Java代码:
var p = { name:"Fred", age:21 }; Remoted.setPerson(p);
4. 集合类型转换器
有个两个默认的转换器,针对Map和Collection:
<convert converter="collection" match="java.util.Collection"/>
<convert converter="map" match="java.util.Map"/>
4.7 DWR与WebWork
WebWork支持在DWR2.0m3以后才有。 要可以通过DWR调用WW的Action,要做两件事。
4.7.1 配置dwr.xml
你必须在dwr的配置文件中加入这样的配置:
<create creator="none" javascript="DWRAction">
<param name="class" value="org.directwebremoting.webwork.DWRAction" />
<include method="execute" />
</create>
<convert converter="bean"
match="org.directwebremoting.webwork.ActionDefinition">
<param name="include" value="namespace,action,method,executeResult" />
</convert>
<convert converter="bean"
match="org.directwebremoting.webwork.AjaxResult" />
<convert converter="bean" match="your_action_package.*"/>
这样你AjaxWebWork Action调用返回一个action实例(而不是文字)。然后你必须包括action对象的转换
器定义(package级别或单独action)。
------------------------------------------DWR中的JavaScript简介---------------------------------------------
DWR根据dwr.xml生成和Java代码类似的Javascript代码。 相对而言Java同步调用,创建与Java代码匹配的Ajax远程调用接口的最大挑战来至与实现Ajax的异步调用特性。
5.1 简单的回调函数
假设你有一个这样的Java方法:
public class Remote {
public String getData(int index)
{ ... }
}
<script type='text/javascript' src='[WEBAPP]/dwr/engine.js'></script>
<script type='text/javascript' src='[WEBAPP]/dwr/interface/Remote.js'></script>
……
<script type="text/javascript">
function handleGetData(str) { alert(str); } Remote.getData(42,
handleGetData);
</script>
42是Java方法getData()的一个参数。
此外你也可以使用这种减缩格式: Remote.getData(42, function(str) { alert(str); });
5.2 调用元数据对象
另外一种语法时使用"调用元数据对象"来指定回调函数和其他的选项。上面的例子可以写成这样:
Remote.getData(42, {
callback:function(str) { alert(str);
} });
---------------------------------------------------- util.js 功能-----------------------------------------------------
util.js包含了一些工具函数来帮助你用javascript数据(例如从服务器返回的数据)来更新你的web页面。 你可以在DWR以外使用它,因为它不依赖于DWR的其他部分。你可以下载整个DWR或者单独下载. 4个基本的操作页面的函数:getValue[s]()和setValue[s]()可以操作大部分HTML元素除了table,list和image。getText()可以操作select list。 要修改table可以用addRows()和removeAllRows()。要修改列表(select列表和ul,ol列表)可以用addOptions()和removeAllOptions()。 还有一些其他功能不是DWRUtil的一部分。但它们也很有用,它们可以用来解决一些小问题,但是它们不是对于所有任都通用的。
7.1 $()
$() 函数(它是合法的Javascript名字) 是从Protoype偷来的主意。 大略上的讲: $ = document.getElementById。 因为在Ajax程序中,你会需要写很多这样的语句,所以使用 $() 会更简洁。 通过指定的id来查找当前HTML文档中的元素,如果传递给它多个参数,它会返回找到的元素的数组。所有非String类型的参数会被原封不动的返回。这个函数的灵感来至于prototype库,但是它可以在更多的浏览器上运行。 可以看看DWRUtil.toDescriptiveString的演示。 从技术角度来讲他在IE5.0中是不能使用的,因为它使用了Array.push,尽管如此通常它只是用来同engine.js一起工作。如果你不想要engine.js并且在IE5.0中使用,那么你最好为Array.push找个替代品。
7.2 addOptions and removeAllOptions
DWR的一个常遇到的任务就是根据选项填充选择列表。下面的例子就是根据输入填充列表。 下面将介绍 DWRUtil.addOptions() 的几种是用方法。 如果你希望在你更新了select以后,它仍然保持运来的选择,你要像下面这样做: var sel = DWRUtil.getValue(id); DWRUtil.removeAllOptions(id); DWRUtil.addOptions(id, ...); DWRUtil.setValue(id, sel);
55 / 92
如果你想加入一个初始的"Please select..." 选项那么你可以直接加入下面的语句: DWRUtil.addOptions(id, \["Please select ..."]);
DWRUtil.addOptions有5种模式
数组: DWRUtil.addOptions(selectid, array) 会创建一堆option,每个option的文字和值都是数组元素中的值。
对象数组 (指定text): DWRUtil.addOptions(selectid, data, prop) 用每个数组元素创造一个option,option的值和文字都是在prop中指定的对象的属性。
对象数组 (指定text和value值): DWRUtil.addOptions(selectid, array, valueprop, textprop) 用每个数组元素创造一个option,option的值是对象的valueprop属性,option的文字是对象的textprop属性。
对象: DWRUtil.addOptions(selectid, map, reverse)用每个属性创建一个option。对象属性名用来作为option的值,对象属性值用来作为属性的文字,这听上去有些不对。但是事实上却是正确的方式。如果reverse参数被设置为true,那么对象属性值用来作为选项的值。
对象的Map: DWRUtil.addOptions(selectid, map, valueprop, textprop) 用map中的每一个对象创建一个option。用对象的valueprop属性做为option的value,用对象的textprop属性做为option的文字。
ol 或 ul 列表: DWRUtil.addOptions(ulid, array) 用数组中的元素创建一堆li元素,他们的innerHTML是数组元素中的值。这种模式可以用来创建ul和ol列表。
这是网上的例子
7.3 addRows and removeAllRows
DWR通过这两个函数来帮你操作table: DWRUtil.addRows() 和 DWRUtil.removeAllRows() 。这个函数的第一个参数都是table、tbody、thead、tfoot的id。一般来说最好使用tbody,因为这样可以保持你的header和footer行不变,并且可以防止Internet Explorer的bug。
DWRUtil.removeAllRows()
DWRUtil.removeAllRows(id);
描述: 通过id删除table中所有行。 参数: id: table元素的id(最好是tbody元素的id)
DWRUtil.addRows()
DWRUtil.addRows(id, array, cellfuncs, [options]);
描述: 向指定id的table元素添加行。它使用数组中的每一个元素在table中创建一行。然后用cellfuncs数组中的没有函数创建一个列。单元格是依次用cellfunc根据没有数组中的元素创建出来的。 DWR1.1开始,addRows()也可以用对象做为数据。如果你用一个对象代替一个数组来创建单元格,这个对象会被传递给cell函数。 参数: id: table元素的id(最好是tbody元素的id) array: 数组(DWR1.1以后可以是对象),做为更新表格数据。 cellfuncs: 函数数组,从传递过来的行数据中提取单元格数据。 options: 一个包含选项的对象(见下面) 选项包括: rowCreator: 一个用来创建行的函数(例如,你希望个tr加个css). 默认是返回一个document.createElement("tr") cellCreator: 一个用来创建单元格的函数(例如,用th代替td). 默认返回一个document.createElement("td")
这是网上的例子
7.4 getText
getText(id)和getValue(id)很相似。出了它是为select列表设计的。你可能需要取得显示的文字,而不是当前选项的值。
这是网上的例子
7.5 getValue
DWRUtil.getValue(id)是 setValue()对应的"读版本"。它可以从HTML元素中取出其中的值,而你不用管这个元素是select列表还是一个div。 这个函数能操作大多数HTML元素包括select(去处当前选项的值而不是文字)、input元素(包括textarea)、div和span。
这是网上的例子
7.6 getValues
getValues()和getValue()非常相似,除了输入的是包含name/value对的javascript对象。name是HTML元素的ID,value会被更改为这些ID对象元素的内容。这个函数不会返回对象,它只更改传递给它的值。 从DWR1.1开始getValues()可以传入一个HTML元素(一个DOM对象或者id字符串),然后从它生成一个reply对象。
7.7 onReturn
当按下return键时,得到通知。 当表单中有input元素,触发return键会导致表单被提交。当使用Ajax时,这往往不是你想要的。而通常你需要的触发一些Javscript。 不幸的是不同的浏览器处理这个事件的方式不一样。所以DWRUtil.onReturn修复了这个差异。如果你需要一个同表单元素中按回车相同的特性,你可以用这样代码实现: <input type="text" onkeypress="DWRUtil.onReturn(event,submitFunction)"/> <input type="button" onclick="submitFunction()"/>
你也可以使用onkeypress事件或者onkeydown事件,他们做同样的事情。 一般来说DWR不是一个Javascript类库,所以它应该试图满足这个需求。不管怎样,这是在使用Ajax过程中一个很有用函数。 这个函数的工作原理是onSubmit()事件只存在于<FORM ...>元素上
这是网上的例子
7.8 selectRange
选择一个输入框中的一定范围的文字。 你可能为了实现类似"Google suggest"类型的功能而需要选择输入框中的一定范围的文字,但是不同浏览器间选择的模型不一样。这DWRUtil函数可以帮你实现。 DWRUtil.selectRange(ele, start, end)
这是网上的例子
7.9 setValue
DWRUtil.setValue(id, value)根据第一个参数中指定的id找到相应元素,并根据第二个参数改变其中的值。 这个函数能操作大多数HTML元素包括select(去处当前选项的值而不是文字)、input元素(包括textarea)、div和span。
这是网上的例子
7.10 setValues
setValues()和setValue()非常相似,除了输入的是包含name/value对的javascript对象。name是HTML元素的ID,value是你想要设置给相应的元素的值。
这是网上的例子
7.11 toDescriptiveString
DWRUtil.toDescriptiveString()函数比默认的toString()更好。第一个参数是要调试的对象,第二个参数是可选的,用来指定内容深入的层次: 0: 单行调试 1: 多行调试,但不深入到子对象。 2: 多行调试,深入到第二层子对象 以此类推。一般调试到第二级是最佳的。 还有第三个参数,定义初始缩进。这个函数不应该被用于调式程序之外,因为以后可能会有变化。
这是网上的例子
7.12 useLoadingMessage
这个方法将来可能被废弃,因为这个实现实在太专断了。为什么是红色,为什么在右上角,等等。唯一的真正答案就是:抄袭GMail。这里的建议是以本页面中的代码为模板,根据你的需求自定义。 你必须在页面加载以后调用这个方法(例如,不要在onload()事件触发之前调用),因为它要创建一个隐藏的div来容纳消息。
最简单的做法时在onload事件中调用DWRUtil.useLoadingMessage,像这样: <head> <script> function init() { DWRUtil.useLoadingMessage(); } </script> ... </head> <body onload="init();"> ...
可能有些情况下你是不能容易的编辑header和body标签(如果你在使用CMS,这很正常),在这样的情况下你可以这样做: <script> function init() { DWRUtil.useLoadingMessage(); } if (window.addEventListener) { window.addEventListener("load", init, false); } else if (window.attachEvent) { window.attachEvent("onload", init); } else { window.onload = init; } </script>
下面这些是这个函数的代码,它对于你要实现自己的加载消息很有用。这个函数的主要内容是动态创建一个div(id是disabledZone)来容纳消息。重要的代码是当远程调用时使它显示和隐藏: DWREngine.setPreHook(function() { $('disabledZone').style.visibility = 'visible'; }); DWREngine.setPostHook(function() { $('disabledZone').style.visibility = 'hidden'; }); This is fairly simple and makes it quite easy to implement your own "loading" message.
function useLoadingImage(imageSrc) { var loadingImage; if (imageSrc) loadingImage = imageSrc; else loadingImage = "ajax-loader.gif"; DWREngine.setPreHook(function() { var disabledImageZone = $('disabledImageZone'); if (!disabledImageZone) { disabledImageZone = document.createElement('div'); disabledImageZone.setAttribute('id', 'disabledImageZone'); disabledImageZone.style.position = "absolute"; disabledImageZone.style.zIndex = "1000"; disabledImageZone.style.left = "0px"; disabledImageZone.style.top = "0px"; disabledImageZone.style.width = "100%"; disabledImageZone.style.height = "100%"; var imageZone = document.createElement('img'); imageZone.setAttribute('id','imageZone'); imageZone.setAttribute('src',imageSrc); imageZone.style.position = "absolute"; imageZone.style.top = "0px"; imageZone.style.right = "0px"; disabledImageZone.appendChild(imageZone); document.body.appendChild(disabledImageZone); } else { $('imageZone').src = imageSrc; disabledImageZone.style.visibility = 'visible'; } }); DWREngine.setPostHook(function() { $('disabledImageZone').style.visibility = 'hidden'; }); }
然后你就可以这样使用:useLoadingImage("images/loader.gif");
7.13 Submission box
h1 非util.js中的功能 这里有一些功能不适合加入到DWRUtil中。它们在解决一下特殊问题是很有用,但是他们还不够通用以适用任何场合。
62 / 92
修补浏览器事件 如果你创建了一个DOM元素,然后用addAttribute在这个元素上创建了一个事件,那么他们不能被正常的触发。你可以使用下面的脚本来遍历一个DOM树,并重新为他们绑定事件,这样他们就能正常的触发了。 把'click'改成你希望的事件。 DWREngine._fixExplorerEvents = function(obj) { for (var i = 0; i < obj.childNodes.length; i++) { var childObj = obj.childNodes [i]; if (childObj.nodeValue == null) { var onclickHandler = childObj.getAttribute('onclick'); if (onclickHandler != null) { childObj.removeAttribute('onclick'); // If using prototype: // Event.observe(childObj, 'click', new Function(onclickHandler)); // Otherwise (but watch out for memory leaks): if (element.attachEvent) { element.attachEvent("onclick", onclickHandler); } else { element.addEventListener("click", onclickHandler, useCapture); } } DWREngine._fixExplorerEvents(childObj); } }
8.5 安全
我们很谨慎的对待DWR的安全问题,并且认为有必要解释一下避免错误要做的事情。 首先DWR让你明确哪些是被远程调用的,是如何被远程调用。原则就是DWR必须调用那些你明确允许的代码。 dwr.xml要求你为每一个远程类定义一个'create'项。你还可以通过指定include和exclude元素来更精确的控制远程调用Bean中可以被调用的方法。 除此之外如果你希望允许DWR在转换你的JavaBean到Javascript或者从Javascript转换到JavaBean时有一定的许可限制,同样可以精确控制哪些Bean的属性可以被转换。 一个很明显但又必须指出的 – 不要在生产环境中打开test/debug模式控制台。如何打开或关闭debug控制台在配置web.xml部分可以找到详细描述。
审查 - DWR带来的最大好处
很值得对比一下DWR和Servlet、JSP或周围的其他web框架。 如果你要审查基于DWR的功能,那是非常简单的。看看dwr.xml你就能得到一个哪些方法被暴露到外面的描述了。你也可以俯视全局用DWR可以访问哪些资源。 但是要在其他系统里做这件事可不是这么容易。如果是Servlet你需要检查WEB-INF/web.xml文件,然后检查写在Servlet中的request.getParameter(...)。如果是Struts和其他Framework你需要检查配置文件,然后顺着流程检查代码,看请求信息出了什么问题。
访问控制
DWR允许你通过两种基于J2EE的机制来进行访问控制。首先你可以基于J2EE角色定义DWR的访问。其次你可以在DWR里面定义访问方法的角色。
其他方面
DWR不允许你定义任何内部类的create和convert。这样设计是为了不出现意外的攻击来操作DWR的核心文件以提升访问权限。
风险
有什么机会可以让攻击者窥视你的系统呢?使用DWR你攻击者可以使服务器创建任何你在dwr.xml中指定的Java对象的实例。并且(如果你用BeanConverter)Java类的任何方法、以及方法任何参数都是可见的。这些类的任何一个属性都有可能是攻击者需要的。 如果你知道DWR是怎么工作的,这些都是很显而易见的结论,但是往往粗心会造成问题。如果你创建了一个有appendStringToFile()方法的FileBean的类,而且用DWR把它暴露出去,那么你就给了攻击者一个机会来填满你的文件系统。
69 / 92
你必须时刻注意用了DWR以后,有没有给攻击者什么机会。 一般来说这样的情景让人感觉使用DWR是有风险的,但是这样的问题在所有的传统web架构中都存在,只是在那些架构中这些不明显,所以就很难被修复。
保证更加安全
这已经很安全了,那么你还能做什么来保证更加安全了?首先记住上面这些关于审查的内容,当web应用的其他地方不安全时,即使它看上去很安全,过多的关注DWR是很愚蠢的。如果DWR让人感觉恐惧,那是因为它的问题都在明处。所以第一步是多检查几遍传统架构的问题。 你可以通过把可远程访问的类放到不同的包里,并且使用代理来防止DWR访问机制出问题。如果你愿意还可以再次检查基于角色的安全控制。这些内容只是在检查DWR已经为你做的事情。 比多检查几次更好的方法是检查DWR的源码,保证它是在正确的工作。这些代码已经被很多人检查过了,但多双眼睛总是有好处的。
整合Acegi
DWR可以整合Acegi security framework。更多的信息见整合DWR和Acegi.
完结。。