首先是关于我想要实现的目标的一些背景故事。
我正在创建一个自定义 HTTP 模块,其目的是拦截发送到多个 (15+) 不同 ArcGIS REST Web 服务的消息。拦截的请求和/或响应将被剥离基于当前用户的任何受限信息。
例如,返回多个层的调用可能会删除某些层。
未修改的响应:
"layers" : [
{
"id" : 0,
"name" : "Facilities",
"parentLayerId" : -1,
"defaultVisibility" : true,
"subLayerIds" : [1, 2, 3]
},
{
"id" : 1,
"name" : "Hazardous Sites",
"parentLayerId" : 0,
"defaultVisibility" : true,
"subLayerIds" : null
},
]
修改后的响应:
"layers" : [
{
"id" : 0,
"name" : "Facilities",
"parentLayerId" : -1,
"defaultVisibility" : true,
"subLayerIds" : [1, 2, 3]
}
]
有许多可用的服务,所有服务都通过 URL 进行唯一标识。每个服务返回的信息非常不同,因此需要进行不同的过滤。此外,每个服务可能会以各种格式(HTML、JSON 等)返回数据。
因此,我需要创建大量不同的过滤器来应用于 HttpRequest.Filters 和/或 HttpResponse.Filters。
Example:
// Request for layers and the format is JSON
IPolicy policy = GetPolicy(userContext);
Filter filter = new LayerJsonResponseFilter(Response.Filter, policy);
Response.Filter = filter;
请求和响应过滤器是通过继承 Stream(或继承自 Stream 的其他类,例如 MemoryStream)来实现的。我希望能够轻松创建新的过滤器,而无需为每个过滤器重新实现 Stream。
这里描述了一个潜在的解决方案:http://www.west-wind.com/weblog/posts/72596.aspx
但是,我想简化解决方案,同时又不失去指定许多不同转换的灵活性,而无需重新实现流。我认为我可以通过以下方式实现这一目标:
- 继承自MemoryStream,以减少方法的重新实现。
- 始终对完整内容进行操作,而不是分块内容。
- 用抽象方法替换事件(例如 Filter())
我考虑了两种可能的解决方案。
解决方案1:创建多个继承自ResponseFilter的过滤器
在这种情况下,每个过滤器都包含执行过滤的逻辑。将创建超过 15 个过滤器,所有过滤器都继承自公共 ResponseFilter 抽象基类,如下所示:
// All filters will inherit from ResponseFilter
public abstract class ResponseFilter : MemoryStream
{
public ResponseFilter(Stream stream, Policy policy) { }
// Must be overridden in a derived class with specific Filter logic.
public abstract string Filter(string content);
// Overridden to cache content.
public override void Write(byte[] buffer, int offset, int count) { }
// Overridden to perform the filter/transformation before the content is written.
public override void Flush()
{
// Get stream content as a string
string content = Filter(content);
// Write new content to stream
}
}
这将按以下方式使用。
// Example
var policy = GetPolicy();
var filter = new MapServiceJsonResponseFilter(response.Filter, policy);
response.Filter = filter;
此选项的优点是可以将类的数量保持在最低限度。然而,如果有必要的话,在应用程序中的其他任何地方重用任何过滤器逻辑就会变得困难。此外,对过滤器进行单元测试需要模拟 Stream,这是另一个缺点。
解决方案2:创建多个过滤器,注入到通用的ResponseFilter中
在这种情况下,将创建单个响应过滤器。实际的过滤器逻辑或算法被注入到过滤器中。所有过滤器都继承自抽象基类FilterBase。
// Represents an HttpResponse Filter. Renamed to avoid confusion with
// the filter algorithm.
public class ResponseFilterStream : MemoryStream
{
public ResponseFilterStream(Stream stream, FilterBase filter) { }
// Overridden to cache content.
public override void Write(byte[] buffer, int offset, int count) { }
// Overridden to perform the filter/transformation before the content is written.
public override void Flush()
{
// Get stream content as a string
string content = _filter.Filter(content);
// Write new content to stream
}
}
// All filter algorithms inherit from FilterBase and must implement
// the filter method.
public abstract class FilterBase
{
protected TransformBase(Policy policy) { }
// Overridden to perform the filter/transformation.
public abstract string Filter(string content);
}
这将按以下方式使用。
// Example
var policy = GetPolicy();
var filter = new MapServiceJsonResponseFilter(policy);
ResponseFilter responseFilter = new ResponseFilter(response.Filter, filter);
response.Filter = filter;
该解决方案的优点是过滤逻辑完全独立于任何实现流的类。如有必要,可以更轻松地重用逻辑。单元测试稍微简单一些,而且我不需要模拟流。
然而,有更多的类(恰好是 1 个),并且用法也稍微复杂一些,但也不是很复杂。
注意:我可能想重命名 FilterBase 以避免与 ResponseFilter 混淆。也许是 TransformBase。
我有几个目标希望通过任一解决方案来实现。
- 该解决方案必须具有高度可测试性。单元测试将用于检查过滤器的正确性。测试必须尽可能简单。
- 该解决方案必须轻松支持创建多个过滤器(15+)。
- 该解决方案应该是可读的(即易于维护)。
我认为解决方案 2 是针对该给定场景的最佳解决方案。我可以完全独立于 Stream 来测试过滤逻辑,并以最小的额外复杂性进行测试。任一解决方案都支持 #2 和 #3,因此测试具有优势。
可能还有哪些其他考虑因素?有更好的选择吗?