Repository: interagent/committee (Ruby)

Issue:
Possibility of skipping validation of request ContentType
Currently, Committee in line: https://github.com/interagent/committee/blob/2b427717cb4c6b9cd77220654c247d80b752dc48/lib/committee/request_validator.rb#L8 is verifying is request ContentType is same as the one declared in Scema `encType` field. By default JSONSchema uses `application/json` and this validator prevents POST/PUT  requests with ContentType `application/x-www-form-urlencoded`. But Rack is constructed the way that it is handling both the same way and parsing input params regardless of type of content. 

App I'm working on requires to allow for both content types, so this behavior of Rack/Sinatra is good for my case. But when I added RequestValidation from Committee it refuses requests with different ContentType than `application/json`. 

So I believe it would be good to allow skipping this `check_content_type!` method execution by setting some option when configuring this middleware (`Committee::Middleware:: RequestValidation`). 

Do you see it possible?



Discussion:

Patch:
diff --git a/README.md b/README.md
index 92198b5f..75e05f51 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,7 @@ Options:
 
 * `allow_form_params`: Specifies that input can alternatively be specified as `application/x-www-form-urlencoded` parameters when possible. This won't work for more complex schema validations.
 * `allow_query_params`: Specifies that query string parameters will be taken into consideration when doing validation (defaults to `true`).
+* `check_content_type`: Specifies that content_type should be verified according jsonschema definition. (defaults to `true`).
 * `optimistic_json`: Will attempt to parse JSON in the request body even without a `Content-Type: application/json` before falling back to other options (defaults to `false`).
 * `prefix`: Mounts the middleware to respond at a configured prefix.
 * `raise`: Raise an exception on error instead of responding with a generic error body (defaults to `false`).
diff --git a/lib/committee/middleware/request_validation.rb b/lib/committee/middleware/request_validation.rb
index ebbacd5f..64680b87 100644
--- a/lib/committee/middleware/request_validation.rb
+++ b/lib/committee/middleware/request_validation.rb
@@ -4,6 +4,7 @@ def initialize(app, options={})
       super
       @allow_form_params  = options.fetch(:allow_form_params, true)
       @allow_query_params = options.fetch(:allow_query_params, true)
+      @check_content_type = options.fetch(:check_content_type, true)
       @optimistic_json    = options.fetch(:optimistic_json, false)
       @raise              = options[:raise]
       @strict             = options[:strict]
@@ -21,7 +22,7 @@ def handle(request)
       ).call
 
       if link = @router.find_request_link(request)
-        validator = Committee::RequestValidator.new(link)
+        validator = Committee::RequestValidator.new(link, check_content_type: @check_content_type)
         validator.call(request, request.env[@params_key])
         @app.call(request.env)
       elsif @strict
diff --git a/lib/committee/request_validator.rb b/lib/committee/request_validator.rb
index 6ecdc83e..286b450b 100644
--- a/lib/committee/request_validator.rb
+++ b/lib/committee/request_validator.rb
@@ -2,10 +2,11 @@ module Committee
   class RequestValidator
     def initialize(link, options = {})
       @link = link
+      @check_content_type = options.fetch(:check_content_type, true)
     end
 
     def call(request, data)
-      check_content_type!(request, data)
+      check_content_type!(request, data) if @check_content_type
       if @link.schema
         valid, errors = @link.schema.validate(data)
         if !valid


Test patch:
diff --git a/test/middleware/request_validation_test.rb b/test/middleware/request_validation_test.rb
index 46a1d4b8..d7ea01da 100644
--- a/test/middleware/request_validation_test.rb
+++ b/test/middleware/request_validation_test.rb
@@ -84,6 +84,16 @@ def app
     end
   end
 
+  it "optionally skip content_type check" do
+    @app = new_rack_app(check_content_type: false)
+    params = {
+      "name" => "cloudnasium"
+    }
+    header "Content-Type", "text/html"
+    post "/apps", MultiJson.encode(params)
+    assert_equal 200, last_response.status
+  end
+
   private
 
   def new_rack_app(options = {})
diff --git a/test/request_validator_test.rb b/test/request_validator_test.rb
index 419060fe..634d2389 100644
--- a/test/request_validator_test.rb
+++ b/test/request_validator_test.rb
@@ -54,6 +54,15 @@
     assert_equal message, e.message
   end
 
+  it "allows skipping content_type check" do
+    @request =
+      Rack::Request.new({
+        "CONTENT_TYPE" => "application/x-www-form-urlencoded",
+        "rack.input"   => StringIO.new("{}"),
+      })
+    call({}, check_content_type: false)
+  end
+
   it "detects an missing parameter in GET requests" do
     # GET /apps/search?query=...
     @link = @link = @schema.properties["app"].links[5]
@@ -88,7 +97,7 @@
 
   private
 
-  def call(data)
-    Committee::RequestValidator.new(@link).call(@request, data)
+  def call(data, options={})
+    Committee::RequestValidator.new(@link, options).call(@request, data)
   end
 end