1.引言
在看博客之前,首先重点说明,本博客中使用的软件版本:
- ArcGIS Server10.2
- ArcGIS Object10.2
如果你的版本是10.0,本博客中的代码肯定不可以用(10.0版本到10.1版本是一个大的提升)
如果你的版本是10.1,本博客中的代码需要稍微修改(10.1版本到10.2版本会有小部分的修改)
2. 什么是SOE?
我们开发SOE之前首先要弄明白什么是SOE?SOE的全称为:服务器对象扩展(Server Object Extension)。一个SOE简单的来说 就是一组包含方法的类(注意这里是一组方法的类),Server Objects 是粗粒度的AO 组件,是一个可以执行特定操作的高级对象,它隐藏了细粒度的ArcObject。Server Objects的粗粒度接口支持对于像绘制地图或地理编码等整体性任务。Server objects 也有SOAP接口,可以把 Server objects暴露成Web Service,从而可以被客户端使用。
3. SOE的优势
- SOE可以作为SOAP或REST Web服务,使得用ArcGIS Web APIs 建立的客户(用 于Javascript、Flex、Silverlight、iOS等)以便调用这些应用程序。事实上,您的 SOE将出 现在ArcGIS Services Directory 之内,并将提供特性设置、基本类型等ArcGIS APIs能够理 解的典型对象类型。
- SOE能够对ArcObjects进行有效封装,提供理想环境以快速执行您的指令。 . 可以建立一个SOE,使用动态分段获取里程标志位置,或者实现几何网络分析(在ArcGIS 10 的时候,因为工具箱中没有提供几何网络的 GP,因此要实现几何网络的分析,可以通过 这种办法)
4. SOE过程(图像解释)
SOE开发需要 ArcObjects和.NET或者Java(Java或者C#二选一)以及 REST 与SOAP 等Web 服务通信技术的知识。ArcObjects SDK 具有多种可供您进行校验的样本,即可用于Java也可用于.NET。
在SOE的开发过程中,我们必须清楚,我们需要在 SOE中将请求获取,然后将传入的参数 转化为AO,然后通过AO 处理,再将处理的结果转成json格式,传给客户端,客户端得到json 格式的结果,然后解析
下图描述了请求响应的整个过程,如下图:
上面这个图片可能会涉及一些细节(数据类型),如果大家看不懂,大家可以看下面这个简单的图:
在上图我们需要解决的三个问题:
- 如何在SOE中获取客户端传来的数据
- 如何在服务器端读取地图服务数据
- 如何将运行后的结果传回客户端
5. SOE需要的接口
5.1 必选接口IRESTRequestHandler
这个接口主要有两种方法:
string GetSchema();
byte[] HandleRESTRequest();
其中byte[] HandleRESTRequest();
方法主要有两个作用:
- 回调资源和操作的方法
- 获取资源在实例级别的描述
该方法在识别这两个作用的时候是通过operationName
参数,如果该参数是空字符产那就是第二个作用,否则是第一个作用。
byte[] HandleRESTRequest();
的参数:
String capabilities:
一组被资源授权的操作,可以为空字符串String resourceName:
资源名称. 空字符串表示根级别,子资源会通过‘/’ 表示String operationName:
操作名称String operationInput:
操作的参数,JSON格式String outputFormat:
客户端请求的输出格式,如JSON,AMFString[] responseProperties:
通过操作返回的一组键值对,逗号分开
5.2 必选接口IServerObjectExtension
这个接口实现的方法:Init(IServerObjectHelper pSOH)
和void Shutdown();
init()
方法:当Server启动的时候会调用该方法,并将IServerObjectHelper对象传入,该接口是对Server对象的弱引用,可以通过IServerObjectHelper.ServerObject得到服务器对象。Shutdown
方法:用在服务器关闭时调用,经常我们在该方法中释放SOE中使用的资源。
5.3 可选接口IObjectConstruct
该接口只有一个方法Construct
,该方法在Init
方法执行后,立即被执行,如果我们的SOE有配置属性,就可通过该方法的参数得到,该方法只调用1次,我们可以将SOE中用的的比较耗费资源的逻辑写在该方法中,比如:获取地图代码,或者你始终操作某一个图层,就可以把获取该图层的代码写在这里。
5.4 可选接口IObjectActivate
当init
和Construct
调用后,SOE的对象已经被创建,并且相应的配置信息也得到了,如果SOE的整个逻辑中需要不停的获取和释放服务器上下文,那么就必须实现改接口,改接口有两个方法: activate()
和deactivate()
,当客户端调用CreateServerContext()
的时候activate()
方法被调用,当客户端释放服务器上下文对象时deactivate()方法被调用。
5.5 SOE接口执行顺序
5.5 SOE接口说明
前面我们说过,SOE开发我们要面对三个问题:
- 如何在SOE中获取客户端传来的数据
- 如何在服务器端读取地图服务数据
- 如何将运行后的结果传回客户端
如果利用上面的接口,解决这三个问题可能大家还不是特别的清楚,接下来我们要通过一个简单的demo来介绍一下接口的具体使用。
6. SOE开发的步骤
介绍完了一些SOE的理论东西,我们就要开发我们的SOE程序,开发SOE程序的过程为(五步):
- 发布我们的地图服务(以MapServer为例)
- 根据我们的地图服务编写SOE代码
- 发布我们的SOE扩展
- 将SOE扩展和我们的地图服务绑定
- 客户端调用SOE扩展
7. SOE之Hello World程序
7.1 HelloWorld程序的解释
在本篇博客中SOE实例是最简单的,在这个代码中解决的问题主要有两个:
- 如何从客户端接受数据
- 如何将结果返回给客户端
7.2 发布我们的地图服务(MapServer)
- 因为我们在本次SOE实例中没有读取地图服务信息,所以使用ArcGIS Server自带的地图服务(当ArcGIS Server安装成功之后自带一个服务)
7.3 创建SOE程序(C#版本)
7.3.1 使用VS 创建SOE程序
7.3.2 默认的SOE模版有5个属性,有9个方法(代码截图,具体属性方法的作用等会解释)
7.3.3 填写SOE扩展的服务类型
- 因为我们的SOE是基于
MapServer
开发的(虽然我们的代码没有修改,假设我们的SOE就是基于MapServer
开发的,因为Hello World程序太简单,不需要读取MapServer
数据,所以也就不需要修改代码),所以修改我们SOE的类型。注意:新建的SOE程序,这个地方是空字符串,必须要修改为MapServer
或者ImageServer
,不然SOE发布不成功
7.3.4 生成soe文件
- 右击项目:重新生成。在debug文件夹下面会出现SOE文件(此文件是发布soe扩展使用的)
7.3.5 将SOE扩展发布到ArcGIS Server
7.3.6 将SOE和地图服务绑定
- 选择RestSOE重新启动地图服务
- 绑定成功
7.4 客户端调用SOE功能
- 访问生成的网址(绑定生成的
url
,7.3.6绑定成功之后生成了一个url):http://localhost:6080/arcgis/rest/services/SampleWorldCities/MapServer/exts/RestSOE
前面我们说了 SOE是包含一组方法的类(我们发布的Hello World程序只有一个方法)。
- 访问
sampleOperation
方法
我们调用了sampleOperation
方法,传过去两个参数
- 返回值
方法调用成功:并且取得了相应的结果
7.5 解释默认的SOE模版
在这里我们将详细解释一下SOE默认模版的一些属性和方法。在这里主要解决一下问题:
- SOE如何接受客户端传来的参数
- SOE如何将结果返回给客户端
- SOE的方法是如何添加的(SOE是一组方法的类)
- SOE如何读取地图服务的信息(这个问题,在这里不解决,只是稍微提一下,重点将在下一篇博客中说明)
7.5.1 SOE中的属性
在SOE中一个最重要的属性是:serverObjectHelper
,我们可以通过这个属性获得地图服务中的信息,具体这个属性怎么使用将在下一篇博客中使用
7.5.2 SOE中的方法
在SOE开发中,我们要着重的关注CreateRestSchema
方法,首先我们看一下这个方法中的代码
private RestResource CreateRestSchema()
{
RestResource rootRes = new RestResource(soe_name, false, RootResHandler);
RestOperation sampleOper = new RestOperation("sampleOperation",
new string[] { "parm1", "parm2" },
new string[] { "json" },
SampleOperHandler);
rootRes.operations.Add(sampleOper);
return rootRes;
}
- 在该函数中创建了Rest资源和Rest资源的操作,并且资源和Rest操作都对应一个处理函数。
- Rest资源对应的函数为
RootResHandler
- Rest操作对应的函数为
SampleOperHandler
RootResHandler
在什么时候被触发?是在访问这个url(http://localhost:6080/arcgis/rest/services/SampleWorldCities/MapServer/exts/RestSOE)的时候被触发- 我们可以看到Rest资源可以添加多个Rest操作,也就是说一个SOE可以有多个方法,每一个方法对应着一个Rest操作
- rest操作体现在什么地方?体现在这里:
- 我们看rest资源所对应的方法(注意:我方法中返回了
hello world
,然后在rest资源那里显示hello world)
private byte[] RootResHandler(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties)
{
responseProperties = null;
JsonObject result = new JsonObject();
result.AddString("hello", "world");
return Encoding.UTF8.GetBytes(result.ToJson());
}
- 我们在看rest操作的定义
对应着
- rest操作函数的实现
private byte[] SampleOperHandler(NameValueCollection boundVariables,
JsonObject operationInput,
string outputFormat,
string requestProperties,
out string responseProperties)
{
responseProperties = null;
string parm1Value;
//将客户端parm1传给parm1Value变量
bool found = operationInput.TryGetString("parm1", out parm1Value);
if (!found || string.IsNullOrEmpty(parm1Value))
throw new ArgumentNullException("parm1");
string parm2Value;
//将客户端parm2传给parm2Value变量
found = operationInput.TryGetString("parm2", out parm2Value);
if (!found || string.IsNullOrEmpty(parm2Value))
throw new ArgumentNullException("parm2");
//创建jsonObject对象,用于将结果传给客户端。在这里,我们将客户端传来的数据,原封不懂的返还给客户端
JsonObject result = new JsonObject();
result.AddString("parm1", parm1Value);
result.AddString("parm2", parm2Value);
return Encoding.UTF8.GetBytes(result.ToJson());
}
7.8 代码总结
- 一个SOE服务对应一个rest资源
- 一个rest资源可以拥有多个rest操作
- 一个rest资源对应一个方法(此方法作用不是很大)
- 一个rest操作对应一个方法(这个方法很重要,一般是soe的核心方法)
- 在rest操作对应的方法中可以获得客户端传来的参数,同时也可以给客户端发送信息