Pergunta

I'm developing a web service (in Ruby) which needs to do a number of different things for each message it receives.

Before my web service can process a message it must do different things:

  • sanitizing (e.g. remove HTML/JS)
  • check format (e.g. valid email provided?)
  • check IP in blacklist
  • invoke 3rd party web service
  • plus 10-30 other things

I'm thinking about implementing a filter/composite filter architecture where each step/phase is a filter. For instance, I could have these filters

  • Sanitize input filter
  • Email filter
  • Country code filter
  • Blacklist filter

Each filter should be possible to reject a message, so I'm considering that a filter should raise/throw exceptions.

This will give a lot of flexibility and hopefully a codebase that are easy to understand.

How would you did this? And what are pros and cons of above design?

Foi útil?

Solução

I would leave Exceptions for the cases when the filter itself actually broke down (e.g blacklist not available etc) and indicate the valid/invalid state either by true/false return values or, as you also suggested, throwing a tag.

If you don't want to stop at first failure, but execute all filters anyway, you should choose the boolean return type and conjunct them together (success &= next_filter(msg))

If I understood your situation correctly, the filter can both modify the message or check some other source for validity (e.g blacklist).

So I would do it like this:

module MessageFilters

  EmailValidator = ->(msg) do
    throw :failure unless msg.txt =~ /@/
  end

  HTMLSanitizer = ->(msg) do
    # this filter only modifies message, doesn't throw anything
    # msg.text.remove_all_html!
  end
end

class Message

  attr_accessor :filters
  def initialize
    @filters = []
  end

  def execute_filters!
    begin
      catch(:failure) do
        filters.each{|f| f.call self}
        true # if all filters pass, this is returned, else nil
      end
    rescue => e
      # Handle filter errors
    end
  end
end

message = Message.new

message.filters << MessageFilters::EmailValidator
message.filters << MessageFilters::HTMLSanitizer

success = message.execute_filters!  # returns either true or nil
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top