Creating multiple (15+) HTTP response filters, Inheritance vs. composition with injection

First, Iโ€™ll talk a little bit about what I'm trying to accomplish.

I am creating a custom HTTP module whose purpose is to intercept messages for several (15+) different ArcGIS REST web services. Intercepted requests and / or responses will be deprived of any limited information based on the current user.

For example, a call that returns multiple layers may contain specific layers.

Unmodified answer:

"layers" : [ { "id" : 0, "name" : "Facilities", "parentLayerId" : -1, "defaultVisibility" : true, "subLayerIds" : [1, 2, 3] }, { "id" : 1, "name" : "Hazardous Sites", "parentLayerId" : 0, "defaultVisibility" : true, "subLayerIds" : null }, ] 

Modified Answer:

 "layers" : [ { "id" : 0, "name" : "Facilities", "parentLayerId" : -1, "defaultVisibility" : true, "subLayerIds" : [1, 2, 3] } ] 

There are many services available, all uniquely identified by URL. Each service returns very different information and therefore it needs to be filtered differently. In addition, each service can return data in various formats (HTML, JSON, etc.).

Thus, I will need to create many different filters to apply to HttpRequest.Filters and / or HttpResponse.Filters filters.

Example:

 // Request for layers and the format is JSON IPolicy policy = GetPolicy(userContext); Filter filter = new LayerJsonResponseFilter(Response.Filter, policy); Response.Filter = filter; 

Request and response filters are implemented by inheriting from Stream (or another class that inherits from Stream, such as a MemoryStream). I want to be able to easily create new filters without overriding the stream for each filter.

A potential solution is described here: http://www.west-wind.com/weblog/posts/72596.aspx

However, I want to simplify the solution without losing the flexibility of defining many different transformations without overriding the flow. I think I can do this:

  • Inherit from MemoryStream to reduce re-implementation of methods.
  • Always use full content, not fragmented content.
  • Replace events with an abstract method (e.g. Filter ())

I examined two potential solutions.

Solution 1. Create some filters inheriting from ResponseFilter

In this scenario, each filter contains logic to perform filtering. There would be another 15 filters created by inheriting from the common ResponseFilter base class as follows:

 // 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 } } 

This will be used as follows.

 // Example var policy = GetPolicy(); var filter = new MapServiceJsonResponseFilter(response.Filter, policy); response.Filter = filter; 

The advantage of this option is that the number of classes is minimized. However, it becomes difficult to reuse any filter logic elsewhere in the application if it becomes necessary. In addition, unit filter testing will require flow mixing, another drawback.

Solution 2: creating multiple filters, embedding in a common response filter

In this case, one response filter is created. The actual logic or filter algorithm is entered into the filter. All filters inherit from the abstract base class 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); } 

This will be used as follows.

 // Example var policy = GetPolicy(); var filter = new MapServiceJsonResponseFilter(policy); ResponseFilter responseFilter = new ResponseFilter(response.Filter, filter); response.Filter = filter; 

The advantage of this solution is that the filtering logic is completely independent of any classes that implement the stream. If necessary, the logic can be more easily reused. Unit testing is a bit simpler since I donโ€™t need to mock the stream.

However, there are more classes (exactly 1), and the use is a little more complicated, although not so scary.

Note. I might want to rename FilterBase to avoid confusion with ResponseFilter. Possibly TransformBase.


I have several goals that I want to meet with any solution.

  • The solution must be highly verifiable. Unit testing will be used to validate filters. It is imperative that testing be as simple as possible.
  • The solution should easily support the creation of multiple filters (15+).
  • The solution should be readable (i.e. easy to maintain).

I think solution 2 is the best solution for this scenario. I can fully test the filtering logic regardless of Stream with minimal additional complexity. Any solution will support # 2 and # 3, so testing gets the edge.

What other considerations could there be? Are there any better alternatives?

+2
inheritance dependency-injection unit-testing architecture composition
source share
1 answer

Solution 2 is obviously preferable. However, it seems that the main problem is the construction of the filters themselves. Hopefully thereโ€™s a lot of reusable composition in filter implementations. Is it possible to โ€œcustomizeโ€ a new filter from components?

+2
source

Source: https://habr.com/ru/post/650221/


All Articles