DEV Community

K Putra
K Putra

Posted on • Edited on

Rails: Fixing ‘Invalid query parameters: invalid %-encoding’

Table of Contents:
Problem
A. Drop the query string
B. Fix the query string
Final word

Problem

ActionController::BadRequest
Invalid query parameters: invalid %-encoding (miconazole%202%%20cr)

Rack::QueryParser::InvalidParameterError
invalid %-encoding (miconazole%202%%20cr)

ActionController::BadRequest
ActionView::Template::Error: Invalid query parameters: invalid %-encoding (%2Fsearch%2Fall%Forder%3Ddescending%26page%3D5%26sort%3Dcreated_at)
Enter fullscreen mode Exit fullscreen mode

In a full stack apps, sometimes users manually edit query strings in the address bar, and make requests that have invalid encodings.

In a api apps, sometimes front end or mobile engineer forget to encoding the query strings, so they just give raw query strings to your apps, then they left it for years until it became very hard to fixed on the client side, and the management decide the server side should fix it.

These corrupted query strings are not that important, but users are receiving 500 server error pages.

What can we do?

I can offer two choice: Drop the query string OR Fix the query string.

A. Drop the query string

I got this from this post right here.

First, make a middleware:

# You can put this everywhere
# In my case, I put this in app/middlewares/hande_bad_encoding_middleware.rb

class HandleBadEncodingMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    begin
      Rack::Utils.parse_nested_query(env['QUERY_STRING'].to_s)
    rescue Rack::Utils::InvalidParameterError
      env['QUERY_STRING'] = ''
    end

    @app.call(env)
  end
end
Enter fullscreen mode Exit fullscreen mode

Second, use this middleware before Rack::Runtime:

# config/application.rb

require_relative 'boot'

require 'rails/all'

# If you get NameError: uninitialized constant,
# you have to use require like this one:
require_relative '../app/middlewares/handle_bad_encoding_middleware.rb'

Bundler.require(*Rails.groups)

module YourAppsName
  class Application < Rails::Application

    ...
    # add this:
    config.middleware.insert_before Rack::Runtime, HandleBadEncodingMiddleware
    ...

  end
end
Enter fullscreen mode Exit fullscreen mode

The result is, you drop the query string:

# from this
"https://your-url.com?query=alcohol%2070%"

# to this
"https://your-url.com"
Enter fullscreen mode Exit fullscreen mode

B. Fix the query string

I did some research and modify the middleware. This is the step:

First (optional), I use gem rack-utf8_sanitizer to remove invalid UTF-8 characters from the query strings. This prevents errors like "invalid byte sequence in UTF-8".

I personally prefer rack-utf8_sanitizer than utf8-cleaner because it is more robust, more option, and less bugs.

Second, make a middleware:

# You can put this everywhere
# In my case, I put this in app/middlewares/hande_bad_encoding_middleware.rb

class HandleBadEncodingMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    begin
      Rack::Utils.parse_nested_query(env['QUERY_STRING'].to_s)
    rescue Rack::Utils::InvalidParameterError
      params = URI.decode_www_form(env['QUERY_STRING'])
      query_string = URI.encode_www_form(params)
      env['QUERY_STRING'] = query_string
    end

    @app.call(env)
  end
end
Enter fullscreen mode Exit fullscreen mode

There are many ways to encode and decode url:

URI.escape
URI.unescape
URI.encode_www_form
URI.decode_www_form
URI.encode_www_form_component
URI.decode_www_form_component
CGI.escape
CGI.unescape
Enter fullscreen mode Exit fullscreen mode

I've tried them all, and I picked the best for me, which are URI.encode_www_form and URI.decode_www_form. Probably you have to pick which is best for you.

Third, use this middleware before Rack::Runtime:

# config/application.rb


require_relative 'boot'

require 'rails/all'

# If you get NameError: uninitialized constant,
# you have to use require like this one:
require_relative '../app/middlewares/handle_bad_encoding_middleware.rb'

Bundler.require(*Rails.groups)

module YourAppsName
  class Application < Rails::Application

    ...
    # you add this for the rack-utf8_sanitizer,
    # as described in the gem's readme:
    config.middleware.insert 0, Rack::UTF8Sanitizer
    # add this:
    config.middleware.insert_before Rack::Runtime, HandleBadEncodingMiddleware
    ...

  end
end
Enter fullscreen mode Exit fullscreen mode

The result is, you fix the query string:

# from this
"https://your-url.com?query=alcohol%2070%"

# to this
"https://your-url.com?query=alcohol%2070%25"
Enter fullscreen mode Exit fullscreen mode

Final Word

If you have any problems or questions, let me know in the comment.

If you are helped, I am glad I can help you.

Top comments (2)

Collapse
 
joeradtke profile image
Joseph Radtke

Thank you for your article. It didn't quite help me, but that's probably my fault.

I'm trying to learn to use Hanami which is quite a project and have got to the point where I am trying to upload images. I'm using the shrine gem and have a very basic problem. When I try to upload a file I get the error which is the subject of your article. Since your article applies to rails I don't quite see how to apply it to a Hanami app. The problem seems to be at the request level before any logic is performed.

If you have any insight into this I would appreciate it. If not, thanks for the article which may eventually help me get past this hurdle.

Joe Radkte

Collapse
 
rahulpuroht profile image
Rahul Purohit

Helpful thanks