Создание нескольких (более 15) фильтров ответа HTTP, наследование и состав с / инъекция - PullRequest
2 голосов
/ 13 декабря 2010

Сначала небольшой рассказ о том, что я пытаюсь выполнить.

Я нахожусь в процессе создания пользовательского модуля HTTP, целью которого является перехват сообщений для нескольких (15+) различных веб-сервисов ArcGIS REST.,Перехваченные запросы и / или ответы будут удалены из любой ограниченной информации, основанной на текущем пользователе.

Например, при вызове, который возвращает несколько уровней, могут быть удалены определенные уровни.

Неизмененный ответ:

"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.

Пример:

// 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).Я хочу иметь возможность легко создавать новые фильтры без переопределения потока для каждого фильтра.

Потенциальное решение описано здесь: http://www.west -wind.com / weblog / posts / 72596.aspx

Однако я хочу упростить решение безпотеря гибкости при указании множества различных преобразований без переопределения потока.Я думаю, что я могу сделать это следующим образом:

  1. Унаследовать от MemoryStream, чтобы уменьшить повторную реализацию методов.
  2. Всегда работать с полным контентом, а не с фрагментарным контентом.
  3. Заменить события абстрактным методом (например, 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;

Преимущество этоговариант в том, что количество классов сведено к минимуму.Тем не менее, становится трудно повторно использовать любую логику фильтра где-либо еще в приложении, если это станет необходимым.Кроме того, модульное тестирование фильтров потребует проверки потока, еще один недостаток.

Решение 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.


У меня есть несколько целей, которые я хочу достичь с помощью любого решения.

  1. Решение должно быть легко проверяемым.Модульное тестирование будет использоваться для проверки правильности фильтров.Обязательно, чтобы тестирование было как можно более простым.
  2. Решение должно легко поддерживать создание нескольких фильтров (15+).
  3. Решение должно быть читабельным (т. Е. Простым в обслуживании).

Я думаю, что решение 2 - лучшее решение для данного сценария. Я могу проверить логику фильтрации полностью независимо от Stream с минимальной дополнительной сложностью. Любое решение будет поддерживать # 2 и # 3, поэтому тестирование дает преимущество.

Какие еще соображения могут быть? Есть ли лучшие альтернативы?

1 Ответ

2 голосов
/ 14 декабря 2010

Решение 2 очевидно предпочтительнее. Однако, похоже, что основная проблема заключается в создании самих фильтров. Надеемся, что в реализациях Filter есть много повторно используемых композиций. Можно ли «настроить» новый фильтр из составных деталей?

...