rails omniauth and UTF-8 errors

2019-04-26 19:10发布

问题:

I had a recent error using omniauth trying to populate some fields from Google's login

Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and UTF-8

"omniauth"=>
  {"user_info"=>
    {"name"=>"Joe McÙisnean",
     "last_name"=>"McÙisnean",
     "first_name"=>"Joe",
     "email"=>"someemail@gmail.com"},
   "uid"=>
    "https://www.google.com/accounts/o8/id?id=AItOawnQmfdfsdfsdfdsfsdhGWmuLTiX2Id40k",
   "provider"=>"google_apps"}

In my user model

  def apply_omniauth(omniauth)
    #add some info about the user
    self.email = omniauth['user_info']['email'] if email.blank?
    self.name = omniauth['user_info']['name'] if name.blank?
    self.name = omniauth['user_info'][:name] if name.blank?
    self.nickname = omniauth['user_info']['nickname'] if nickname.blank?
    self.nickname = name.gsub(' ','').downcase if nickname.blank?

    unless omniauth['credentials'].blank?
      user_tokens.build(:provider => omniauth['provider'], 
                        :uid => omniauth['uid'],
                        :token => omniauth['credentials']['token'], 
                        :secret => omniauth['credentials']['secret'])
    else
      user_tokens.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
    end
  end

I'm not hugely knowledgeable about UTF encoding, so I'm not sure where I should be specifying the encoding? But I'm guessing it's here before it get's put into the user model and created, I'm unsure what to do about it?

UPDATE:

Rails 3.0.10 Omniauth 0.2.6 Ruby 1.9.2 PG 0.11.0

Default encoding is UTF-8

That didn't seem to be it, so I dug further and found this in the view:

Showing /Users/holden/Code/someapp/app/views/users/registrations/_signup.html.erb where line #5 raised:

incompatible character encodings: ASCII-8BIT and UTF-8
Extracted source (around line #5):

2:   <%= f.error_messages %>
3: 
4:   <%= f.input :name, :hint => 'your real name' %>
5:   <%= f.input :nickname, :hint => 'Username of your choosing' %>
6: 
7:   <% unless @user.errors[:email].present? or @user.email %>
8:     <%= f.input :email, :as => :hidden %>

UPDATE UPDATE:

It seems to be the omniauth gem which is returns the ASCII-8BIT chars, so my next question is how can I parse the hash and convert it back into UTF8 so my app doesn't explode?

session[:omniauth] = omniauth.to_utf8

Another part to this crazy ride is when I type this into the console

d={"user_info"=>{"email"=>"someemail@gmail.com", "first_name"=>"Joe", "last_name"=>"Mc\xC3\x99isnean", "name"=>"Joe Mc\xC3\x99isnean"}}

It automatically converts it to UTF-8, but it explodes when shoved into a session

 => {"user_info"=>{"email"=>"someemail@gmail.com", "first_name"=>"Joe", "last_name"=>"McÙisnean", "name"=>"Joe McÙisnean"}} 

This is a painful nightmare if there ever was one.

回答1:

Omniauth proved to be the problem producing the ASCII-8BIT

I ended up forcing the Omniauth hash into submission using:

omniauth_controller.rb

session[:omniauth] = omniauth.to_utf8

added recursive method to force convert the rogue ASCII-8BIT to UTF8

some_initializer.rb

class Hash
  def to_utf8
    Hash[
      self.collect do |k, v|
        if (v.respond_to?(:to_utf8))
          [ k, v.to_utf8 ]
        elsif (v.respond_to?(:encoding))
          [ k, v.dup.force_encode('UTF-8') ]
        else
          [ k, v ]
        end
      end
    ]
  end
end

Special thanks to tadman

recursively convert hash containing non-UTF chars to UTF