Slow Dance RSSフィード

2007-10-23

携帯サイトの厄介な文字化け with JpMobile

あるサイトで、リンクをクリックすると、そのアンカーテキストで検索をかけ、その結果を表示

するプログラムを作成した。そしたら、結果の部分が文字化けする。しかも、getで投げた、

アンカーのテキストが。

それは、なぜか2回UTF8変換が起こっている。


対処として、文字コード変換を行っているmobile_filter(JpMilbleの機能)を外すとドコモでは

問題なくなる。

だが、Ezでは、文字コード全体をSJISと認識してしまうため、他の部分含めすべてバグル。

この点、ドコモとSBはUTF8と認識してくれるので、上手く行ってしまう。


■再現

mobile_filterをexceptして以下を実行すると同じになる。やはりutf8がutf8で変換されている

@school = NKF::nkf("-S -w",params[:school])。-Sとなっているが、実は入ってくる文字はUTF8。


他との違いはなんだろうか。何故かこのメソッドの場合のみ上手くいかない。

そういえば、ここの文字は直にDBから引いたものをパラメタとして渡してる。

(後に、これは勘違いだと知る)

<%= link_to #{h @user.address}, :action => "search_by_addr", :address => @user.address %>

ここは他と違うところだ。

ユーザーからの入力でも、コントローラやモデルで引いたものでもなく、DBから直に引いたものを使っている。

確かにDBはUTF8でそれを直に使った場合に何かあるかも。

そこをtosjisしたらできた。

<%= link_to #{h @user.address}, :action => "search_by_addr", :address => @user.address.tosjiss %>

あ、できた。これならmobile_filterを有効にしても大丈夫。

おそらく、ビューでクエリを発行したからDB呼んで直に飛ばしたので、utf8でとんだ。それが、

utf8変換されたっぽい。


・・と思ったら、今度はDocomo、EzはOKだが、バンクがだめ。

げげ。モバイルフィルタがVodaにはUtf8で渡すからだめだ。


根本的に、getクエリに、DBから直に持ってきた日本語を入れるのが問題。

よって、@user.address.idのようにして、日本語を渡さないようにする。

そして、それを受けたコントローラでfindかければOK。


原則

★日本語でDBからひいたものをパラメータに渡さなない使わない!☆

これが今回の教訓になった。


しかし、これでも駄目なことに気がついた。先ほどは、直接DBから引いているから駄目と

書いたが、元々はmobile_filterを使って、Controllerで作られたインスタンス変数(中身は日本語)

をgetでのパラメータを送ると、見事に2回変換がかかり、失敗してしまう。

例えば次のコード。これは、最初の検索はいいのだが、ページングで失敗する。最初のget時に、

params[:keyword]はきちんと文字コード変換が行なわれる。

しかし、その後、この、既にエンコードされた値をそのままビューのgetパラメータとして使うので、

エンコードする必要のないものをエンコードしてしまう。

■Userを名前で検索する

Controller

   def search
    if params[:keyword]
      @keyword = params[:keyword]
      
      @pages, @users = paginate(
                                 :users, 
                                 :conditions => ["name is not NULL and status = 1 and name like ?","%#{@keyword}%"],
                                 :order => "id DESC", 
                                 :per_page => LIMIT_PER_PAGE
                               )
      flash.now[:notice] = "#{@pages.item_count}件ヒットしました。"
      render :action => "search_result"
    else
      render :action => "search_boxes"
    end
  end

View

<% @title = "名前検索" -%>

  <%= @pages.item_count %>件中 <%= (@pages.item_count != 0 ? @pages.current.first_item : 0)%> - <%= @pages.current.last_item %>件目


<% unless @pages.item_count == 0 -%>
  <%= separator %>
 <p>
  <%= separator %>
  <% for user in @users do -%>
    <%= name_address(user) %><br />
    <%= separator %>
  <% end -%>
  </p>
<div align="center" style="text-align:center;">
  <% if (@pages.current.previous || @pages.current.next) -%>
  <%= separator %>
    <br />
    <%=link_to "前へ",:keyword => @keyword, :page => @pages.current.previous if @pages.current.previous %><%=" | " if (@pages.current.previous && @pages.current.next)%>
    <%=link_to "次へ",:keyword  => @keyword, :page => @pages.current.next if @pages.current.next %>

  <% end -%>
</div>
<% end -%>
<%=separator%>

<div style="text-align:left; font-size:small; background-color:<%= mypage_bg_color(get_user_from_session) %>;color:<%= @color %>; text-align:left;"><%=link_to "検索画面へ", :action => "search_boxes" %><br />
<%=mypage_separator(get_user_from_session)%>
</div>

この問題を防ぐ方法は次のようになる。

「:keyword => @keywordの値を変換前、つまり携帯側の文字コードに戻す」。ただ、単純に戻すと、

Softbant/Vodafoneの場合も変換されてしまうので注意。このキャリアはmobile_filterの変換を

受けないので、今回の問題には関係ない。しかし、解決策の点では関係ある。

解決策は次の通り。

View

 :keyword => reencode_to_mobile_code(@keyword)

Helper

  def reencode_to_mobile_code(str)
    filter = Jpmobile::Filter::Sjis.new
    
    if str && filter.apply_incoming?(controller)
      filter.to_external(str) 
    else
      str
    end
  end

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/LukeSilvia/20071023/1193327000