快捷搜索:

Project Zero 简介,第 2 部分: SOA 中的 RESTful 应用程

REST 风格的资本建模

本系列的 第 1 部分 “为 Web 利用法度榜样构建 RESTful 办事” 展示了若何用资本处置惩罚法度榜样和数据 API 将一个简单的表作为一个 RESTful 资本公开。此外,还构建了一个简单的 Dojo 利用法度榜样来调用这些办事。

本文将进一步增强该示例以展示若何以 RESTful 风格公开更繁杂的数据。并将经由过程构建第二个 Consumer 利用法度榜样来阐明若何用 Zero 为资本建模以及若何用 Zero 基于事故的客户机编程模型构建快捷的富客户机。

扩展示例

第 1 部分 中的演习是将一个优惠表作为 RESTful 办事公开。在本文中将扩展这个示例。图 1 显示了此用例图。

图 1. 用例

图片看不清楚?请点击这里查看原图(大年夜图)。

这里有两个角色:应用者和优惠。要构建的利用法度榜样有两个:

供给者优惠(Provider Incentive)利用法度榜样 为使供给者能治理其供给的优惠并能找到目标应用者,本文只偏重于为优惠构建 RESTful 办事。 应用者优惠(Consumer Incentive)利用法度榜样容许应用者查找优惠。它们将能经由过程供给者或位置来搜索优惠。

以利用法度榜样为中间和以办事器为中间的设计

企业中的平台(比如基于 Java™ EE 的办事器)服从的因此办事器为中间的思路。Web 利用法度榜样都要构建和支配到利用办事器平台上。办事器平台(比如 WebSphere®)能供给 Java EE 规范所要求的所有办事质量。这些办事的例子有基于行列步队的消息通报、散播式事务或协议治理。平日,利用办事器会在同一个 Java 虚拟机上运行多少利用法度榜样。架构师一样平常会环抱与其他利用法度榜样共享软件和数据资本及共享由利用办事器供给的办事(纵然在某些环境下,它们不必然会被用到)的开拓思路设计利用法度榜样。

纵然利用法度榜样支配于自力的利用办事器内,该办事器本身平日也会承载所有可用的办事。利用办事器容许企业级的集成。企业级集成的特性包括跨多种系统的散播式事务、用于关键数据发送的基于行列步队的消息通报或是各类其他类型的办事。图 2 中凸起显示了一个企业级集成的示例。企业中的平台大年夜都环抱治理各类协讲和中心件而设计。无意偶尔,它们会与办事很多利用法度榜样的企业数据库对话。

图 2. 企业集成

图片看不清楚?请点击这里查看原图(大年夜图)。

Web 2.0 还会涉及到 HTTP 级其余一类不是十分关键的集成。利用法度榜样平日环抱一组数据设计,这组数据的目的便是为了公布及与其他数据集混杂以创建新的利用法度榜样,这些新创建的利用法度榜样可能并非数据供给者预期的。图 3 展示了一个集成的示例。

图 3. Web 2.0 中的集成

利用法度榜样环抱数据设计并经由过程 HTTP 以 REST 风格公开。富 Internet 利用法度榜样之后可以经由过程混杂和匹配这些数据来创建新的情景。例如,一个地牟利用法度榜样可以在这个舆图上以 REST 风格公开其地点。与之完全不合的利用法度榜样,比如一个雇员治理对象,可以应用这个地牟利用法度榜样并加入一组雇员信息来创建雇员事情地点的可视舆图。这是经由过程交融两组数据实现的。在企业情况下,这样的一个利用法度榜样,只管不是很关键,可能也必要不光一个企业历程完成。

选择以办事器为中间照样以利用法度榜样为中间的设计并不完全取决于伸缩性。以利用法度榜样为中间的伸缩必要一个外部实用对象来对利用法度榜样进行伸缩处置惩罚,可能会涉及到诸如 WebSphere XD 这样的软件来治理历程和复制状态。以办事器为中间的设计则会涉及到加倍综合的伸缩办理规划,此中利用办事器会有内置的伸缩机制。

假如选择以利用法度榜样为中间的设计,开始时规模可以很小,跟着应用者的增添可以很轻易地进行伸缩,由此,可以实现 Web 2.0 风格设计的代价主张。另一个紧张的特征便是 Web 2.0 风格的利用法度榜样可以调用企业利用法度榜样,反之亦然。图 4 显示了一个能用 HTTP 造访企业利用法度榜样的 mashup。可以使用 WebSphere Web 2.0 Feature Pack 这样的技巧来以 REST 风格公开企业工件以便它们能介入到 mashup 或 Rich Internet Application (RIA) 中。

图 4. Mashup 造访企业利用法度榜样

这两种要领各有利弊。以办事器为中间的要领的优点是易于治理,而以利用法度榜样为中间的优点是便于开拓职员进行编程,但这种办理规划平日必要外部软件来治理。

RESTful 数据

正如您所见,能以 REST 风格公开数据对付能否成为 Web 2.0 mashup 的组成部分而言异常紧张。第 1 部分 评论争论了 REST 为何是 Project Zero 的关键。REST 是用来公开资本的。而资本是经由过程 Internet 公开信息的根基,是以可以方便地应用它组成下一类 Web 利用法度榜样。Mashup 能很快地由多个资本组合而成。Project Zero 的优化也是环抱以 REST 风格公开资本的理念。本文的这个示例中,我们要公开由 energy 供给者供给的优惠。图 5 中给出了此数据模型,显示了这个供给者及其与优惠之间的关系。

图 5. 供给者及优惠的数据模型

其目的是让感兴趣的人能够找到优惠。同时供给者还必要掩护这些优惠。表 1 展示了若何以 REST 风格公开这些数据(第 1 部分 展示了这种表若何有助于 REST 风格的资本建模)。

表 1. REST 设计

资本

URI

措施

表示

描述

供给者

/provider/

GET

JSON 工具

检索供给者记录

优惠

/provider/

/incentive

POST

JSON 工具

创建一个新优惠

优惠

/provider/

/incentive

GET

JSON 数组

为特定的 providerId 检索优惠列表

优惠

/provider/

/incentive/

GET

JSON 工具

检索个别优惠

优惠

/provider/

/incentive/

PUT

JSON 工具

更新单个优惠

优惠

/provider/

/incentive/

DELETE

删除单个优惠

优惠

/incentive?location=

GET

JSON 数组

在某个特定状态为随意率性一个供给者返回一个优惠列表

与第 1 部分不合,这里的优惠在供给者名称空间下是可造访的。优惠的生命周期取决于供给者,因而也由其供给者来治理。经由过程 REST 的风格,我们就能够确保此优惠记录存在于供给者内,从而满意需求。然而,有的应用者可能必要跨多个供给者搜索优惠。这时,可以应用 /incentive 名称空间来供给跨多个供给者的优惠列表。

过多的供给者会使 /incentive 名称空间变得很大年夜,以是必要设法对它做一些限定。这个示例仅容许用指定的位置造访 /incentive 名称空间。我们也可以选择应用查询参数要领来限定选择。

将非功能需求利用于 RESTful 办事

一旦定义了办事,就可以开始为它们附加办事质量了。非功能需求的形式和大年夜小各别。借助 REST,我们所供给的是基于 HTTP 的办事,因而非功能需求的利用可被简化。本示例将处置惩罚安然性,这意味着要经由过程确保 URL 将安然性规则利用于 REST 资本。表 2 显示了这个偏重于安然性的 REST 表的示例。

表 2. 确保 REST 资本的安然性

资本

URI

措施

角色

是否必要实例级安然性?

供给者

/provider/

GET

所有

优惠

/provider/

/incentive

POST

供给者

是:某供给者只能治理其自身的优惠。

优惠

/provider/

/incentive

GET

所有

优惠

/provider/

/incentive/

GET

所有

优惠

/provider/

/incentive/

PUT

供给者

是:某供给者只能治理其自身的优惠。

优惠

/provider/

/incentive/

DELETE

供给者

是:某供给者只能治理其自身的优惠。

优惠

/incentive?location=

GET

所有

任何其他资本

/

ALL

均不得当

表 2 显示了这个 URL 资本及可造访此资本的角色。REST 为公开实体而定义了一种模式,斟酌到这一点十分紧张。平日,一些数据可能会必要基于实例的安然性;只稀有据的所有者才能查看这些数据。在本例中,只有拥有优惠的供给者才能创建、更新和删除优惠,仅仅具有供给者角色是不敷的。上面的设计表中有一栏标记了数据是否必要受实例保护。

运行示例的先决前提

要想运行本文中的示例,必要:

Eclipse 3.2 或更高的版本。在本例顶用的是 Eclipse 3.3。

Project Zero 针对 Eclipse 的 Java 和 Groovy 插件。

本示例是用 Zero 的 M3 宣布版构建的。请确保 已将 Eclipse Update Site 设为 http://www.projectzero.org/update/zero.eclipse.M3 。

本演习不必要 PHP 插件。要想得到关于安装此插件的信息,请拜见 Project Zero 下载。

留意:假如已完成了 第 1 部分 的操作,那么在安装 M3 特点和插件前,必须先卸载 M1 插件。请参考 Eclipse 赞助获取删除插件的相关指示。

将本文的 下载 文件解压缩到谋略机的 C 盘。

Firefox 浏览器

对本系列第 1 部分中所先容的观点有很好的理解。

其他有用的对象:

针对 HTML/JavaScript 对象的 Aptana Eclipse 插件

Eclipse Europa

FireBug

针对 FireFox 的 Poster 插件

构建供给者利用法度榜样

我们将从构建供给者利用法度榜样开始。目标是创建前面定义过的 RESTful 办事。Incentive 利用法度榜样构建于 第 1 部分 中先容的常识的根基上。

创建新的利用法度榜样

创建新的利用法度榜样:

打开一个新的事情空间。

图 6.

创建一个新项目。

图 7.

选择 Project Zero -> Project Zero Application 作为项目类型。

图 8.

将利用法度榜样命名为 ProviderIncentiveApp。

图 9.

添加依附项

必须添加所必要的依附项。如第 1 部分中所示,ivy 技巧可用来治理依附项。

打开 config 目录下的 ivy.xml 文件。

图 10.

在 Dependencies 下,单击 Add。

图 11.

添加下面的依附项:

zero.data

zero.data.setup.webtools

derby

依附项领导如下所示。

图 12.

保存 ivy.xml 文件。若要查看此资本,其内容应如清单 1 所示。

清单 1

单击 Update Dependencies 图标。此图标将同时在远程存储库和本地存储库中查找所有设置设置设备摆设摆设包的最新版本。

图 13.

单击 OK 查找更新。

图 14.

完成后关闭编辑器。

创建表

我们将应用 derby 的嵌入式版本进行利用法度榜样编程。这是一个很好的开拓选择。要创建本例所需的表,必要应用 Project Zero 的 M3 驱动中新引入的数据库设置对象。

右键单击 ProviderIncentiveApp 项目。

图 15.

选择 General->File System。

图 16.

假设已将可下载的文件都解压到了 C: 目录下,选择 C:\ProjectZeroArticleSeries/Part2Artifacts/ProviderAppArtifacts。 选择 sql,然后单击 Finish。

图 17.

此时的项目目录结构将如下图所示。

图 18.

打开 create.sql 文件,将看到用于创建 PROVIDER 和 INCENTIVE 表的 DDL,如清单 2 所示。

清单 2

CREATE TABLE PROVIDER (

PROVIDER_ID VARCHAR (50) NOT NULL,

NAME VARCHAR(256) NOT NULL,

DESCRIPTION VARCHAR(256) NOT NULL,

LOCATION VARCHAR(50) NOT NULL,

PROVIDER_TYPE VARCHAR(128),

CONTACT VARCHAR(256));

ALTER TABLE PROVIDER ADD CONSTRAINT PROVIDER_PK PRIMARY KEY (PROVIDER_ID);

CREATE TABLE INCENTIVE (

INCENTIVEID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1, INCREMENT BY 1),

|--10--------20--------30--------40--------50--------60--------70--------80--------9|

|-------- XML error: The previous line is longer than the max of 90 characters ---------|

NAME VARCHAR(256) NOT NULL,

DESCRIPTION VARCHAR(256) NOT NULL,

PROVIDER_ID VARCHAR (50) NOT NULL,

INCENTIVETYPE VARCHAR(128),

VALIDFROM TIMESTAMP,

VALIDTO TIMESTAMP,

WEBSITE VARCHAR(256)

);

ALTER TABLE INCENTIVE ADD CONSTRAINT INCENTIVE_PK PRIMARY KEY (INCENTIVEID);

ALTER TABLE INCENTIVE ADD CONSTRAINT INC_PPR_FK FOREIGN KEY (PROVIDER_ID)

REFERENCES PROVIDER (PROVIDER_ID);

这里有一个用来删除表和添加示例数据的文件。接下来,必要运行利用法度榜样。右键单击项目并选择 Run As -> Project Zero Application。

图 19.

反省节制台以确保它已启动并正在运行。

图 20.

打开浏览器并进入 http://localhost:8080/setup。浏览器应如下图所示。

图 21.

键入下面的信息,然后单击 Create Tables。

Database Name: PRO_DB

Database Type: Apache Derby (Embedded)

User Name: APP

图 22.

此对象将申报它已成功履行了 create.sql 脚本。

单击 Add Sample Data。此对象应该申报它成功履行了 sample.sql 脚本。

图 23.

单击节制台中的 Stop 按钮终止利用法度榜样。在开拓时以这种要领启动和终止利用法度榜样是可以的。要想在测试和临盆情况中启动和终止利用法度榜样,最好应用治理敕令行。

图 24.

为优惠利用法度榜样创建 RESTful 办事

要为优惠利用法度榜样创建 RESTful 办事:

右键单击 app/resources 文件夹并选择 New -> File。

图 25.

将这个文件命名为 provider.groovy。

图 26.

单击 Yes 给项目添加 groovy 支持。

图 27.

确保 provider.groovy 已打开。输入 清单 3 所示的代码(也可从 C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet\proSnippet.1.txt 中将它粘贴过来)。

这段代码应用数据 API 履行查询并将结果存储为 JSON 款式。此模式在本系列的 第 1 部分 中曾先容过。

onRetrieve 措施用来表示一种处置惩罚法度榜样以获取 Provider 的单个实例。此模式在 第 1 部分 中先容过。

在这个办事之上还有一个注释。Project Zero 以这种要领对 RESTful 资本进行归档。Project Zero 将用它来出现 RESTful 文档以及用于 REST 办事的测试对象。稍后还会用到这个对象。本例中,我们对可用的 HTTP 返回代码进行了归档、对款式进行了描述,还给出了一个返回值的示例。

清单 3

import zero.data.groovy.Manager;

/**

*

* @success 200 Returns the profile for the provider with the given ID.

* @error 404 Not authorized Provider for User

* @format application/json

* @example

* {

*  "name": "Energy Provider Name",

*  "description": "Sample Output",

*  "location": "New Jersey"

*  "provider_type": "energy"

*  "contact": "roland@projectzero.org"

* }

*

*/

def onRetrieve()

{

def data = Manager.create('PRO_DB');

def result = null;

def id = request.params.providerId[0];

//Note: Next piece of code should be in one line.

result =

data.queryFirst

("select name,description,location,provider_type,contact from provider where

provider_id = $id");

if(result != null) {

request.view='JSON'

request.json.output = result

render()

} else {

request.status = HttpURLConnection.HTTP_NOT_FOUND

request.error.message = "Provider $id not found."

request.view = "error"

render()

}

}

在应用这个数据库对象时,数据库的设置设置设备摆设摆设会在 data.config 文件内天生。利用法度榜样要应用此设置设置设备摆设摆设,必须将它包孕在主 zero.config 内。打开 zero.config 文件。

图 28.

反省 zero.config 文件的第一部分。请分外留意新的简化了的设置设置设备摆设摆设语法。

图 29.

下图所示的第二个条款显示了所天生的数据设置设置设备摆设摆设。这个用于繁杂属性的新语法采纳了 JSON 款式。

图 30.

用菜单条中的启动按钮启动利用法度榜样。

图 31.

用浏览器进入 http://localhost:8080/resources/docs。 下图显示了可用的 RESTful资本。选择 Provider。

图 32.

资本文档对象出现了在 groovy 文件中所注释的信息。此 REST 对象也可用来测试办事。

图 33.

经由过程单击款式,如下所示,可以看到这个示例。

图 34.

图 35.

经由过程单击如下所示的 URI ,可测试此 RESTful 办事。

图 36.

单击 Send。

图 37.

结果将会显示返回的 JSON 工具。

图 38.

创建优惠办事

这一节我们将创建优惠办事并将它与供给者办事进行关联。

在 /app/resources 文件夹中创建一个名为 incentive.groovy 的新文件。

图 39.

在 /app/resources 文件夹中创建一个名为 incentive.bnd 的新文件。

图 40.

输入下面两行: provider/incentive

incentive

以上定义了供给者与优惠之间的关系。在本例中,指定了既支持 /provider/

/incentive,也支持 /incentive 名称空间。若想只容许嵌套模式,那么只指定 provider/incentive 即可。

图 41.

加入下面的 onList 措施(或从 C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet/proSnippet3.txt 中粘贴)。请留意我们应用 groovy 快捷要领反省报头,首先查看供给者是否存在。假如存在,就为供给者履行一个查找所有优惠的查询。假如 providerId 不存在 ,那么这个哀求便是针对 /incentives 名称空间的。这里还有一个对位置参数存在与否的反省。假如存在,就会经由过程查询履行一个 SQL 搜索。反之,则不容许任何其他调用。

清单 4

import zero.data.groovy.Manager;

/**

*

* @success 200 Returns incentives by provider or location.

* @error 404 Cannot Query all Incentives in the system.

/provider/providerId/incentive or /incentive?location=locationValue

must be used.

* @format application/json

* @example

* {

*  "incentiveId" : "Incentive Id"

*  "name": "Incentive Name",

*  "description": "Sample Output",

*  "providerName": "Provider Name"

*  "providerId": "Provider Id"

*  "incentive_type": "Incentive Type"

*  "validfrom": 1189396800000,

*  "validto": 1189396800000,

*  "website": "http://www.projectzero.org"

* }

*

*/

def onList()

{

def data = Manager.create('PRO_DB');

def provider = null

def location = null;

// List by provider flag

if(request.params.providerId)

provider = request.params.providerId[0];

// List by location flag

if(request.params.location)

location = request.params.location[0];

def result = null;

if(provider != null)

{

result = data.queryList("select i.incentiveId,i.name,p.name

as providerName,i.provider_id,i.incentivetype as

incentive_type,i.validFrom,i.validTo,i.website,p.location from provider

as p,incentive as i where p.provider_id = $provider and i.provider_id =

p.provider_id");

}

else if (location != null)

{

result = data.queryList("select i.incentiveId,i.name,p.name

as providerName,i.provider_id,i.incentivetype as

incentive_type,i.validFrom,i.validTo,i.website,p.location from provider

as p,incentive as i where p.location = $location and i.provider_id =

p.provider_id");

}

else

{

request.status = HttpURLConnection.HTTP_NOT_FOUND

request.error.message = "Cannot Query all Incentives in the

system. /provider/providerId/incentive or

/incentive?location=locationValue must be used."

request.view = "error"

render()

return;

}

if(result != null) {

request.view='JSON'

request.json.output = result

render()

}

}

保存这个文件。

回到浏览器,进入 http://localhost:8080/resources/doc 并选择 Incentive 链接。

图 42.

优惠 URI 应该在此 provider 名称空间下。

在 M3 中有一个 bug,在那里,Resource 文档仅显示了一种资本模式。这鄙人一个 Milestone 将被矫正。

图 43.

单击 URI。要获取 Provider ID,输入 austinEnergy。

图 44.

应该会获得 Austin Energy 的优惠列表。

图 45.

在浏览器中,试着在 http://localhost:8080/resources/incentive 下查找一个优惠。应该会收到一个如下所示的差错信息。

图 46.

应用浏览器或类似 Firefox Poster 这样的对象,履行 GET 哀求 http://localhost:8080/resources/incentive?location=Texas。下图显示了应用 Poster 对象后的结果。

图 47.

返回到 incentive.groovy 文件,输入清单 5 中的代码(或是从 C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet/proSnippet3.txt 中粘贴)。这段代码包孕检索某个优惠和创建、更新及删除某个优惠的调用。这里的代码与 第 1 部分 中的代码十分相似。

清单 5

/**

*

* @success 200 Returns incentive by id.

* @error 404 Incentive Id not found.

* @format application/json

* @example

* {

*  "incentiveId" : "Incentive Id"

*  "name": "Incentive Name",

*  "description": "Sample Output",

*  "providerName": "Provider Name"

*  "incentive_type": "Incentive Type"

*  "validfrom": 1189396800000,

*  "validto": 1189396800000,

*  "website": "http://www.projectzero.org"

* }

*

*/

def onRetrieve()

{

def data = Manager.create('PRO_DB');

def id = request.params.incentiveId[0];

result = data.queryFirst("select

i.incentiveId,i.name,i.description,p.name as

providerName,i.incentivetype as

incentive_type,i.validFrom,i.validTo,i.website from provider as

p,incentive as i where i.incentiveId = $id");

if(result != null) {

request.view='JSON'

request.json.output = result

render()

} else {

request.status = HttpURLConnection.HTTP_NOT_FOUND

request.error.message = "Incentive $id not found."

request.view = "error"

render()

}

}

/**

*

* @success 204 Post new Incentive for the provider.

* @error 403 Not authorized to insert this record.

* @error 404 Cannot Post directly to /incentives, only

/provider/

/incentive

* @format application/json

* @example

* {

*  "name": "Incentive Name",

*  "description": "Sample Output",

*  "incentive_type": "Incentive Type",

*  "validfrom": 1189396800000,

*  "validto": 1189396800000,

*  "website": "http://www.projectzero.org"

* }

*

*/

def onCreate()

{

def provider = request.params.providerId[0];

if(provider != null)

{

def data = Manager.create('PRO_DB');

def incentive = zero.json.JsonType.fromData(request.input[]).getJson()

def user = request.subject['remoteUser'];

def validFrom = new java.sql.Timestamp(incentive.validfrom);

def validTo = new java.sql.Timestamp(incentive.validto);

def key = data.insert("insert into incentive

(name,description,provider_id,incentivetype,validfrom,validto,website)

values ($incentive.name,$incentive.description,$provider,$incentive.incentivetype,

$validFrom,$validTo,$incentive.website)",['incentiveId']);

def locationUri = getRequestedUri(false) + '/' + key

request.headers.out.Location = locationUri

request.status = HttpURLConnection.HTTP_NO_CONTENT

}

else

{

request.status = HttpURLConnection.HTTP_NOT_FOUND

request.error.message = "Cannot POST directly to /incentive,

only /provider/

/incentive."

request.view = "error"

render()

return;

}

}

/**

*

* @success 204 Delete Incentive for the provider.

* @error 403 Not authorized to insert this record.

* @error 404 Cannot delete directly to /incentives,

only /provider/

/incentive

*

*/

def onDelete()

{

def provider = request.params.providerId[0];

if(provider != null)

{

def data = Manager.create('PRO_DB');

def id = request.params.incentiveId[0];

def user = request.subject['remoteUser'];

data.update("delete from incentive where incentiveId = $id");

request.status = HttpURLConnection.HTTP_NO_CONTENT

}

else

{

request.status = HttpURLConnection.HTTP_NOT_FOUND

request.error.message = "Cannot DELETE directly to

/incentive, only /provider/

/incentive."

request.view = "error"

render()

return;

}

}

/**

*

* @success 204 Incentive updated for provider.

* @error 403 Not authorized to update this record.

* @error 404 Cannot Put directly to /incentive/,

only /provider/

/incentive/

* @format application/json

* @example

* @example

* {

*  "name": "Incentive Name",

*  "description": "Sample Output",

*  "incentive_type": "Incentive Type",

*  "validfrom": 1189396800000,

*  "validto": 1189396800000,

*  "website": "http://www.projectzero.org"

*

* }

*

*/

def onUpdate()

{

def provider = request.params.providerId[0];

if(provider != null)

{

def data = Manager.create('PRO_DB');

def incentive = zero.json.JsonType.fromData(request.input[]).getJson()

def user = request.subject['remoteUser'];

def id = request.params.incentiveId[0];

def validFrom = new java.sql.Timestamp(incentive.validfrom);

def validTo = new java.sql.Timestamp(incentive.validto);

data.update("update incentive set

name=$incentive.name,description=$incentive.description,incentivetype =

$incentive.incentiveType,validfrom=$validFrom,validto=$validTo,website=

$incentive.website where incentiveId = $id ");

request.status = HttpURLConnection.HTTP_NO_CONTENT

}

else

{

request.status = HttpURLConnection.HTTP_NOT_FOUND

request.error.message = "Cannot Put directly to

/incentive/, only

/provider/

/incentive/."

request.view = "error"

render()

return;

}

}

确保操作的安然性

作为需求的一部分,还必要确保对优惠的创建、更新和删除操作针对的是此优惠的供给者。我们将从应用内置的 Zero File Registry 开始。Zero File Registry 是一个不错的开拓选择。Project Zero 也支持基于 LDAP 的注册表。一个常见的最佳实践是在产品情况中应用 LDAP。对付 Project Zero 而言,在开拓时覆盖默认的注册表十分轻易。

在浏览器中输入 http://localhost:8080/zero/webtools/user,如下所示。

输入以下内容:

UserName: austinEnergy

Password: passw0rd

Groups: Provider

单击 Add。

图 48.

输入另一个用户,如下所示。

UserName: centerPoint

Password: passw0rd

Group: Provider

单击 Add。

图 49.

现在 File Registry 中有了两个用户。

图 50.

反省 config 目录,将看到如下所示的 zero.users 文件(可能必要刷新 Eclipse Project)。

图 51.

添加授权规则

此时,必要添加授权规则并确保 RESTful URI 的安然性。这里必要用一个不存在的组保护不受支持的 URI。此外,还必要对所有者确保 RESTful 数据的安然性,从这可以看出 Project Zero 对基于实例的安然性的支持。

在 config 目录中创建一个新文件。

图 52.

将文件命名为 security.config。

图 53.

输入 清单 6 中的内容(或是从 C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet\proSnippet4.txt 中粘贴)。

rule.config 已包括在内,并且输入了一组参数。uri 定义 REST url。垂直滚动条中的内容也将把 URI 模式之后的所有工作斟酌进去。第一个规则会阻拦任何人在 /incentive 名称空间上履行 update HTTP 措施。第二个规则仅容许已登录的供给者履行 update 措施。例如,假如我以 austinEnergy 登录,那么我只可以在 austinEnergy 名称空间上发出 POST、DELETE 或 PUT。

清单 6

@include "${/config/dependencies/zero.core}/config/security/rule.config"

{

"uri":"/resources/incentive|",

"condition":"urimatches",

"authType":"Basic",

"groups":"[NO_ONE]",

"methods":"DELETE|POST|PUT"

}

@include "${/config/dependencies/zero.core}/config/security/rule.config"

{

"uri":"/resources/provider/{remoteUser}|",

"condition":"urimatches",

"authType":"Basic",

"methods":"DELETE|POST|PUT"

}

打开 config 目录中的 zero.config 文件。

图 54.

为 security.config 文件添加一个 include 项。

图 55.

测试安然性

至此已确保了 RESTful 资本的安然性,现在就可以用 REST 文档对象测试安然性了。

终止(或经由过程单击节制台上的 Stop 按钮) 并重启 Provider 利用法度榜样。可以应用快捷要领进行启动。

图 56.

让浏览器进入到 http://localhost:8080/resources/docs。 单击 Incentive。

图 57.

将看到所有的可用 HTTP 措施,如下所示。

图 58.

单击 GET 获取单个记录。

图 59.

输入 austinEnergy 作为 Provider ID;输入 1 作为 Incentive ID。

图 60.

应该可以得到此 JSON 工具。

图 61.

关闭结果窗口。

单击 POST 的款式。

图 62.

复制此 JSON 示例。

图 63.

单击 POST URI。将值粘贴到 Body 并输入 austinEnergy 作为 Provider ID。

图 64.

此时,应该进行身份验证。

输入 austinEnergy 作为用户名;输入 passw0rd 作为密码。

图 65.

获得的结果如下所示。记录提交后结果的 Location。

图 66.

假如有兴趣,可以用 5 测试 PUT 和 DELETE。Delete 应如下图所示。

图 67.

以 austinEnergy 登录后,从新用 URI 打开 POST。

试着在 centerPoint URI 下 POST 数据。

图 68.

应该会获得 403 Forbidden 差错。

图 69.

本演习中,我们已经完成了利用法度榜样的 Incentive 部分。在构建 Consumer 示例时,此利用法度榜样还要维持运行。

构建 Consumer 利用法度榜样

后续文章会具体先容 Consumer 利用法度榜样和 RESTful 数据。

至此,您应该已经懂得了若何应用数据 API 构建资本处置惩罚法度榜样。在 M2 中,Project Zero 引入了另一种为资本建模的措施。一些开拓职员倾向于对 SQL 进行编程,而另一些开拓职员则更倾向于应用一种持久技巧。Zero 资本模型更偏重于将数据映射到 REST(而不必然是工具)。

除 Zero 资本外,您还将应用 Zero Web 模板技巧来构建一个快捷的客户机,此客户机将使用 Zero Connection API 调用远程的 REST 资本。

创建新的利用法度榜样

第一步是创建一个新利用法度榜样。

创建一个名为 ConsumerIncentiveApp 的新 Project Zero 利用法度榜样。

图 70.

右键单击 ConsumerIncentive 利用法度榜样并选择 Import。

图 71.

From 目录应为 C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts。只选择 sql 文件夹,如下所示。

图 72.

查看如下图中所示的 create.sql 文件。

图 73.

留意这个很简单的 Consumer 表只有 4 个字段。

图 74.

打开 zero.config 文件。

图 75.

将默认端口数改为 8081,以便能同时运行两个利用法度榜样。

图 76.

打开 ivy.xml。

图 77.

添加下面的依附项:

derby

dojo

zero.data

zero.data.setup.webtools

zero.web.template

zero.resource

图 78.

更新依附项。

图 79.

运行 Consumer。

图 80.

反省节制台以确保它在端口 8081 上运行。

图 81.

为 Consumer 利用法度榜样运行数据库设置对象。Port 应为 http://localhost:8081/setup/。

输入 CON_DB 作为数据库名,Apache Derby (Embedded) 作为数据库类型,APP 作为用户名。

图 82.

单击 Add Sample Data。

图 83.

再次为利用法度榜样打开 zero.config 文件。输入清单 7 中的内容(或是从 C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet1.txt 中粘贴)。这将见告资本模型该应用哪个数据库。

清单 7

config/db/zero-resource/config=

{

"class":"org.apache.derby.jdbc.EmbeddedDataSource",

"databaseName":"CON_DB"

}

图 84.

终止 Consumer(而非 Incentive)利用法度榜样。

创建一个简单的资本模型

本节,将在 Consumer 表的根基上创建一个简单的资本模型。因为 M2 还只是这项技巧的预览宣布,以是我们将采纳一种异常简单的模式来演示它的用法(在本系列的后续部分,我们将应用一个轻细繁杂一些的示例)。

右键单击 app 目录并选择 New -> Source folder 来创建一个新资本文件夹。

图 85.

创建一个名为 app/models 的文件夹。

图 86.

在 app/models 目录中创建一个名为 consumer.groovy 的新文件。

图 87.

单击 Finish 添加 Groovy 支持。

将清单 8 中的代码输入到 consumer.groovy(或是从 C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet2.txt 粘贴)。这样就创建了一个简单的模型。

清单 8

import zero.resource.fields.*

fields = [

name: [type:'CharField'],

state:[type:'CharField'] ,

provider:[type:'CharField']

]

现在就可以用 Zero API 或 HTTP 在本地造访数据了。

要用 HTTP 公开模型,必要在 /app/resources 中创建另一个文件。

图 88.

将它也命名为 consumer.groovy。这时会呈现一个已存在 consumer.groovy 文件的差错报警,可以轻忽此差错。

图 89.

输入代码 ZRM.delegate(),如下所示,它将使用 HTTP 公开模型。

图 90.

假如 Consumer 利用法度榜样正在运行,就终止 Consumer(而非 Provider)利用法度榜样,然后再启动此 consumer 利用法度榜样。

图 91.

打开浏览器(或用 Poster Firefox 插件)进入 http://localhost:8081/resources/consumer。

图 92.

打开结果 JSON 文本,查看应用者列表。

图 93.

打开以下 URL: http://localhost:8081/resources/consumer/1 。将会获得一个记录。

图 94.

应该会返回一条单个记录。

图 95.

创建一个简单的客户机

我们将在这一节用 Zero Web 模板创建一个很简单的富 Internet 客户机。 第 1 部分 曾展示了 Dojo 客户机若何调用 RESTful 办事。Zero 也支持其他模式,比如访存 HTML 片段,我们在后面会对它做进一步阐明(在这个系列的后续部分,我们将联合应用 Dojo 和 Zero 的模板功能)。

Zero 客户机编程模型让 Zero 办事器事故能够相应 JavaScript 事故。它们还可以应用全局高低文的客户机区共享数据。

在 Public 目录下创建一个新文件。

图 96.

将它命名为 incentiveSearch.zhtml。

图 97.

输入 清单 9 中的 HTML 代码(或从 C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet3.txt 中粘贴)。

这个 HTML 模板应用了两个 Dojo Grid 组件:一个用来保存应用者数据,另一个用来保存优惠结果。应用 groovy 说话让您可以在办事器或是客户机上创建利用法度榜样事故以相应 UI 事故。Project Zero 应用下面的定义语法来声明一个具有 Ajax 生命周期的利用法度榜样事故:

on("").fire("").before

("").after("")

before_event

指定在所有 Ajax 哀求之前所触发的事故,用来显示加载、数据验证等。

after_event

指定在所有 Ajax 哀求之后所触发的事故,用来暗藏加载、UI 更新等。

application_event

已触发的事故,比如更新/访存数据、更新 UI 或任何定制的 biz/UI 逻辑。

在运行时,事故处置惩罚序次为:before_event --> application_event --> after_event。

application_even 应在办事器端处置惩罚,这样 Ajax 生命周期才故意义。before_event 和 after_event 平日在客户机端处置惩罚。

清单 9

Consumer Incentive Search

|--10--------20--------30--------40--------50--------60--------70--------80--------9|

|-------- XML error: The previous line is longer than the max of 90 characters ---------|

在 Public 目录中创建另一个名为 incentiveSearch.groovy 的文件。这个文件将包括相应客户机事故的事故处置惩罚法度榜样。

图 98.

输入 清单 10 中的代码(或从 C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet4.txt 中粘贴)。

当模板装入时,onInitialize 措施会被调用。对付资本模型,我们应用本地 API 得到应用者并将它放在客户机区中。

对付每个事故,这里都有一个响应的事故处置惩罚法度榜样。它反省哀求参数是否存在,并据此抉择是履行供给者搜索照样位置搜索。 我们还应用了 JSON API 将有效负载转入客户机区。这个客户机区可由 JavaScript 客户机和办事器 groovy 代码造访。

清单 10

import zero.core.connection.*;

import zero.resource.*;

def onInitialize()

{

def collection = TypeCollection.retrieve('consumer');

def result = collection.list();

def items = [];

for(resultItem in result)

{

def item =

[name:resultItem.name ,id:resultItem.id ,state:resultItem.state,

provider:resultItem.provider ]

items.add(item);

}

client.data.consumer = items;

client.data.incentives = [];

}

def onIncentiveSearch()

{

if(event.input.location[])

{

Connection.Response response =

Connection.doGET("http://localhost:8080/resources/incentive?location=

${event.input.location[]}");

client.data.incentives =

zero.json.java.JSONArray.parse(response.getResponseBodyAsString());

}

else if(event.input.provider[])

{

Connection.Response response =

Connection.doGET("http://localhost:8080/resources/provider/

${event.input.provider[]}/incentive");

client.data.incentives =

zero.json.java.JSONArray.parse(response.getResponseBodyAsString());

}

else

{

client.data.incentives = [];

}

}

在 Public 目录中创建一个新文件并将它命名为 incentiveSearch.js。此文件将会具有客户机事故。 这些回调被用来出现办事器调用的结果。

图 99.

输入下面清单 11 中的代码。Dojo FileStore 与 Grid API 被用来本地加载数据。

清单 11

formatDate = function(date) {

var d = new Date();

d.setTime(date);

return d;

}

formatLocationLink = function(location) {

var locationLink = '';

return locationLink;

}

formatProviderLink = function(provider) {

var providerLink = "";

return providerLink;

}

var consumerLayout = [{

cells: [[

{name: 'Id', field: "id", width:"10%"},

{name: 'Name', field: "name", width:"30%"},

{name: 'State', field: "state",

width:"35%",formatter:formatLocationLink},

{name: 'Provider', field: "provider",

width:"25%",formatter:formatProviderLink}

]

]

}];

var incentiveLayout = [{

cells: [[

{name: 'Provider', field: "providername",width:"20%"},

{name: 'Type', field: "incentive_type",width:"20%"},

{name: 'Provider Location', field: "location",width:"20%"},

{name: 'Valid From', field: "validfrom",width:"20%",formatter:formatDate},

|--10--------20--------30--------40--------50--------60--------70--------80--------9|

|-------- XML error: The previous line is longer than the max of 90 characters ---------|

{name: 'Valid To', field: "validto",width:"20%",formatter:formatDate}

]

]

}];

var consumerMeta =

{

id:'id',

items:[],

label:'consumer'

}

var incentiveMeta =

{

id:'incentiveid',

items:[],

label:'incentive'

}

function onLoad(){

zfire("renderConsumers");

}

function onRenderConsumers()

{

consumerMeta.items = dojo.clone(zget("/client/data/consumer"));

console.debug(consumerMeta.items);

var consumerStore = new dojo.data.ItemFileWriteStore({data: consumerMeta});

var consumerModel = new dojox.grid.data.DojoData();

consumerModel.store = consumerStore;

consumerModel.query = {id:'*'};

var consumerGrid = dijit.byId('consumerGrid');

consumerGrid.setModel(consumerModel);

}

function onIncentiveRender()

{

incentiveMeta.items = dojo.clone(zget("/client/data/incentives"));

console.debug(incentiveMeta.items);

var incentiveStore = new dojo.data.ItemFileWriteStore({data: incentiveMeta});

var incentiveModel = new dojox.grid.data.DojoData();

incentiveModel.store = incentiveStore;

incentiveModel.query = {incentiveid:'*'};

var incentiveGrid = dijit.byId('incentiveGrid');

incentiveGrid.setModel(incentiveModel);

}

incentiveSearch.css 文件用于增强 UI。将 C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\incentiveSearch.css 导入到 public 目录中。

图 100.

经由过程打开浏览器并进入 http://localhost:8081/locationSearch.zhtml 启动 UI。客户机应该按如下所示出现。

图 101.

单击 Texas 链接以触发一个优惠搜索。

图 102.

同样地,单击每个 provider 以触发供给者搜索。

图 103.

图 104.

停止语

本文先容了有关 Zero 的一些新观点,评论争论了以利用法度榜样为中间的设计,着重先容了若何公开繁杂数据和确保基于 URL 的资本的安然性。此外,您还懂得了新的资本建模和 Zero 客户机 API。

本系列的下一期文章将进一步评论争论资本建模,重点先容应用 Dojo 1.0 和 Zero 客户机编程模型来组装更多的办理规划。

您可能还会对下面的文章感兴趣: