扩展一个表述生成器 |
由 1. REST 服务发布机制简述 可知,HTTP 请求在达到 REST 应用对象,交给资源实现类处理的时候,在资源实现类得到的响应结果(内容)还要经过 Encoder(表述生成器)的处理,才会形成表述,返回给客户端。
SuperMap iServer 中提供的 Encoder 有:模板表述生成器(主要用于生成 HTML 格式的表述)、 Json 表述生成器、RJson(格式化的 Json) 表述生成器、XML 表述生成器、图片表述生成器(支持 BMP、GIF、JPEG、JPG、PNG 等图片格式)等。资源根据客户端请求中指定的媒体类型(期望的表述格式),选择合适的 Encoder 将结果(内容)进行处理,返回给客户端。
SuperMap iServer 提供了 Encoder 的扩展机制。当你想将资源发布成一种新的表述格式,而这种表述格式,SuperMap iServer 中现有的 Encoder 并不能提供,这时你就可以对 Encoder 进行扩展,从而可以用新的表述格式将资源发布出来。
扩展一个表述生成器(Encoder)分如下两步:
在 SuperMap iServer 的 REST 实现框架中,提供了 com.supermap.services.rest.encoders.Encoder 抽象类用于表述生成器,所有的表述生成器都继承自该类。例如,在 SuperMap iServer 中已提供的 ImageEncoder, JsonEncoder, SceneEncoder, StreamEncoder, TemplateEncoder, XMLEncoder 等。
用户对表述生成器进行扩展,既可以继承 Encoder 抽象类,对其中的抽象方法进行实现,也可以继承现有的表述生成器类,改写其中的方法。其中,重要的方法如下表所示:
方法名称 | 含义 |
createSupportedMediaTypes() | 创建本表述生成器支持的表述格式的列表。即能将 Java 对象转换成什么媒体类型的表述。 |
toRepresentation(MediaType, Object) | 将 Java 对象转换成指定媒体类型的表述。 |
继承 Encoder 抽象类(或其子类),实现(或重写)上表中的两个方法,就可以实现一个自定义的表述生成器。
作为示例,仅为说明问题,这里实现了一个跟 JsonEncoder 基本一样的表述生成器 MyEncoder,只是支持的媒体类型改为“application/cjson”这种自定义的媒体类型,cjson 格式的表述内容跟 json 格式完全一致,只是 HTTP 响应中的媒体类型不同(由 application/json 变为 application/cjson)。具体应用时,你可以根据需求定义具体的实现。
MyEncoder 的层次结构如下:
MyEncoder 的示例代码如下:
package com.supermap.sample.extendREST;
import java.util.ArrayList;
import java.util.List;
import org.restlet.data.MediaType;
import org.restlet.representation.Representation;
import com.supermap.services.rest.encoders.JsonEncoder;
public class MyEncoder extends JsonEncoder{
public Representation toRepresentation(MediaType mediaType, Object resourceObj) {
//得到表述内容,内容跟 JSON 表述生成器转换得到的内容一致。
Representation rep = super.toRepresentation(mediaType, resourceObj);
if(rep != null){
//设置 HTTP 响应中的媒体类型为 application/cjson。
rep.setMediaType(new MediaType("application/cjson"));
}
return rep ;
}
protected List<MediaType> createSupportedMediaTypes() {
List<MediaType> supportedMediaTypes = new ArrayList<MediaType>(); // NOPMD
//MyEncoder 表述生成器支持的媒体类型为 application/cjson
supportedMediaTypes.add(new MediaType("application/cjson"));
return supportedMediaTypes;
}
}
至此,一个简单的表述生成器类就完成了。你可以在%SuperMap iServer_HOME%/samples/code/ExtendREST 下找到该实例程序的源代码。
MyEncoder 类,编译后,需要打成 Jar 包放到%SuperMap iServer_HOME%/webapps/iserver/WEB-INF/lib 目录下,这里将它打进 extendREST.jar 中。
自定义表述生成器配置的配置有两种方式,任意一种都可以:
2.1,添加到外部配置文件
需要说明的是,在 SuperMap iServer REST 服务中,服务器根据请求 URI 的后缀来选择表述生成器,从而返回不同类型的表述。默认地,服务器会根据值为“application/后缀名”的媒体类型去选择表述生成器,例如当 URI 后缀为 cjson 时,服务器就会去选择支持“application/cjson”这种媒体类型的表述生成器,在这里,就找到了 MyEncoder,由 MyEncoder 对结果进行处理,并返回客户端,响应的媒体类型即为“application/cjson”。
当然,如果你不想使用这种 URI 后缀跟媒体类型的对应关系,可以在 iServer 中显示的配置,参见:URI 后缀与媒体类型的对应关系。
配置自定义的表述生成器到 REST 服务,首先要将实现类注册成一个组件,然后将组件配置到资源中。既可以配置到单个资源,为单个资源所有,也可以配置到所有资源,成为所有资源都支持的表述生成器。流程如下所示:
注册 MyEncoder 类为 Bean 组件,需要在 REST 应用配置文件中添加一个<bean/>节点,REST 应用配置文件 iserver-rest-appContext.xml 位于%SuperMap iServer_HOME%/webapps/iserver/WEB-INF 目录下。如下所示,将 MyEncoder 类注册成了 MyEncoder Bean 组件(class 的路径为 MyEncoder 类在 Jar 包中的路径):
<bean id="MyEncoder" class="com.supermap.sample.extendREST.MyEncoder"></bean>
配置到单个资源需要在资源配置文件 iserver-rest-resources.xml 中进行。上一节(扩展一个简单算法资源)的步骤2. 添加资源信息到资源配置文件 中,曾讲过资源配置信息的结构,里面提到 <resource/> 节点下的 <extensionEncoderBeanNames/> 子节点。每一个资源配置项(<resource/>)都可以有一个 <extensionEncoderBeanNames/> 子节点,表示扩展的资源表述生成器有哪些。它的取值为已注册过的 Bean 组件的 ID,如果有多个 Bean 组件,则 ID 之间用“,”隔开。这样,该资源配置项对应的资源就拥有了额外的表述生成器。如将 MyEncoder 组件配置到 rectangleArea 资源如下:
<resource>
<configID>rectangleArea</configID>
<urlTemplate>/maps/{mapName}/rectangleArea</urlTemplate>
<resourceType>ArithmeticResource</resourceType>
<implementClass>com.supermap.sample.extendREST.rectangleAreaResource</implementClass>
<extensionEncoderBeanNames>MyEncoder</extensionEncoderBeanNames>
<extensionDecoderBeanNames></extensionDecoderBeanNames>
<extensionHttpActionHandlerBeanName></extensionHttpActionHandlerBeanName>
</resource>
这样,rectangleArea 资源就多了一种可用的表述生成器——MyEncoder,从而可以支持 application/cjson 这种表述格式。
重启服务后,在 IE 中输入以下 URI,模拟 GET 请求,获取资源 cjson 的表述格式:
http://localhost:8090/iserver/services/components-rest/rest/maps/世界地图/rectangleArea.cjson?rect2D={leftBottom:{x:23,y:34},rightTop:{x:40,y:50}}&unit=METER
由于 application/cjson 这种媒体类型是我们自定义的,IE 不能解析 ,所以提示保存文件,如下所示:
用文本编辑器打开保存之后的文件,你会发现得到了量算的结果。(正如前面示例说明中所讲,仅做示例,内容跟 json 格式的表述一样)
配置到所有资源需要在 REST 应用配置文件 iserver-rest-appContext.xml 中进行。即增加一个默认表述生成器。默认表述生成器在 iserver-rest-appContext.xml 中由 key="systemEncoders"的<entry/>节点表示。将 MyEncoder 组件在 iserver-rest-appContext.xml 中注册到所有资源,如下所示:
<beans>
......
<util:map id="restConfig">
<entry key="systemEncoders" value="xmlEncoder,jsonEncoder,pjsonEncoder,templateEncoder,MyEncoder" />
</util:map>
......
</beans>
这样,所有的资源都可以使用 MyEncoder 表述生成器,效果跟 rectangleArea 资源的 cjson 格式表述类似。
注:iserver-rest-resources.xml、iserver-rest-appContext.xml 位于%SuperMap iServer_HOME%/webapps/iserver/WEB-INF 目录下。
此种方式,可以将配置信息一起打在 Jar 包里,实现扩展模块的即插即用,方便系统管理员配置。
本示例扩展的是地图模块的资源,模块配置文件为:Jar:///META-INF/extensions/services/rest/mapRest(参见:配置文件说明)。
本示例中,mapRest 配置如下:
encoders=com.supermap.sample.extendREST.MyEncoder
decoders=
verifiers=
resourceFiles=config/resource/rest/mappingResources.xml
resourceFiles 与资源配置文件对应,
Jar:///config/resource/rest/mappingResources.xml 配置如下:
<resources>
<resource>
<configID>rectangleArea</configID>
<urlTemplate>/maps/{mapName}/rectangleArea</urlTemplate>
<resourceType>ArithmeticResource</resourceType>
<implementClass>com.supermap.sample.extendREST.RectangleAreaResource</implementClass>
<extensionEncoderBeanNames>MyEncoder</extensionEncoderBeanNames>
<extensionDecoderBeanNames></extensionDecoderBeanNames>
</resource>
<resources>
此步骤可以在实现资源类的 Java 工程中一起进行,打在同一个 Jar 包中,如本示例中的 extendREST.jar。
至此,一个表述生成器的扩展就完成了。后面讲到的扩展参数解析器、扩展 HTTP 请求处理器,流程跟扩展表述生成器类似,所以在讲配置方式的时候,后面会稍微简单一些。
需要说明的是,MyEncoder 这个表述生成器比较简单,没有实际意义,仅仅是作为例子说明扩展表述生成器的过程。